A Little C Primer/An Introductory C Program

Here's a simple C program to calculate the volume of a sphere:

/* sphere.c */

#include <stdio.h>                  /* Include header file for printf. */

#define PI 3.141592654f             /* Define a constant. */

static float sphere(float);         /* Function prototype. sphere is given internal linkage with the keyword
                                        'static' since it isn't used outside of this translation unit. */

int main(void)                      /* Main function. */
{
	float volume;               /* Define a float variable. */
	float radius = 3;           /* Define and initialize variable. */
	
	volume = sphere(radius);    /* Call sphere and copy the return value into volume. */
	printf( "Volume: %f\n", volume );  /* Print the result. */
	return 0;
}

static float sphere(float rad)             /* Definition of a volume calculating function. */
{ 
	float result;               /* "result" is defined with an automatic storage duration and block scope. */
	
	result = rad * rad * rad;
	result = 4 * PI * result / 3;
	return result;              /* "result" is returned to the function which called sphere. */
}

The first feature that can be observed in this program is that comments are enclosed by "/*" and "*/". Comments can go almost anywhere, since the compiler ignores them. Another obvious feature is that individual statements in the program end with a ";". Either forgetting a ";" or adding one where it isn't needed is a common C programming bug. Lines of code are grouped into blocks by using curly brackets ("{ }").

C is case-sensitive. All C keywords are in lower-case. Program variable and function names in whatever case desired, but by convention they should be in lower case. "Constants", to be discussed a bit later, are upper case by convention.

Not all the lines in a C program are executable statements. Some of the statements shown above are "preprocessor directives".

C compilation is a multi-pass process. To create a program, a C compiler system performs the following steps:

  • It runs the source file text through a "C preprocessor". All this does is perform various text manipulations on the source file, such as "macro expansion", "constant expansion", "file inclusion", and "conditional compilation", which are also explained later. The output of the preprocessor is a second-level source file for actual compilation. You can think of the C preprocessor as a sort of specialized automatic "text editor".
  • Next, it runs the second-level source file through the compiler proper, which actually converts the source code statements into their binary equivalents. That is, it creates an "object file" from the source file.
  • The object file still cannot be executed, however. If it uses C library functions, such as "printf()" in the example above, the binary code for the library functions has to be merged, or "linked", with the program object file. Furthermore, some addressing information needs to be linked to the object file so it can actually be loaded and run on the target system.

These linking tasks are performed by a "linker", which takes one or more object files and links them to binary library files to create an "executable" file that can actually be run.

C has a large number of libraries and library functions. C by itself has few statements, so much of its functionality is implemented as library calls.

Commands intended for the C preprocessor, instead of the C compiler itself, start with a "#" and are known as "preprocessor directives" or "metacommands". The example program above has two such metacommands:

  #include <stdio.h>

  #define PI 3.14

The first statement, "#include <stdio.h>", simply merges the contents of the file "stdio.h" into the current program file before compilation. The "stdio.h" file contains declarations required for use of the standard-I/O library, which provides the "printf()" function.

Incidentally, the "stdio.h" file, or "header file", only contains declarations. The actual code for the library is contained in separate library files that are added at link time. Custom header files loaded with custom declarations can be created if needed, and then included in a program as follows:

   #include "mydefs.h"

Angle brackets ("< >") are not used with custom header files. They only are used to define the default header files provided standard with a C compiler (and which, on Unix/Linux systems, are found in directory "/usr/include" ).

A C program is built up of one or more functions. The program above contains two user-defined functions, "main()" and "sphere()", as well as the "printf()" library function.

The "main()" function is mandatory when writing a self-contained program. It defines the function that is automatically executed when the program is run. All other functions will be directly or indirectly called by "main()".

A C function is called simply by specifying its name, with any arguments enclosed in following parentheses, with commas separating the arguments. In the program above, the "printf()" function is called as follows:

   printf( "Volume: %f\n", volume );

This invocation provides two arguments. The first—"Volume: %f\n"—supplies text and some formatting information. The second—"volume"—supplies a numeric value.

A function may or may not return a value. The "sphere()" function does, and so is invoked as follows:

   volume = sphere( radius );

A function uses the "return" keyword to return a value. In the case of "sphere", it returns the volume of the sphere with the statement:

   return result;

All variables in a C program must be "declared" by specifying their name and type. The example program declares two variables for the "main" routine:

   float volume;
   float radius = 3;

—and one in the "sphere" routine:

   float result;

The declarations of "volume", "radius", and "result" specify a floating-point variable. The declaration allows variables to be initialized when declared if need be, in this case declaring "radius" and assigning it a value of "3".

All three of these declarations define variables block scope. Variables with block scope exist only within the blocks that declare them. Variables of the same name could be declared in different blocks without interference. It is also possible to declare variables with file scope that can be shared by all functions by declaring them outside of all blocks within a translation unit. By default, all identifiers with file scope are given external linkage. It is only with the keyword "static" that an identifier is given internal linkage. Declarations like this:

extern int a;

identify "a" as an int whose storage is defined elsewhere; preferably in another translation unit.

   /* global.c */

   #include <stdio.h>

   void somefunc( void );
   int globalvar;

   void main()
   {
     extern int globalvar;
     globalvar = 42;
     somefunc();
     printf( "%d\n", globalvar );
   }

   void somefunc( void )
   {
     extern int globalvar;
     printf( "%d\n", globalvar );
     globalvar = 13;
   }

Besides the variable declarations, the example programs above also feature a function declaration, or "function prototype", that allows the C compiler to perform checks on calls to the function in the program and flag an error if they are not correct:

   float sphere(float);

The function prototypes declare the type of value the function returns (the type will be "void" if it does not return a value), and the arguments that are to be provided with the function.

Finally, the "printf()" library function provides text output capabilities for the program. The "printf()" function can be used to print a simple message as follows:

   printf( "Hello, world!" );

—displays the text:

   Hello, world!

Remember that "printf()" doesn't automatically add a "newline" to allow following "printf()"s to print on the next display line. For example:

   printf( "Twas bryllig " );
   printf( "and the slithy toves" );

—prints the text:

   Twas bryllig and the slithy toves

A newline character ("\n") must be added to force a newline. For example:

   printf( "Hello,\nworld!" );

—gives:

   Hello,
   world!

These examples only print a predefined text constant. It is possible to include "format codes" in the string and then follow the string with one or more variables to print the values they contain:

   printf( " Result = %f\n", result );

This would print something like:

   Result = 0.5

The "%f" is the format code that tells "printf" to print a floating-point number. For another example:

   printf( "%d times %d = %d\n", a, b, a * b );

—would print something like:

   4 times 10 = 40

The "%d" prints an integer quantity. Math or string expressions and functions can be included in the argument list.

To simply print a string of text, there is a simpler function, "puts()", that displays the specified text and automatically appends a newline:

   puts( "Hello, world!" );

Just for fun, let's take a look at what our example program would be like in the pre-ANSI versions of C:

   /* oldspher.c */

   #include <stdio.h>
   #define PI 3.141592654

   float sphere();        /* Parameters not defined in function prototype. */

   main()
   {
     float volume;
     int radius = 3;

     volume = sphere( radius );
     printf( "Volume: %f\n", volume );
   }

   float sphere( rad )
   int rad;               /* Parameter type not specified in function header. */
   { 
     float result;

     result = rad * rad * rad;
     result = 4 * PI * result / 3;
     return result;
   }

The following sections elaborate on the principles outlined in this section.