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.