In mathematics, a function is used as a placeholder for an equation of some number of algebraic variables.
f(x) = x2 + x + (5/4)
A function has a name (e.g. 'f'), as well as named arguments, which correspond to the algebraic variables that form the equation (e.g. 'x').
int square( int value ) { return value * value; }
Program code contains:
- names
- keywords
- symbols (denoting structure or operators)
- constant values (numbers, characters, or strings)
In programming, names identify functions, variables, or types.
Programming languages have primitive types that occupy one addressable location† of memory, eg.
- char
- int
- float
- bool
- pointer (*) - memory address
† an addressable location may use one or more consecutive bytes depending on the machine code instruction executed of the underlying CPU architecture.
Composite types allow grouping of primitive types
struct _coordinates { double lat; double lng; };
Composite types are declared (used) in the same way as primitive types, and like primitive types can be returned from a function.
struct _coordinates CreateCoordinates() { struct _coordinates position; position.lat = -27.415325; position.lng = 153.0707667; return position; }
The 'dot notation' is used to access the parts of a composite type.
Composite types can often also include other defined composite types†
struct _route { struct _coordinates src; struct _coordinates dst; };
† Warning: depends on the programming language.
It is not often explicilty discussed, but the names associated with functions also have a type - a function pointer.
int add( int x, int y ) { return x + y; } int (*add2)(int,int) = add;
Function pointers can be assigned into variables that are declared with a compatible function pointer type.
Function pointers can also be declared within struct definitions.
struct _route { struct _coordinates src; struct _coordinates dst; struct _coordinates (*midpoint)( struct _route ); };
However, the 'midpoint' function pointer needs to be initialised to the address of a valid function before it can be used.
struct _coordinates Midpoint( struct _route route ) { struct _coordinates midpoint; midpoint.lat = (route.src.lat + route.dst.lat) / 2; midpoint.lng = (route.src.lng + route.dst.lng) / 2; return midpoint; } struct _route CreateRoute( double lat1, lng1, lat2, lng2 ) { struct _route route; route.midpoint = Midpoint; route.src.lat = lat1; route.src.lng = lng1; route.dst.lat = lat2; route.dst.lng = lng2; return route; }
Composite types can also be initialised by using the curly bracket notation.
route.src = { lat1, lng1 };
Composite types may contain pointers to other composite types.
struct _listnode { struct _listnode* next; struct _route* element; };
Pointers allow a composite type to contain a reference to another structure of the same type as itself.
Generally, the 'calloc' function is used to allocate enough space for a structured type on the heap.
Calloc returns the memory address of the allocated and zeroed memory.
struct _listnode* CreateListNode( struct _route* aRoute ) { struct _listnode* node; node = calloc( 1, sizeof( struct _listnode ) ); node->element = aRoute; node->next = NULL; return node; }
As 'node' is a pointer, the "->" operator must be used to de-reference the pointer before accessing the struct's parts.
int main( int argc, char** argv ) { struct _route route = CreateRoute(); struct _coordinates midpoint = route.midpoint( route ); // Same as: // // struct _coordinates midpoint = Midpoint( route ); return 0; }
Executing a function pointer is exactly the same as executing a function.
A structure of structs may contain any number of hierarchical levels.
int main( int argc, char** argv ) { struct _dont_know something; something.else.entirely.in.memory.value = 10; something.else.entirely.in.memory.func( 10 ); something.else->entirely.out->memory.value = 10; something.else->entirely.out->memory.func( 10 ); return 0; }
The only restriction is that a struct cannot include a struct of its own type within itself. If that is desired, a pointer must be used.
A pure function is deterministic and causes no side-effects.
Non-pure functions may cause side-effects by altering global memory
or altering the state of a passed parameter to the function.