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


this 'const char *s' parameter must accept the string we want to print.
If you look back to the types listed above, you will notice that there is no string type among them. That is because C doesn't have a special string type. A string in C is just a series of chars, and the end of the string is indicated by a char with value 0. Such a series is called an array. But instead of passing the array to the puts() function, a pointer to it is passed. The pointer is indicated by the * and the const means that the puts() function will not modify the string we pass it. If this seems like Chinese for now (provided you don't live in China, of course), don't worry: this will be more thoroughly explained later in this document (see 10. Arrays, strings and pointers). All you need to know for now is that the above declaration means that puts() will take a string as parameter, and that that string will not be modified.
In our program, we pass puts() the parameter "Hello, world!". The double quotes indicate that this is a string literal (which basically comes down to a string that may not be modified [*]). Since puts() accepts a string that it will not modify, this works out perfectly. If we would have attempted to call puts() with e.g. a number as parameter, the compiler would most likely have complained.
[*] Attempting to modify a string literal will cause undefined behaviour. Some compilers/operating systems may allow string literals to be modified, but this will fail miserably on others (or even the same ones with different compiler options set), so should definitely never be done.
You must be careful to use double quotes ("), which indicate a string literal, because single quotes (') indicate a character literal (e.g. 'A') and these are not interchangeable. [*]
[*] Another peculiarity is that character literals in C are not of type char, but of type int. This is different from C++.
String and character literals may contain special escape codes to perform some implementation-defined actions:

\a alert (causes an audible or visual alert when printed)
\b backspace
\f formfeed (may clear the screen on some systems, don't rely on this)
\n newline (goes to the beginning of the next line; will be automatically
            translated to/from \r\n or another (if any) end-of-line
            sequence on systems where this is necessary)
\r carriage return (goes to the beginning of the current line)
\t horizontal tab
\v vertical tab
\\ backslash (be careful when using backslashes in literals!)
\' single quote (useful inside character literals)
\" double quote (useful inside string literals)
\? question mark (useful to avoid unwanted trigraph translation when two
                  question marks are followed by certain characters)
\<octal digits> character value in octal
\x<hex digits>  character value in hexadecimal

2.5. return
The next, and final, line of our small program is:

   return 0;

return exits a function and unless the function has return type void it must be followed by a value corresponding to its return type.
As we mentioned above, main() always returns an int value. In the case of main(), this value indicates to the operating system whether the program ended successfully or because of an error. Success is indicated by returning the value 0. Also, the constants EXIT_SUCCESS and EXIT_FAILURE may be used; they are defined in the standard header stdlib.h. [*]
[*] Usually, other return values than 0, EXIT_SUCCESS or EXIT_FAILURE are allowed as well for the return value of main(), but this is not standard so you should avoid it unless you have a specific reason to do so, provided you're aware this may restrict the range of operating systems your program will work on.
Note that the above is specific to the main() function; other functions that you write are of course not subject to these restrictions, and may take any parameters and return any value they like. [*]
[*] Some other functions, like the compare function for qsort() or the function passed to atexit() have to have a certain form as well, but those few exceptions are of a more advanced nature.

3. Using variables
3.1. The 42 program

/* prog3-1.c: 42 */

#include <stdio.h>

int main(void)
{
   int answer = 42;

   printf("And the answer is: %d\n", answer);
   return 0;
}


When compiled and run, this program should write the following onto your output device:

And the answer is: 42

3.2. Defining variables
The first new thing that stands out is the first line of the body of main():

   int answer = 42;

This line defines a variable named 'answer' of type int and initializes it with the value 42. This might also have been written as:

   int answer;        /* define uninitialized variable 'answer' */

   /* and after all variable definitions: */

   answer = 42;       /* assigns value 42 to variable 'answer' */


Variables may be defined at the start of a block (a block is the piece of code between the braces { and }), usually this is at the start of a function body, but it may also be at the start of another type of block (unlike in C++ where variables may be defined anywhere in the code).
Variables that are defined at the beginning of a block default to the 'auto' status. This means that they only exist during the execution of the block (in our case the function). When the function execution begins, the variables will be created but their contents will be undefined (unless they're explicitly initialized like in our example). When the function returns, the variables will be destroyed. The definition could also have been written as:

   auto int answer = 42;

Since the definition with or without the auto keyword is completely equivalent, the auto keyword is obviously rather redundant.
However, sometimes this is not what you want. Suppose you want a function to keep count of how many times it is called. If the variable would be destroyed every time the function returns, this would not be possible. Therefore it is possible to give the variable what is called static duration, which means it will stay intact during the whole execution of the program. For example:

   static int answer = 42;

This initializes the variable answer to 42 at the beginning of the program execution. From then on the value will remain untouched; the variable will not be re-initialized if the function is called multiple times!
Sometimes it is not sufficient that the variable be accessible from one function only (and it might not be convenient to pass the value via a parameter to all other functions that need it), but you need access to the variable from all the functions in the entire source file [*] (but not from other source files).
[*] The term the standard uses is "translation unit". On most implementations such a translation unit is simply a text file containing source code, often marked with a ".c" extension or something similar. Therefore in the rest of this document the term "source file" will be used where "translation unit" is meant.
This can also done with the static keyword (which may be a bit confusing), but by putting the definition outside all functions. For example:

#include <stdio.h>

static int answer = 42; /* will be accessible from entire source file */

int main(void)
{
   printf("And the answer is: %d\n", answer);
   return 0;
}


And there are also cases where a variable needs to be accessible from the entire program, which may consist of several source files (see 15. Modular approach). This is called a global variable and should be avoided when it is not required. This is also done by putting the definition outside all functions, but without using the static keyword:

#include <stdio.h>

int answer = 42; /* will be accessible from entire program! */

int main(void)
{
   printf("And the answer is: %d\n", answer);
   return 0;
}


There is also the extern keyword, which is used for accessing global variables in other modules. This is explained in 15. Modular approach).
There are also a few qualifiers that you can add to variable definitions. The most important of them is const. A variable that is defined as const may not be modified (it will take up space, however, unlike some of the constants in e.g. Pascal; see 13. Preprocessor macros/conditionals if you need an equivalent for those). For example:

#include <stdio.h>

int main(void)
{
   const int value = 42; /* constant, initialized integer variable */

   value = 100;    /* wrong! - will cause compiler error */

   return 0;
}


Then there are two more modifiers that are less commonly used:
The volatile modifier requires the compiler to actually access

Page : << Previous 3  Next >>