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').

In programming, functions are similarly used as an abstraction that allows computation to be talked about in terms of descriptively named functions rather than the underlying statements that produce the desired result.
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.