kota's memex

Derived types are types that are constructed from other c basic types. These include pointers, arrays, type definitions, structures, and unions.

Pointer types

A pointer type is derived from the function or object type that it points to, called the reference type. A pointer provides a reference to an entity of the referenced type.

int *ip;
char *cp;
void *vp;

The "address-of" operator & allows you to take the address of an object or function which can be assigned to a pointer of that type.

int i = 17;
int *ip = &i;

The indirection operator converts a pointer type into a value of that type. It denotes indirection and operates on pointers only. It the pointer it not pointer to a valid object or function, bad things may happen.

Arrays

An array is a contiguously allocated sequence of object that all have the same element type. Array types are characterized by their element types and the number of elements in the array. Here we declare an array of 11 element of type int identified by ia, and an array of 17 element of type pointer to float identified by afp:

int ia[11];
float *afp[17];

Square brackets are used to identify elements of an array. This contrived example creates a string "0123456789". We needed to use a bounds of 11 so we could terminate the string with a "null character" which is one of the more annoying aspects of c.

char str[11];
for (unsigned int i = 0; i < 10; ++i) {
  str[i] = '0' + i;
}
str[10] = '\0';

In the expression str[i]: str is automatically converted to a pointer to the first element of the array and i remains an unsigned integer type. So str[i] is equivalent to *(str + i). Another thing to note is that arrays are 0 indexed such that str[10] is referring to the 11th element.

Multidimensional arrays

Here's a two dimensional 5x3 array of type int (also known as a matrix):

void func(int arr[5]);

int main(void) {
	unsigned int i = 0;
	unsigned int j = 0;
	int arr[3][5];
	func(arr[i]);
	int x = arr[i][j];
	return 0;
}

In this case arr is an array of 3 elements. Each of which is an array of 5 elements of type int.

Type definitions

You can use a typedef to declare an alias for an existing type; it never creates a new type:

typedef unsigned int uint_type;
typedef signed char schar_type, *schar_p, (*fp)(void);

On the first line, we declared uint_type as an alias for the unsigned int type. On the second line we declare schar_type as an alias for signed char, schar_p as an alias for signed char *, and finally fp as an alias for signed char(*)(void).

In general you should avoid defining types that end in _t as the C standard reserves identifiers that match [0-9a-z]*_t and posix simply reserves everything that ends in _t.

Structures

A struct contains sequentially allocated member objects. Each object has its own name and may have a distinct type, unlike arrays, which must all be of the same type:

struct sigrecord {
	int signum;
	char signame[20];
	char sigdesc[100];
} sigline, *sigline_p;

This structure has three members objects: signum of type int, signame is an array of char consisting of 20 elements, and sigdesc is another array of char with 100 elements.

You can reference members of an object of the structure type by using the . structure member operator. If you have a pointer to a struc, you can reference its members with the -> structure pointer operator instead:

sigline.signum = 5;
strcpy(sigline.signame, "SIGINT");
strcpy(sigline.sigdesc, "Interrupt from keyboard");

sigline_p = &sigline;

sigline_p->signum = 5;
strcpy(sigline_p->signame, "SIGINT");
strcpy(sigline_p->sigdesc, "Interrupt from keyboard");

Unions

Union types are similar to structures, except that the memory used by the member objects overlaps. Unions can contain an object of one type at one time, and an object of a different type at a different time, but never both objects at the same time, and are primarily used to save memory. This example union contains 3 structures, n, ni, and nf. Which might be used in a graph or other datastructure in which some nodes can be int and other nodes can be float:

union {
	struct {
		int type;
	} n;
	struct {
		int type;
		int intnode;
	} ni;
	struct {
		int type;
		double doublenode;
	} nf;
} u;
u.nf.type = 1;
u.nf.doublenode = 3.14;

As with structs you can access the union members with the . operator. Using a pointer you can also use the -> operator. Code that uses the above union would check the value stored in u.n.type and then accessing either the intnode or doublenode struct depending on the type. If this has instead been implemented with a struct it would require the storage for both an int and float for each node.

Tags

Tags are a special naming mechanism for structs, unions, and enumerations. For example, the identifier s here is a tag:

struct s {
  // stuff
};

By itself, a tag is not a type name and cannot be used to declare a variable. Instead, you must declare variables of this type as follows:

struct s v; // instance of struct s
struct s *p; // pointer to struct s

The names of unions and enums are also tags not types, meaning that they cannot be used along to declare a variable. For example:

enum day { sun, mon, tue, wed, thu, fri, sat };
day today; // error
enum day tomorrow; // OK

Tags are also defined in a seperate namespace from ordinary identifiers so you can have both a tag and another identifier with the same spelling in the same scope:

enum status { ok, fail }; // enum with tag status
enum status status(void); // function named status