C preprocessor: Difference between revisions

From OSDev.wiki
Jump to navigation Jump to search
[unchecked revision][unchecked revision]
Content added Content deleted
(See discussion - deserves the disputed tag)
Line 25: Line 25:
{{stub}}
{{stub}}
=== Uses ===
=== Uses ===
<source lang="c">
#define MAX(a,b) ((a) > (b) ? (a) : (b))
</source>
This definition has the impact of computing one of a,b twice so the sideeffects would happen twice.
<source lang="c">
#define MAX(a,b) ({typeof(a) _a = (a); \
typeof(b) _b = (b); \
_a > _b ? _a : _b;})
</source>
Using GCC statement expressions and typeof we may avoid this problem.


==== assert() ====

Assertions are used to catch situations which should never happen, even under error circumstances. If the condition given in the parantheses does not evaluate to "true", a diagnosis is printed which contains source file name, line number, and (since C99) name of the current function; the program then calls abort().


{{stub}}
==== Uses for debugging ====
Assertions are used to catch situations which should never happen, even under error circumstances.
<source lang="c">
<source lang="c">
#include <assert.h>
#define assert(condition) do { if (!(condition)) { complain("assertion fail: " #condition); panic(); } } while(0)

assert( sizeof(struct free_memory_block) == 8 );
assert( sizeof(struct free_memory_block) == 8 );
assert( 1 != 2 );
assert( 1 != 2 );
assert( gdt_ptr != null );
assert( gdt_ptr != null );
</source>
</source>
Assertions may be turned off in production code
<source lang="c">
#define assert(x) do {} while(0)
</source>
Some rare unrecoverable errors should be tested for also in production code and these test should not be disabled so that we recognise the problem instead of having random crashes
<source lang="c">
#define testif(condition) do { if (!(condition)) { complain("testif fail: " #condition); panic(); } } while(0)
testif( isChecksumCorrect( kernel_heap.first_free_list ) );
testif( timersAreOn );
</source>
Capturing debugging information like values of variables at different moments of execution
<source lang="c">
void alert(const char *msg);
void alert(uint32 u);
void alert_dec(uint32 u);


For production code, assertions may be turned off by defining NDEBUG:
#define complain(msg) do {\
alert_decimal(__FILE__); \
alert(": "); \
alert_decimal(__LINE__); \
alert(": "); \
alert(msg); \
alert("\n"); \
} while(0)


<source lang="bash">
void * malloc(size_t s) {
gcc -DNDEBUG ...
complain((uint32)kernel_heap.first_free->addr);
do_something();
complain((uint32)kernel_heap.first_free->addr);
if (do_something2()) y = malloc(sizeof(struct book_keeping_struct));
complain((uint32)kernel_heap.first_free->addr);
do_something3();
}
</source>
</source>
with an output of
src/memory/malloc.c: 271: 0xd0010000 //entering malloc
src/memory/malloc.c: 273: 0xd0010000 //having done_something
src/memory/malloc.c: 271: 0xd0010000 //do_something2 makes malloc to recursive call
src/memory/malloc.c: 273: 0xd0010000 //done_something
src/memory/malloc.c: 275: 0x0e1bc30a //aha! an error after do_something2() (which returns 0) in nested malloc call
...


Note that <assert.h> does not have (or need) a header guard, i.e. can be included multiple times in a source file, and that whether NDEBUG is defined or not is evaluated anew ''at every inclusion of <assert.h>''. You can thus enable / disable assertions at a very fine-grained level if necessary:
Finding death point or program flow

SYSFAIL: page fault, %eip= 0xc0001d330, %cr2=0x00000000
<source lang="c">
<source lang="c">
#include <assert.h>
#define lnDbg do { alert("<<"); alert(__func__); alert(" : "); alert_decimal(__LINE__); alert(">>\n"); } while(0)

/* assert() at this point only fails-on-false if NDEBUG is not defined */
assert( isChecksumCorrect() );

#ifdef NDEBUG
/* Hard-enabling of assert() even if NDEBUG is defined */
#define NDEBUG_WAS_SET
#undef NDEBUG
#include <assert.h>
#endif

/* assert() in this block of code should fail-on-false even in production */
assert( isChecksumCorrect() );

#ifdef NDEBUG_WAS_SET
/* Restoring NDEBUG if it was enabled originally */
#define NDEBUG
#include <assert.h>
#endif
</source>
</source>
&nbsp;
<source lang="c" line start="13">
void a(int i) {
lnDbg;
if (fun(i)) c(i) else a(i-1);
lnDbg;
a(i);
}
void c(int i) {
lnDbg;
if (!is(i)) return ;
lnDbg;
c(i+1);
lnDbg;
}
</source>
<<a: 14>>
<<c: 20>> //line 16 wasn't run, fun(i) has returned true
<<a: 16>> //line 22 wasn't run, !is(i) has returned true
<<a: 14>>
SYSFAIL: .... //after line 14 neither line 20 or 16 has been reached so the call fun(i) caused the page fault.


==== Disabled Code ====
Such macros may be stored in an shared.h file included by other compilation units.

{{stub}}
Sometimes you want to disable a section of code. However, if the code already contains /* C-style comments */, using those very same comments is not an option because they do not nest. The preprocessor provides an easy way around:


==== Deleted Code ====
A code block may be commented out to delete it from the program, however nesting deleted fragments may reduce legibility with C++ style comments, and C comments do not nest at all.
A better solution is to wrap the code in and #if 0-#endif block, where the conditional 0 means false :
<source lang="c">
<source lang="c">
#if 0
#if 0
/* disabled code */
print("memory state: ");
...
print(mem->state);
print("\nallocated blocks: ");
print(mem->allocs);
#endif
#endif
</source>
</source>

Many editors, like [http://www.vim.org/ Vim] have by default syntax highlighting rules that treat such #if 0-#endif blocks as comments.
If you want to enable the code section only ''temporarily'', replace the '#if 0' with '#if 1'. Using '#else' it is even possible to have alternative code sections (e.g. for testing).
The #if-#endif directives must be balanced, single-quotes characters must balance etc. so for deleting non-code text use comments instead.


=== Hazards of the C preprocessor ===
=== Hazards of the C preprocessor ===

Revision as of 14:48, 28 June 2010

The factual accuracy of this article is disputed.
Please see the relevant discussion on the talk page.

C preprocessor

The C preprocessor is a powerful tool and properly used may be very useful. The following have been checked to work in GCC.

Rules

#ifdef SOME_MACRO
#define SOME_OTHER_MACRO SOME_VALUE
#undef SOME_MACRO
#endif

#ifndef NDEBUG
/* Older compilers may not have __func__ */
fprintf(stderr, __FILE__ ":" __LINE__ " : in function " __func__ "\n");
#endif

The preprocessor can be used to define macros with the #define macro. A previously defined macro can be undefined with #undef. And #ifdef block can be used to conditionally compile a block of code if a certain macro is defined. This can be useful if the code is platform-dependant, or is used for debugging.

When the preprocessor reads a file, it replaces any occurrence of a defined macro with the value it was defined to. Since the preprocessor is language agnostic, if an error is introduced through its use, the error will not be reported until the file is compiled. This also means that the preprocessor can be used with languages other than C. The standard way to invoke the preprocessor with GCC is "gcc -E".

This page is a stub.
You can help the wiki by accurately adding more contents to it.

Uses

assert()

Assertions are used to catch situations which should never happen, even under error circumstances. If the condition given in the parantheses does not evaluate to "true", a diagnosis is printed which contains source file name, line number, and (since C99) name of the current function; the program then calls abort().

#include <assert.h>

assert( sizeof(struct free_memory_block) == 8 );
assert( 1 != 2 );
assert( gdt_ptr != null );

For production code, assertions may be turned off by defining NDEBUG:

gcc -DNDEBUG ...

Note that <assert.h> does not have (or need) a header guard, i.e. can be included multiple times in a source file, and that whether NDEBUG is defined or not is evaluated anew at every inclusion of <assert.h>. You can thus enable / disable assertions at a very fine-grained level if necessary:

#include <assert.h>

    /* assert() at this point only fails-on-false if NDEBUG is not defined */
    assert( isChecksumCorrect() );

#ifdef NDEBUG
/* Hard-enabling of assert() even if NDEBUG is defined */
#define NDEBUG_WAS_SET
#undef NDEBUG
#include <assert.h>
#endif

    /* assert() in this block of code should fail-on-false even in production */
    assert( isChecksumCorrect() );

#ifdef NDEBUG_WAS_SET
/* Restoring NDEBUG if it was enabled originally */
#define NDEBUG
#include <assert.h>
#endif

Disabled Code

Sometimes you want to disable a section of code. However, if the code already contains /* C-style comments */, using those very same comments is not an option because they do not nest. The preprocessor provides an easy way around:

#if 0
    /* disabled code */
    ...
#endif

If you want to enable the code section only temporarily, replace the '#if 0' with '#if 1'. Using '#else' it is even possible to have alternative code sections (e.g. for testing).

Hazards of the C preprocessor

There is a number of counter-intuitive consequences of macros and macro expanding design. Macro Pitfalls

See also

External Links

The GNU C preprocessor manual: