C preprocessor: Difference between revisions

1,994 bytes removed ,  14 years ago
[unchecked revision][unchecked revision]
(See discussion - deserves the disputed tag)
Line 25:
{{stub}}
=== 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">
#include <assert.h>
#define assert(condition) do { if (!(condition)) { complain("assertion fail: " #condition); panic(); } } while(0)
 
assert( sizeof(struct free_memory_block) == 8 );
assert( 1 != 2 );
assert( gdt_ptr != null );
</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>
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">
#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>
&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">
#if 0
/* disabled code */
print("memory state: ");
...
print(mem->state);
print("\nallocated blocks: ");
print(mem->allocs);
#endif
</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 ===
448

edits