Making programming versatile.
C is unusual in that it has a pre-processor. This comes from its Unix origins. As its name might suggest, the preprocessor is a phase which occurs prior to compilation of a program. The preprocessor has two main uses: it allows external files, such as header files, to be included and it allows macros to be defined. This useful feature traditionally allowed constant values to be defined in Kernighan and Ritchie C, which had no constants in the language.
Pre-processor commands are distinguished by the hash (number) symbol #. One example of this has already been encountered for the standard header file stdio.h. #include
is a command which tells the preprocessor to treat the file stdio.h as if it were the actually part of the program text, in other words to include it as part of the program to be compiled.
Macros are words which can be defined to stand in place of something complicated: they are a way of reducing the amount of typing in a program and a way of making long ungainly pieces of code into short words. For example, the simplest use of macros is to give constant values meaningful names: e.g. #define TELEPHNUM 720663
This allows us to use the word TELEPHNUM in the program to mean the number 720663. In this particular case, the word is clearly not any shorter than the number it will replace, but it is more meaningful and would make a program read more naturally than if the raw number were used. For instance, a program which deals with several different fixed numbers like a telephone number, a postcode and a street number could write: printf("%d %d %d",TELEPHNUM,postcode,streetnum);
instead of printf("%d %d %d",720663,345,14);
Using the macros instead makes the actions much clearer and allows the programmer to forget about what the numbers actually are. It also means that a program is easy to alter because to change a telephone number, or whatever, it is only necessary to change the definition, not to retype the number in every single instance.
The important feature of macros is that they are not merely numerical constants which are referenced at compile time, but are strings which are physically replaced before compilation by the preprocessor! This means that almost anything can be defined: #define SUM 1 + 2 + 3 + 4
would allow SUM to be used instead of 1+2+3+4. Or #define STRING "Mary had a little lamb..."
would allow a commonly used string to be called by the identifier "string" instead of typing it out afresh each time. The idea of a define statement then is: #define macroname definition on rest of line
Macros cannot define more than a single line to be substituted into a program but they can be used anywhere, except inside strings. (Anything enclosed in string quotes is assumed to be complete and untouchable by the compiler.) Some macros are defined already in the file stdio.h such as:
EOF
The end of file character (= -1 for instance)
NULL
The null character (zero) = 0
Macro functions:
Macros with parameters:
Example 6:
Note about include:
Other Preprocessor commands:
Example 7:
Questions 12:
Node:Macro functions, Next:Macros with parameters, Previous:Preprocessor, Up:Preprocessor
Macro Functions
A more advanced use of macros is also permitted by the preprocessor. This involves macros which accept parameters and hand back values. This works by defining a macro with some dummy parameter, say x. For example: a macro which is usually defined in one of the standard libraries is abs() which means the absolute or unsigned value of a number. It is defined below: #define ABS(x) ((x) < 0) ? -(x) : (x)
The result of this is to give the positive (or unsigned) part of any number or variable. This would be no problem for a function which could accept parameters, and it is, in fact, no problem for macros. Macros can also be made to take parameters. Consider the ABS() example. If a programmer were to write ABS(4) then the preprocessor would substitute 4 for x. If a program read ABS(i) then the preprocessor would substitute i for x and so on. (There is no reason why macros can't take more than one parameter too. The programmer just includes two dummy parameters with different names. See the example listing below.) Notice that this definition uses a curious operator which belongs to C:
This is like a compact way of writing an if..then..else statement, ideal for macros. But it is also slightly different: it is an expression which returns a value, where as an if..then..else is a statement with no value. Firstly the test is made. If the test is true then the first statement is carried out, otherwise the second is carried out. As a memory aid, it could be read as: if
(Do not be confused by the above statement which is meant to show what a programmer might think. It is not a valid C statement.) C can usually produce much more efficient code for this construction than for a corresponding if-else statement.
Node:Macros with parameters, Next:Example 6, Previous:Macro functions, Up:Preprocessor
When and when not to use macros with parameters
It is tempting to forget about the distinction between macros and functions, thinking that it can be ignored. To some extent this is true for absolute beginners, but it is not a good idea to hold on to. It should always be remembered that macros are substituted whole at every place where they are used in a program: this is potentially a very large amount of repetition of code. The advantage of a macro, however, is speed. No time is taken up in passing control over to a new function, because control never leaves the home function when a macro is used: it just makes the function a bit longer. There is a limitation with macros though. Function calls cannot be used as their parameters, such as: ABS(function())
has no meaning. Only variables or number constants will be substituted. Macros are also severely restricted in complexity by the limitations of the preprocessor. It is simply not viable to copy complicated sequences of code all over programs.
Choosing between functions and macros is a matter of personal judgement. No simple rules can be given. In the end (as with all programming choices) it is experience which counts towards the final ends. Functions are easier to debug than macros, since they allow us to single step through the code. Errors in macros are very hard to find, and can be very confusing.
Node:Example 6, Next:Note about include, Previous:Macros with parameters, Up:Preprocessor
Example Listing/************************************************************//* *//* MACRO DEMONSTRATION *//* *//************************************************************/ #include
Node:Note about include, Next:Other Preprocessor commands, Previous:Example 6, Up:Preprocessor
Note about #include
When an include statement is written into a program, it is a sign that a compiler should merge another file of C programming with the current one. However, the #include statement is itself valid C, so this means that a file which is included may contain #includes itself. The includes are then said to be "nested". This often makes includes simpler.
Node:Other Preprocessor commands, Next:Example 7, Previous:Note about include, Up:Preprocessor
Other Preprocessor commands
This section lies somewhat outside the main development of the book. You might wish to omit it on a first reading.
There are a handful more preprocessor commands which can largely be ignored by the beginner. They are commonly used in "include" files to make sure that things are not defined twice.
NOTE : true has any non zero value in C. false is zero.
#undef
This undefines a macro, leaving the name free.
#if
This is followed by some expression on the same line. It allows conditional compilation. It is an advanced feature which can be used to say: only compile the code between #if and #endif if the value following #if is true, else leave out that code altogether. This is different from not executing code--the code will not even be compiled.
#ifdef
This is followed by a macro name. If that macro is defined then this is true.
#ifndef
This is followed by a macro name. If that name is not defined then this is true.
#else
This is part of an #if, #ifdef, #ifndef preprocessor statement.
#endif
This marks the end of a preprocessor statement.
#line
Has the form:
#line constant filename
This is for debugging mainly. This statement causes the compiler to believe that the next line is line number (constant) and is part of the file (filename).
#error
This is a part of the proposed ANSI standard. It is intended for debugging. It forces the compiler to abort compilation.
Node:Example 7, Next:Questions 12, Previous:Other Preprocessor commands, Up:Preprocessor
Example/***********************************************************//* To compile or not to compile *//***********************************************************/ #define SOMEDEFINITION 6546#define CHOICE 1 /* Choose this before compiling */ /***********************************************************/ #if (CHOICE == 1) #define OPTIONSTRING "The programmer selected this"#define DITTO "instead of .... " #else #define OPTIONSTRING "The alternative"#define DITTO "i.e. This! " #endif /***********************************************************/ #ifdef SOMEDEFINITION #define WHATEVER "Something was defined!" #else #define WHATEVER "Nothing was defined" #endif /************************************************************/ main () {printf (OPTIONSTRING);printf (DITTO);}
Node:Questions 12, Previous:Example 7, Up:Preprocessor
Questions
Define a macro called "birthday" which describes the day of the month upon which your birthday falls.
Write an instruction to the preprocessor to include to maths library math.h.
A macro is always a number. True or false?
A macro is always a constant. True or false?
Node:Pointers, Next:Standard Output and Standard Input, Previous:Preprocessor, Up:Top
Pointers
Making maps of data.
You have a map (a plan) of the computer's memory. You need to find that essential piece of information which is stored at some unknown location. How will you find it? You need a pointer!
A pointers is a special type of variable which holds the address or location of another variable. Pointers point to these locations by keeping a record of the spot at which they were stored. Pointers to variables are found by recording the address at which a variable is stored. It is always possible to find the address of a piece of storage in C using the special & operator. For instance: if location were a float type variable, it would be easy to find a pointer to it called location_ptr. float location;float *location_ptr,*address; location_ptr = &(location);
or address = &(location);
The declarations of pointers look a little strange at first. The star * symbol which stands in front of the variable name is C's way of declaring that variable to be a pointer. The four lines above make two identical pointers to a floating point variable called location, one of them is called location_ptr and the other is called address. The point is that a pointer is just a place to keep a record of the address of a variable, so they are really the same thing.
A pointer is a bundle of information that has two parts. One part is the address of the beginning of the segment of memory that holds whatever is pointed to. The other part is the type of value that the pointer points to the beginning of. This tells the computer how much of the memory after the beginning to read and how to interpret it. Thus, if the pointer is of a type int, the segment of memory returned will be four bytes long (32 bits) and be interpreted as an integer. In the case of a function, the type is the type of value that the function will return, although the address is the address of the beginning of the function executable.
If, like some modern day programmers, you believe in sanctity of high level languages, it is probably a source of wonder why anyone Would ever want to know the address of these variables. Having gone to the trouble to design a high level language, like C, in which variables can be given elegant and meaningful names: it seems like a step in the backward direction to want to be able to find out the exact number of the memory location at which it is stored! The whole point of variables, after all, is that it is not necessary to know exactly where information is really stored. This is not quite fair though. It is certainly rare indeed when we should want to know the actual number of the memory location at which something is stored. That would really make the idea of a high level language a bit pointless. The idea behind pointers is that a high level programmer can now find out the exact location of a variable without ever having to know the actual number involved. Remember:
A pointer is a variable which holds the address of the storage location for another given variable.
C provides two operators & and * which allow pointers to be used in many versatile ways.
Pointer operators:
Uses for pointers:
Pointers and Initialization:
Example 8:
Types Casts and Pointers:
Function pointers:
Calling functions by pointer:
Questions 13:
Node:Pointer operators, Next:Uses for pointers, Previous:Pointers, Up:Pointers
& and *
The & and * operators have already been used once to hand back values to variable parameters, See Value parameters. They can be read in a program to have the following meanings:
&
The address of...
*
The contents of the address held in...
Another way of saying the second of these is:
*
The contents of the location pointed to by...
This reinforces the idea that pointers reach out an imaginary hand and point to some location in the memory and it is more usual to speak of pointers in this way. The two operators * and & are always written in front of a variable, clinging on, so that they refer, without doubt, to that one variable. For instance:
&x
The address at which the variable x is stored.
*ptr
The contents of the variable which is pointed to by ptr.
The following example might help to clarify the way in which they are used: int somevar; /* 1 */int *ptr_to_somevar; /* 2 */ somevar = 42; /* 3 */ ptr_to_somevar = &(somevar); /* 4 */ printf ("%d",*ptr_to_somevar); /* 5 */ *ptr_to_somevar = 56; /* 6 */
The key to these statements is as follows:
Declare an int type variable called somevar.
Declare a pointer to an int type called ptr_to_somevar. The * which stands in front of ptr_to_somevar is the way C declares ptr_to_somevar as a pointer to an integer, rather than an integer.
Let somevar take the value 42.
This gives a value to ptr_to_somevar. The value is the address of the variable somevar. Notice that only at this stage does is become a pointer to the particular variable somevar. Before this, its fate is quite open. The declaration (2) merely makes it a pointer which can point to any integer variable which is around.
Print out "the contents of the location pointed to by ptr_to_somevar" in other words somevar itself. So this will be just 42.
Let the contents of the location pointed to by ptr_to_somevar be 56. This is the same as the more direct statement: 7. somevar = 56;8.
Node:Uses for pointers, Next:Pointers and Initialization, Previous:Pointer operators, Up:Pointers
Uses for Pointers
It is possible to have pointers which point to any type of data whatsoever. They are always declared with the * symbol. Some examples are given below. int i,*ip; char ch,*chp; short s,*sp; float x,*xp; double y,*yp;
Pointers are extremely important objects in C. They are far more important in C than in, say, Pascal or BASIC (PEEK,POKE are like pointers). In particular they are vital when using data structures like strings or arrays or linked lists. We shall meet these objects in later chapters.
One example of the use of pointers is the C input function, which is called scanf(). It is looked at in detail in the next section. scanf() is for getting information from the keyboard. It is a bit like the reverse of printf(), except that it uses pointers to variables, not variables themselves. For example: to read an integer: int i; scanf ("%d",&i);
or int *i; scanf ("%d",i);
The & sign or the * sign is vital. If it is forgotten, scanf will probably corrupt a program. This is one reason why this important function has been ignored up to now.
Assembly language programmers might argue that there are occasions on which it would be nice to know the actual address of a variable as a number. One reason why one might want to know this would be for debugging. It is not often a useful thing to do, but it is not inconceivable that in developing some program a programmer would want to know the actual address. The & operator is flexible enough to allow this to be found. It could be printed out as an integer: type *ptr: printf ("Address = %d",(int) ptr);
Node:Pointers and Initialization, Next:Example 8, Previous:Uses for pointers, Up:Pointers
Pointers and Initialization
Something to be wary of with pointer variables is the way that they are initialized. It is incorrect, logically, to initialize pointers in a declaration. A compiler will probably not prevent this however because there is nothing incorrect about it as far as syntax is concerned.
Think about what happens when the following statement is written. This statement is really talking about two different storage places in the memory: int *a = 2;
First of all, what is declared is a pointer, so space for a `pointer to int' is allocated by the program and to start off with that space will contain garbage (random numbers), because no statement like a = &someint;
has yet been encountered which would give it a value. It will then attempt to fill the contents of some variable, pointed to by a, with the value 2. This is doomed to faliure. a only contains garbage so the 2 could be stored anywhere. There may not even be a variable at the place in the memory which a points to. Nothing has been said about that yet. This kind of initialization cannot possibly work and will most likely crash the program or corrupt some other data.
Node:Example 8, Next:Types Casts and Pointers, Previous:Pointers and Initialization, Up:Pointers
Example Listing/**********************************************//* *//* Swapping Pointers *//* *//**********************************************/ /* Program swaps the variables which a,b */ /* point to. Not pointless really ! */ #include
0 comments: on " "
Post a Comment