Composite data types are user defined data types that consist of several elements grouped together. Usually data elements are stored close together in consecutive memory addresses.
There are several composite data types in C.
This composite data type is a group of multiple elements. All elements have same data type and are ordered by a numeric index (i ≥ 0). The elements in the array can be identified using array name and index enclosed in square brackets: array[i]. First element is array[0].
Array Elements
element_type identifier[size];
#include <stdio.h>
int main () {
/* define array with 10 integer elements */
int a[10] = {0};
/* array initialization */
for (int i = 0; i < 10; i++ ) {
a[i] = i;
}
/* array traversal and print */
for (int i = 0; i < 10; i++ ) {
printf("a[%d] = %d\n", i, a[i] );
}
return 0;
}
We can define string literals in C using double quotes: "…". However a string variable must be defined as an array of characters. Each element of a string can be identified by variable name and its index. Last element in a string is NUL character: "\0".
char string_name[capacity+1] = "content";
#include <stdio.h>
#include <string.h>
int main () {
char a[12] = "foo";
char b[12] = "bar";
char c[12];
int l; //length for string a
printf("a = %s\n", a );
printf("b = %s\n", b );
strcpy(c, a); //copy strings
printf("c = %s\n", c );
l = strlen(a); //string length
printf("before length = %d\n", l);
strcat( a, b); //concatenate strings
printf("strcat( a, b): %s\n", a );
l = strlen(a); //string length
printf("after length = %d\n", l);
return 0;
}
A structure is known as record in other computer languages. It is a group of elements. Each element has a name and type. The elements can have different types. The type of element can be also a composite type. In this case the structure is sometimes called aggregate.
struct name {
type item_name;
type item_name;
...
type item_name;
} variable,...;
Notes:
#include <stdio.h>
#include <string.h>
/* define structure of Book */
struct Project {
char name[50];
char author[50];
} foo, bar;
/* define a method to print Project info */
void PrintInfo (struct Project this){
/* print project info */
printf( "\n");
printf( "project.name: %s\n", this.name);
printf( "project.author: %s\n", this.author);
}
int main( ) {
/* first project */
strcpy( foo.name, "Bee Language");
strcpy( foo.author, "Elucian Moise");
/* second project */
strcpy( bar.name, "C Language");
strcpy( bar.author, "Dennis Ricthie");
/* print project info */
printf( "project: %s\n", foo.name);
printf( "project: %s\n", bar.name);
/* call function with structure argument */
PrintInfo(foo);
PrintInfo(bar);
return 0;
}
Union is similar to structure. In union the elements are exclusive. That is union type has a single value at a time. The memory reserved for union value is large enough to enable any value to be stored, but only last value assigned is stored.
#include <stdio.h>
#include <string.h>
union Test {
int i;
float f;
char s[20];
};
int main( ) {
union Test a;
union Test b;
union Test c;
a.i = 2;
printf( "a.i = %d\n", a.i);
b.f = 2.5;
printf( "b.f = %f\n", b.f);
strcpy( c.s, "test");
printf( "c.s = %s\n", c.s);
return 0;
}
Alert: C will not initialize the elements of unions to correct values. You can get random values that may surprise you. Many times you’r program will compile but will have logical errors. You must test everything 10 times before you can put a C program in production.
#include <stdio.h>
#include <string.h>
union Test {
int i;
float f;
char s[20];
};
int main( ) {
union Test a;
a.i = 2;
printf( "a.i = %d\n", a.i);
a.f = 2.5;
printf( "a.f = %f\n", a.f);
printf( "a.i = %d\n", a.i);
strcpy( a.s, "test");
printf( "a.s = %s\n", a.s);
printf( "a.i = %d\n", a.i);
printf( "a.f = %f\n", a.f);
return 0;
}
Unexpected Output:
a.i = 2
a.f = 2.500000
a.i = 1075838976
a.s = test
a.i = 1953719668
a.f = 77135366849413060736626594938880.000000
Normally, Boolean values are stored in computer memory as unsigned integers. One integer has 4 bytes. So one Boolean value will occupy 4 bytes even if only 1 byte is necessary to store one Boolean value.
To save space you can group multiple Boolean values (logic flags) into one single variable of type struct with bit fields. To define a bit field you specify the length of the field after ":". Obvious the length must be smaller than the data type of the element.
#include <stdio.h>
#include <string.h>
/* define b as mask of 3 bites */
struct {
unsigned int one :1;
unsigned int two :1;
unsigned int three:1;
unsigned int other:5;
} b;
int main( ) {
b.one = 1; //true
b.two = 0; //false
b.three = 3; //not Boolean
b.other = 12; //not Boolean
printf( "b-size %d\n", sizeof(b)); //expected: 4 (bytes)
printf( "b.one = %i\n", b.one); //expected: 1
printf( "b.two = %i\n", b.two); //expected: 0
printf( "b.three = %i\n", b.three); //unexpected result: 1
printf( "b.other = %i\n", b.other); //expected 12: can fit in 5 bites
return 0;
}
Caution: If you assign a value that will not fit in the number of bits allocated the result can be incorrect but you get no error. In the example above the value of item three is corrupted. Does not fit in 1 byte correctly while other field is also not Boolean but is correct. It fits in 5 bits reserved by structure.
A type alias is a name given to user defined type. You can use keyword typedef to define a new type. The type you define can be used to declare global or local variables, parameters or function result.
Syntax:
typedef composite_type type_name;
#include <stdio.h>
#include <string.h>
/* create a type with alias */
typedef struct {
unsigned int one :1;
unsigned int two :1;
unsigned int three:1;
} Mask;
int main( ) {
/* local variable based on user defined type */
Mask b;
/* assign values for each element */
b.one = 1;
b.two = 2;
b.three = 3;
printf( "b.one = %i\n", b.one); // 1
printf( "b.two = %i\n", b.two); // 0
printf( "b.three = %i\n", b.three);// 1
return 0;
}
Observe: b.two and b.three are corrupted values. This is on purpose for giving an example how wrong things can go if you are not consistent. You can forget what you meant when you do not comment your code. Then you can do something stupid and C will just have unexpected secondary effects with no warning or error. Therefore C is not a safe language.
If the composite type is already defined, you can create a type alias after or before its declaration. C compiler is using "hoisting" technique to find forward declarations. In next example we define a list of points, and we create two points in the list.
#include <stdio.h>
/* declare a struct named point */
typedef struct {
int x;
int y;
} point;
/* declare a type t_list from list */
typedef struct list t_list;
/* declare structure: list of points */
struct list {
point p;
t_list *next;
};
int main() {
/* define 2 members in a list */
t_list last = { .p = { .x = 3, .y =7 }, }; // last element
t_list first = { .p = { .x = 4, .y =5 }, .next = &last }; // first element
/* accessing first element */
printf("first.x = %d\n", first.p.x);
printf("first.y = %d\n", first.p.y);
/* accessing next element */
printf("next.x = %d\n", first.next->p.x);
printf("next.y = %d\n", first.next->p.y);
}
Observe: The structure t_list is recursive and nested. It is a list of points, where a point is also a structure of two integers: x, y. This structure has only two members but it could have more.
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int x;
int y;
} Struct; //define a type
// function with multiple results
Struct get_s() {
Struct s = {1, 2};
return s;
}
int main () {
Struct s; //variable not pointer
s = get_s();
printf ("s = {%d,%d}",s.x,s.y);
return 0;
}
Note: In previous example a function get_s() return a structure not a pointer. This is a single result with encapsulated elements. Observe that returning a structure do not require "static" keyword for variable "s". Since this is a structure is transferred outside of the function using "copy" unlike vectors that are pointers so they must be declared static to be returned by a function.
Read next: Pointer Arithmetic