Topic : An introduction to C
Author : Tom Torfs
Page : << Previous 2  Next >>
Go to page :


is:

#include "file"

This directive will be replaced with the contents of file. Usually this form is used for non-standard headers that are part of your program (see 15. Modular approach).2.4. Functions and types
Now onto the next line: (the { and } braces belong to this line as well)

int main(void)
{
}


This is a function definition. A function is a part of a program that can be called by other parts of the program. A function definition always has the following form:

type name(parameters)
{
   /* the function code goes here */
}


The function will return a value of type 'type' to the caller. C supports the following types: (note that the ranges indicated are minimum ranges, they may (and will!) be larger so you shouldn't rely on them having a certain size)
Integer types: (non-fractional numbers)

signed char    minimum range: -127..+127
unsigned char  minimum range: 0..255
signed short   minimum range: -32767..+32767
unsigned short minimum range: 0..65535
signed int     minimum range: -32767..+32767
unsigned int   minimum range: 0..65535
signed long    minimum range: -2147483647..+2147483647
unsigned long  minimum range: 0..4294967295

The type char may be equivalent to either signed char or unsigned char (that depends on your compiler), but it is always a separate type from either of these. Also notice that in C there is no difference between storing characters or their corresponding numerical values in a variable, so there is also no need for a function to convert between a character and its numerical value or vice versa (this is different from languages like Pascal or BASIC).
For the other integer types, if you omit signed or unsigned the default will be signed, so e.g. int and signed int are equivalent.
The type int must be greater than or equal to the type short, and smaller than or equal to the type long. This depends on your compiler and operating system. If you simply need to store some values which are not enormously large it's often a good idea to use the type int; it usually is the size the processor can deal with the easiest, and therefore the fastest.
For the next revision of the C standard, an even larger type 'long long' is proposed. You shouldn't use it yet for now. If you need that large values, you may want to use floating-point types.
Floating point types: (fractional numbers)

float       minimum range: +/- 1E-37..1E+37  minimum precision: 6 digits
double      minimum range: +/- 1E-37..1E+37  minimum precision: 10 digits
long double minimum range: +/- 1E-37..1E+37  minimum precision: 10 digits

With several compilers double and long double are equivalent. That combined with the fact that most standard mathematical functions work with type double, is a good reason to always use the type double if you have to work with fractional numbers.
Special-purpose types: (don't worry too much about these yet)
Commonly used ones:

size_t        unsigned type used for storing the sizes of objects in bytes
time_t        used to store results of the time() function
clock_t       used to store results of the clock() function
FILE          used for accessing a stream (usually a file or device)

Less commonly used ones:

ptrdiff_t     signed type of the difference between 2 pointers
div_t         used to store results of the div() function
ldiv_t        used to store results of ldiv() function
fpos_t        used to hold file position information

More advanced ones:

va_list      used in variable argument handling
wchar_t      wide character type (used for extended character sets)
sig_atomic_t used in signal handlers
jmp_buf      used for non-local jumps

If the function does not return anything you can use the pseudo-type void as the return value type (this is not allowed for main(), see below).
The function name follows the rules for all names (formally: identifiers): it must consist entirely of letters (uppercase and lowercase are different! [*]), digits and underscores (_), but may not begin with a digit.
[*] during linking, case differences in external names might be ignored; so it's often not a good idea to use 2 variable/function names that only differ in their case (this would also reduce clarity)
The parameter list may be void in which case the function won't take any parameters. Otherwise it should be a list of the following form:

type1 name1, type2 name2, type3 name3     etc.

The possible types are the same as for the return value, and the names follow the same rules as those for the function name. An example:

double myfunction(int foo, long bar)
{
}


Is a function called 'myfunction' that accepts a parameter of type int and a parameter of type long and returns a value of type double
The parameters' values will be filled in by the callers. The above function might be called as follows:

myfunction(7,10000);

In this case the return value is ignored. If it's needed we may assign it to a variable as follows: (see 3. Using variables)

somevariable = myfunction(7,10000);

A function that takes no parameters (which has void as its parameter list) would be called as follows:

myfunction();

Notice that unlike in e.g. Pascal you can't do something like: [*]

myfunction;             /* wrong! */

[*] This will probably compile, but it does something entirely different than you think (basically, it does nothing).
Here's an example of a situation where using a function provides advantages, apart from the readability advantage that using a function instead of lumping all code together always provides.

   do_something();
   check_error_condition();

   do_something_else();
   check_error_condition();


   etc.

Suppose the do_something() and do_something_else() functions perform some useful tasks. However, it is possible that an error occurs while performing these tasks, and if that happens these functions set some sort of error condition to a specific value. By looking at this value, the calling code can then print an appropriate error message and/or take appropriate actions. But if the code to do this would have to be copied everywhere a function like do_something() is used, the program would get enormously bloated, not to mention the fact that if something has to be changed in this error handling code, it may have to be changed in hundreds of places.
Now, if this code is put in its own function, say check_error_condition(), all that has to be done in all these places is a call to this function. The code stays compact and readable, and if something has to be changed in the error handling code, it only has to be changed in one place.
main() is a very special function: it is the function that is called at the beginning of our program's execution. Because it's so special we are not free to choose its return value or parameters. Only two forms are allowed:

int main(void)
{
}


Which you should use if you don't need access to command line arguments.

int main(int argc, char *argv[])
{
}


Which allows access to the command line arguments (see 10.5. Example: using command line arguments).
In some source code you may find other definitions of main(), such as returning void or taking 3 parameters etc. These may work on certain compilers but they are not standard so you should avoid them.
The actual function code (called the function body) goes inside the { and } braces. In this case the first line of the function body is:

   puts("Hello, world!");

The puts() function is declared in the standard header stdio.h as follows:

int puts(const char *s);

This is not a function definition, because it is not followed by { and } braces but by a semicolon, which in C means 'end of statement'. Instead, it is a function declaration (also known as 'prototype') which informs the compiler of the return value and parameter types of this function. The actual function definition can be found somewhere in your compiler's libraries, but you needn't worry about that because the declaration provides all the information you need in order to be able to call this function.
The return value of the puts() function is an int, and its meaning is rather straightforward: it returns EOF (which is a negative value defined in stdio.h) if an error occurs, or a nonnegative value if it's successful. We ignore this return value in our small program, but in 'real' code it's often a good idea to check these return values, and if an error occurs deal with it properly.
But the parameter, that's something else. To understand it, we must first know what puts() does: it writes a string (= text) to the standard output device (usually the screen, but it may be another device or a file) and then moves on to the next line. So obviously

Page : << Previous 2  Next >>