C functions are the building blocks of all C programs. In fact, in C there is no such concept as a ‘main program’ (like in BASIC or Pascal, for example). The main program is a function by itself — by convention called
Most of C programming consists of writing and calling functions. A huge, monolithic program is considered bad practice since this is a structured language. There's also another reason to use functions: you avoid the useless repetition of code, which, on the Oric, is deﬁnitely a bad idea.
Here's the syntax of a C function, along with an example of a function to calculate the sum of the squares of its two parameters (called arguments in C).
Here are the basic parts of the function, in order of appearance:
What the function returns. We may return any data type, as long as it takes four bytes or less (two bytes or less for the 16-bit compiler). This may not seem very useful (how do you return a string?), but we'll soon see that it all gets solved using pointers (i.e. instead of returning the data type itself, we return its address in memory). More of this later on. By the way, an interesting data type is
void, which is the empty data type. If you use it here, you're eﬀectively saying that your function will not return anything (something like a BASIC subroutine or a Pascal procedure). You can skip the type altogether:
int is the default value.
Simply put, the name of the function. Like all C identiﬁers, function identiﬁers may only consist of lower or upper-case letters, and the underscore (
_). The case of the letters matters (as usual), and the identiﬁer may have any length you want. A point about this: don't use short, cryptic identiﬁers to save memory. This is a compiled language, which means that identiﬁers are for the user's reference only and are not stored in the executable program.
The argument list: this is a list of comma-delimited argument declarations. The list has to be enclosed in parentheses. If there aren't any arguments, the argument list is empty, but you still have to have the parentheses (i.e.
()). An argument declaration consists of a type and a argument identiﬁer: it's just like declaring variables. Unlike variable declarations, though, you cannot declare two
(int x, y). C, for some perverted reason I won't even try to guess, wants a data type to the left of each and every argument identiﬁer.
The body of the function: it must be enclosed in curly brackets. It contains (of course) variable declarations and statements. The arguments of the function are accessible here just like perfectly normal variables (which they are).
An important point: nothing stops you from declaring a variable with the same name as an argument. This is called shadowing an argument. The variable takes precedence over the argument (since the variable was deﬁned after the argument). Say you have an argument
x, and you also declare
x as a variable inside the function. Every time you refer to
x, you refer to the variable, and not the argument. This may result in a lot of problems if you haven't noticed what's going on.
Speaking of variables, remember that, unlike global variables which are always ﬁlled with
0, the initial values of variables in functions are undeﬁned. Take care to initialise your function variables to a suitable value.
Finally, don't forget that your function may have to
return a value. If the function type is
void, you don't need to return anything. If you need to bail out of the function, you use
return with no arguments. If the function type is anything else but
void, you have to return a value whenever you exit the function. Use
return with an expression as its argument to return a value. You can see this in the short example function. If you don't explicitly return a value, anything can get back to the caller. Most compilers warn you of this, of course.
Here's an alternative way of declaring a function (with the same example):
What's diﬀerent here? Basically, the argument list may just contain the identiﬁers of the arguments. Their types are declared between the closing parenthesis and the opening curly bracket. The manner of declaration here is identical to that of declaring variables. As you can see in the example, we declare
y as ‘
int x, y;’.
This redundancy has a reason: by not declaring any types for the arguments, you stop the compiler from complaining whenever you try to pass a value of the wrong type to the function. This is quite useful sometimes.
Here's the catch. You can't use a C function unless it's already been declared earlier on in the program. Well, that's not exactly true. You can do this, but the compiler seldom likes it, because it needs to check if the variables you pass to the function are of the right type and, unless the function is already deﬁned, the types are unknown.
The solution involves prototype declarations: function declarations without bodies. To use a function
A before it's declared, copy its header (the type, name and argument list — only the bit inside the parentheses) right above the function where you want to call
A. The prototype for
sum_of_squares() would be like this:
This is also used in the C Header ﬁles (
.h ﬁles), where the various library functions are merely prototyped, not actually implemented (because they're written in Assembly, not C).
Due to the way the 16-bit compiler is implemented, the variables (including arguments) local to a function cannot exceed 256 bytes in size. In fact, this holds for all non-global variables (i.e. all variables inside curly brackets). I don't have complete details on the nature of this limitation, but it can be quite annoying (you'll have to consider making some of your variables global). The 32-bit compiler is not limited in this way, fortunately.