Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
ANSI C Macros
1. ANSI C Macros – The C Preprocessor
S.SRIKRISHNAN
PRE - FINAL YEAR
SSN COLLEGE OF ENGINEERING
ne of the features not included in the original C language was the concept of
constants. In order to accommodate various features, (including constants),
the authors of C developed the C preprocessor. None of the services provided
by the C preprocessor (cpp) is indispensible for writing C programs, but these services
make the task considerably easier.
Outline:
1. How the preprocessor works
2. The #define Directive
3. Constants
4. Macros
5. Macros with Arguments
6. The #undef Directive
7. The #include Directive
8. Conditional compilation : #ifdef, #ifndef, #endif
9. Conditional compilation : #if, #else
10.Predefined Macros
O
2. How The Preprocessor Works
When you issue the command to compile a C program, the program is run
automatically through the preprocessor
The preprocessor is a program that modifies the C source program according
to the directives supplied in the program.
The preprocessor does not modify the program file, but creates a new file that
contains the processed version of the program.
This new file is then submitted to the compiler.
Fig(a) The Compilation Process
If a program contains the directive
#define NULL 0,
and the statement
x = NULL;
the preprocessor replaces every occurrence of NULL following the #define directive
with 0.
The resulting program no longer includes the directive (since the directives are
only for the preprocessor, not the compiler), and the preceding statement now reads
as follows:
x = 0;
Constants, therefore, are abbreviations supplied for the convenience of the
programmer. Each occurrence of a constant is translated by the preprocessor so that
3. the program is comprehensible to the C compiler. The preprocessor can also delete or
add C program statements.
Note:
All preprocessor directives begin with the number or sharp sign (#).
The directive is terminated not by a semicolon, but by the end of the line on
which it appears.
Only one directive can occur on a line.
A preprocessor symbol is never replaced if it occurs within single or double
quotation marks.
The preprocessor does not check for normal C syntax, except for identifying
quotation marks. It merely substitutes symbols where it finds them.
The #define Directive
The #define directive is used to define a symbol to the preprocessor and assign
it a value. The symbol is meaningful to the preprocessor only in lines of code
following the definition.
For example, if the directive
#define NULL 0,
is included in the program, then in all lines following the definition, the symbol NULL
is replaced by the symbol 0. If the symbol NULL is encountered before the definition,
it is not replaced.
The #define directive is followed by one or more spaces or tabs and the symbol
to be defined. It cannot be a C keyword or an identifier, if it is, a syntax error is
detected by the compiler.
For example, suppose the program contains the directive
#define dumb 54
which in turn is followed by the declaration
4. int dumb;
This would be translated by the preprocessor into
int 54;
which would be rejected by the compiler.
If a #define directive does not fit on a single line, it can be continued on
subsequent lines. All lines of the directive except the last line must end with a
backslash() character. A directive can be split only at a point where a space is
legal.For example:
#define max(a,b)
({ typeof (a) _a = (a);
typeof (b) _b = (b);
_a > _b ? _a : _b; })
Constants
A common use for defined symbols is the implementation of named constants.
The following are the examples of constants in C:
25
1.23
„a‟
“hello”
-6
Any of these values can be assigned to a defined preprocessor symbol:
#define INTEGER 25
#define CHARACTER „a‟
A defined symbol can specify only a complete constant. For example, if a
program contains the definitions
#define NULL 0
the number 120 cannot be represented as 12NULL. A defined symbol can be
recognized only if it is delimited by a white space, punctuation, or operators.
(This rule also applies to C identifiers, such as variables or function names.)
5. Macros
The following is a valid preprocessor directive.
#define TEST if (a > b)
The symbol TEST is defined to be the entire contents of the directive following
the symbol TEST; that is the string
if (a > b)
The statement
TEST printf(“It workedn”);
would be translated to
if (a > b) printf(“It workedn”);
Some programmers include the following definitions in all their programs:
#define and &&
#define or ||
These definitions enable the programmer to write more readable code, such as
the following:
if(a < b or c > d and e < f)
Macros With Arguments
The C preprocessor permits macros with arguments, just as functions do.
Consider the following example:
#define Decrement(x) if(x > 0) x -= 1
( Just like a function to return the decremented valu. Note: No values returned here)
6. Code 1:
#include<stdio.h>
#define Decrement(x) if(x>0) x-=1
int main(void)
{
int k=11;
Decrement(k);
char c='B';
Decrement(c);
printf("%X %c",k,c);
return 1;
}
Output:
A A
Code 2:
#include<stdio.h>
#define NUM_PRINT(value,spec)printf(“value= %specn”,value)
int main(void)
{
int k=11;
NUM_PRINT(k,d);
return 1;
}
Output:
value=11
What if we have:
#define NUM_PRINT(n,spec)printf(“n= %specn”,n)
Code 3:
#include<stdio.h>
#define TOHUNDRED(x) (x * 100)
void main()
{
int a=12,b=4;
printf(“%d”,TOHUNDRED(a+b));
}
7. Output:
412
Code 4:
#include<stdio.h>
#define sqr(a) ((a)*(a))
int main()
{
int x=7;
printf(“%c”,(char)sqr(x));
return 1;
}
Output:
1
A macro can be defined in terms of another macro, as in the following example
of nested macros:
#define CONTROL “%dn”
#define printint(x) printf(CONTROL,x)
#define TEST(x) if(x>0) printint(x)
If the program contains the statement
TEST(w);
where w is an integer variable, the statement goes through the following conversion
types:
if (w>0) printint(w);
if (w>0) printf(CONTROL,w);
if (w>0) printf(“%dn”,w);
Note:
A macro definition cannot contain itself, as in
#define infinity infinity
#define A B
8. #define B A
There are also serious limits to the preprocessor‟s ability to detect hidden recursion,
so these errors might not be detected until the preprocessor attempts to make the
substitutions and finds itself in an infinite loop.
When macros and when functions?
A function that would consist of only one line of code usually should be
implemented as a macro, to save computing time.
If memory space is the primary consideration, however, a macro may not be
the best choice.
Macros are more powerful than functions in that their arguments can be any
strings at all – They can be used with arguments of different types.
The #include Directive
Often a programmer accumulates a collection of useful constants and macro
definitions that are used in almost every program. It is desirable to be able to store
these definitions in a file that can be inserted automatically into every program. This
task is done by the #include directive.
A file is inserted at any point where the #include directive is encountered.
Such files are called as header files, and by convention their names end with
characters „.h‟ (as in stdio.h).
Header files can contain any text at all. Aside from preprocessor directives to
define macros, they may also contain C code to define structure templates, global
variables, or function definitions.
An example of an inclusion you have already seen is
#include<stdio.h>
in which stdio.h is the file to be included. The angular brackets tell the preprocessor
to search for the file in one or more standard directories. These directories contain
9. the header files that are provided by the system. If the brackets are replaced with
double quotation marks, as in
#include “stdio.h”
the preprocessor looks first in the programmer‟s own directory, or the same one that
contains the program files. If it is not found there, then standard directories are
searched.
Another common use of the header files is to provide consistency among
several program files. Often a program is so large that it is convenient to break it
down into smaller units, each of which is stored in a separate file. After these
separate program files are compiled, they must somehow be linked together to form a
single file. This linking is usually accomplished by a program called the linkage editor,
which is often run automatically when the program is compiled.
A header file can contain other #include directives. It cannot include itself,
because this would lead to infinite recursion. It cannot include another file that
includes this file, as this would also lead to infinite recursion.
C standard library
<assert.h>
<complex.h>
<ctype.h>
<errno.h>
<fenv.h>
<float.h>
<inttypes.h>
<iso646.h>
<limits.h>
<locale.h>
<math.h>
<setjmp.h>
<signal.h>
10. <stdarg.h>
<stdbool.h>
<stddef.h>
<stdint.h>
<stdio.h>
<stdlib.h>
<string.h>
<tgmath.h>
<time.h>
<wchar.h>
<wctype.h>
The #undef Directive
It may be necessary to redefine a macro at some point in a program. For
example, the user may wish to redefine a macro or constant specified in a header file
such as stdio.h. If only a few macros from such a file need to be redefined, the
easiest way is to use #include the entire file and then redefine the macros in the
question.
For example, suppose the programmer wishes the constant EOF to have the
value -2, the programmer could begin with the following directives:
#include<stdio.h>
#undef EOF
#define EOF -2
(In practice, it would be dangerous to redefine the value of EOF; it is done
here merely as an example)
If a preprocessor symbol has already been defined, it must be undefined before
being redefined. This is accomplished by the #undef directive, which specifies the
name of the symbol to be undefined.
11. It is not necessary to perform the redefinition at the beginning of a program. A
symbol can be redefined in the middle of a program, so that it has one value in the
first half and another value at the end.
A symbol need not be redefined after it is undefined. If the program uses the
symbol in a statement after the point at which it is undefined, however, the symbol is
not replaced. A defined symbol will be replaced before the point (if any) at which it is
undefined.
Conditional Compilation : #ifdef, #ifndef, #endif
The general idea behind conditional compilation is that a piece of code can be
selectively compiled, depending upon whether a specific value has been #defined, or
not. Reasons for doing this vary, but the main ones seem to be:
Platform specific constraints
Debug builds
Performance issues
(When we talk of 'platform specific' here, we mean flavors of the same platform,
rather than different operating systems or hardware platforms.)
In essence, the aim is to create a different executable file (application),
depending on several flags that are set by the programmer. Although, as we shall see,
the technique is also used to prevent multiple #includes, which generate errors during
the compile process.
If an #ifdef return a true value, all the lines between the #ifdef and the
corresponding #endif directive are left in the program. If those lines contain
preprocessor directives, the directives are processed. If #ifdef evaluates as false, the
associated lines are ignored, including the preprocessor directives included.
Even though we talk about conditional compilation directives in the same terms
as the C if statement, the preprocessor directives are executed before the
compilation is initiated. Thus, there can be no overlap between the C code and the
preprocessor directives. For example, the #ifdef directive cannot be used to test for
the declaration of a variable.
12. The simplest sort of conditional is
#ifdef NAME
/* compile these lines if NAME is defined */
#endif
#ifndef NAME
/* compile these lines if NAME is not defined */
#endif
Sample piece of Code 1:
#ifdef LINKED_LIST
Add_node(p,&inv_list);
#endif
Sample piece of Code 2:
#ifndef MAX_LEN
#define MEX_LEN 1000
#endif
If the programmer wants FLAG never to be defined, then the following can be done:
#ifdef FLAG
#undef FLAG
#endif
Conditional Compilation : #if, #elif, #else
There are 2 main disadvantages with the #ifdef and the #ifndef directives:
It is not possible to test whether a symbol has a specific value.
There are no connecting logical AND or OR operators.
These are overcome by the more general #if - #else directive
#ifdef name
/*program text*/
#else
/*more program text*/
#endif
13. Note: #elif is available only with the ANSI C preprocessor.
#if A>47 // compiled if A is greater than 47
#else
#if A < 20 // compiled if A is less than 20
#else // compiled if A is greater than or equal
// to 20 and less than or equal to 47
#endif // end of if, A is less than 20
#endif // end of if, A is greater than 47
Any undefined preprocessor symbol used in the #if expression is treated as if it
has the value 0.
Predefined Macros:
Macro Description
__DATE__ The compilation date of the current source file. The date is a string
literal of the form Mmm dd yyyy. The month name Mmm is the same as
for dates generated by the library function asctime declared in TIME.H.
__FILE__ The name of the current source file. __FILE__ expands to a string
surrounded by double quotation marks. To ensure that the full path to
the file is displayed, use /FC (Full Path of Source Code File in
Diagnostics).
__LINE__ The line number in the current source file. The line number is a decimal
integer constant. It can be changed with a #line directive.
__STDC__ Indicates full conformance with the ANSI C standard. Defined as the
integer constant 1 only if the /Za compiler option is given and you are
not compiling C++ code; otherwise is undefined.
__TIME__ The most recent compilation time of the current source file. The time is
a string literal of the form hh:mm:ss.
__TIMESTAMP__ The date and time of the last modification of the current source file,
expressed as a string literal in the form Ddd Mmm Date hh:mm:ss yyyy,
where Ddd is the abbreviated day of the week and Date is an integer
from 1 to 31.
Code:
#include<stdio.h>
int main()
{
printf("File :%stLine %dn",__FILE__,__LINE__);
14. printf("Date: %stTime: %sn",__DATE__,__TIME__);
return 1;
}
Output:
File :C:Documents and SettingsS.SrikrishnanDesktoptest.c
Line 4
Date: Sep 10 2010 Time: 15:09:28
REFERENCES:
Leland L. Beck, “System Software – An Introduction to Systems Programming”, 3rd
Edition, Pearson Education Asia, 2006.
Henry Mullish, Herbert L. Cooper, “The Spirit of „C‟ – An Introduction to Modern
Programming”, Jaico Publishing House.