The C Preprocessor (and more)! Peter Kristensen 2012-11-19 Peter Kristensen The C Preprocessor (and more)!
Outline 1 C Pre Processor Compiler Assembler Linker Frontend 2 Simple directives Headers Macros 3 4 Trigraphs Digraphs Peter Kristensen The C Preprocessor (and more)!
Overview 1 C Pre Processor Compiler Assembler Linker Frontend 2 Simple directives Headers Macros 3 4 Trigraphs Digraphs C Pre Processor Compiler Assembler Linker Frontend 3 / 30
cpp (gcc -E) C Pre Processor Compiler Assembler Linker Frontend cpp is not to be confused with c++ is run before the compiler handles lines starting with # include files expand macros remove comments... and more 4 / 30
cc1 C Pre Processor Compiler Assembler Linker Frontend The real C compiler Almost never invoked manually Only understands preprocessed C Creates assembler code 5 / 30
as C Pre Processor Compiler Assembler Linker Frontend Complies assembler to native code Creates object files Inserts placeholders for external code 6 / 30
ld C Pre Processor Compiler Assembler Linker Frontend Combine object files Replaces placeholders with address of code in other object files Creates an executable If all symbols are well-defined 7 / 30
gcc C Pre Processor Compiler Assembler Linker Frontend gcc - the project - is a collection of tools cpp, cc1, as, ld, and more gcc - the command - is a frontend for these tools gcc -v shows how it invokes the different tools 8 / 30
Overview 1 C Pre Processor Compiler Assembler Linker Frontend 2 Simple directives Headers Macros 3 4 Trigraphs Digraphs Simple directives Headers Macros 9 / 30
Simple directives Simple directives Headers Macros #define / #undef Used to define stuff #define DEBUG 1 debug will now be replaced with 1 Can be defined using arguments to gcc gcc -DDEBUG=1 #if / #else / #endif Can be used to conditionally add / remove code #if DEBUG printf("doing stuff\n"); #endif 10 / 30
Header files Simple directives Headers Macros #include inserts the content of any file By convention:.h files declares functions.c files defines functions A declaration is done using a function prototype int add(int a, int b); The prototype enabled the compiler to issue warnings and errors. e.g. add(1,2,3) doesn t match the prototype, so it s illegal. error: too many arguments to function add Without the prototype, the call would be a warning instead of an error (-Wall) 11 / 30
Simple directives Headers Macros Macros Macros are based on the #define syntax A macro is a meta function, executed by the preprocessor at compile time With great power comes great responsibility 12 / 30
Some Macros Simple directives Headers Macros Conditional debugging #if DO_DEBUG #define DEBUG(x) printf("%s\n",x) #else #define DEBUG(x) #endif If DO_DEBUG is defined, DEBUG(x) will print a debug string, otherwise nothing SQUARE(x) x*x Lets the compiler calculate constants for us, saving CPU at runtime! 13 / 30
Simple directives Headers Macros Precedence Rules Macros are expanded by the preprocessor, so they have the highest precedence SQUARE(5) = 5*5 = 25 SQUARE(1+2) = 1+2*1+2 = 5!= 9 Can be omitted using parentheses #define SQUARE(x) (x)*(x) SQUARE(1+2) = (1+2)*(1+2) = 9 14 / 30
Simple directives Headers Macros Multi lines macros #define HELPER(x) \ help1(x); \ help2(x) Call with HELPER(1); Can go very wrong! if (0) HELPER(42); This will call help2 because the first ; ends the if. 15 / 30
Safe macros Simple directives Headers Macros #define HELPER(x) \ do { \ help1(x); \ help2(x); \ } while (0) Wrap the macro in a do while to get a block around it Better solution for modern compilers: inline inline void HELPER(int x) { help1(x); help2(x); } By using inline the compiler knows it should try to optimize the function away 16 / 30
Predefined macros Simple directives Headers Macros FILE, LINE, func E.g. #define DEBUG() \ printf("%s:%d @ %s\n", FILE, \ LINE, func ) Will print the file, the line and the function where the DEBUG() call was made DATE, TIME When the file was compiled GNUC the major version of gcc (if gcc is used) linux, APPLE What OS More: echo gcc -dm -E - 17 / 30
Textify and Concat Simple directives Headers Macros A single # converts a argument to a string #define TEST(x) printf(#x) TEST(hest); Becomes printf("hest"); A double ## will concatenate a argument with text #define TEST(x) int test_##x = 42 TEST(hest); Becomes test_hest = 42; 18 / 30
Simple directives Headers Macros X-Macro Keep a list of definitions and use it multiple times Each line in the definition file calls a macro with some arguments Before including the definitions file, define a macro to use the definitions Can be done multiple times by redefining the macro and including the file again 19 / 30
X-Macro Sample Simple directives Headers Macros xm.def CASE(test, 42) CASE(hest, 12) c source #define CASE(x,y) printf("%s = %d\n",#x,y); #include "xm.def" This will expand to printf("%s = %d\n","test",42); printf("%s = %d\n","hest",12); 20 / 30
Overview 1 C Pre Processor Compiler Assembler Linker Frontend 2 Simple directives Headers Macros 3 4 Trigraphs Digraphs 21 / 30
Often needed for callbacks int pthread_create(pthread_t * restrict, const pthread_attr_t * restrict, void *(*)(void *), void * restrict); WTF? void *(*)(void *) 22 / 30
Pointer summary int a; int variable 23 / 30
Pointer summary int a; int variable int* b; pointer to int variable 23 / 30
Pointer summary int a; int variable int* b; pointer to int variable int *b; whitespace are ignored 23 / 30
Pointer summary int a; int variable int* b; pointer to int variable int *b; whitespace are ignored int** c; pointer to pointer to int variable 23 / 30
Pointer summary int a; int variable int* b; pointer to int variable int *b; whitespace are ignored int** c; pointer to pointer to int variable int **c; or. int* *c; still ignoring whitespace 23 / 30
Pointer summary int a; int variable int* b; pointer to int variable int *b; whitespace are ignored int** c; pointer to pointer to int variable int **c; or. int* *c; still ignoring whitespace void* d; pointer to something Need a typecase to work E.g. b = (int*)d; 23 / 30
Back to function pointers int add2(int a) { return a+2; } int (*foo)(int); a function pointer named foo, points to a function taking a int, and returning a int. foo = add2; foo now points to add2 int x = foo(40); is now the same as: int x = add2(40); 24 / 30
How to read (and other C declerations) int (*foo)(int); A variable Start in the middle: foo 25 / 30
How to read (and other C declerations) int (*foo)(int); A variable Start in the middle: foo Read right until the first parentheses: nothing 25 / 30
How to read (and other C declerations) int (*foo)(int); A variable Start in the middle: foo Read right until the first parentheses: nothing Read left until the first parentheses: * pointer 25 / 30
How to read (and other C declerations) int (*foo)(int); A variable Start in the middle: foo Read right until the first parentheses: nothing Read left until the first parentheses: * pointer Read right again: (int) function takeing a integer. 25 / 30
How to read (and other C declerations) int (*foo)(int); A variable Start in the middle: foo Read right until the first parentheses: nothing Read left until the first parentheses: * pointer Read right again: (int) function takeing a integer. Read left again: int returning a integer. 25 / 30
How to read (and other C declerations) int (*foo)(int); A variable Start in the middle: foo Read right until the first parentheses: nothing Read left until the first parentheses: * pointer Read right again: (int) function takeing a integer. Read left again: int returning a integer. concat what you read: foo pointer to function taking a integer and returning a integer. 25 / 30
Lets do that again! void *(*)(void *) A type The middle: nothing (meaning unnamed argument) 26 / 30
Lets do that again! void *(*)(void *) A type The middle: nothing (meaning unnamed argument) Right: nothing 26 / 30
Lets do that again! void *(*)(void *) A type The middle: nothing (meaning unnamed argument) Right: nothing Left: * pointer 26 / 30
Lets do that again! void *(*)(void *) A type The middle: nothing (meaning unnamed argument) Right: nothing Left: * pointer Right: (void *) function taking a void pointer argument 26 / 30
Lets do that again! void *(*)(void *) A type The middle: nothing (meaning unnamed argument) Right: nothing Left: * pointer Right: (void *) function taking a void pointer argument Left: void * returning void* 26 / 30
Lets do that again! void *(*)(void *) A type The middle: nothing (meaning unnamed argument) Right: nothing Left: * pointer Right: (void *) function taking a void pointer argument Left: void * returning void* a pointer to a function which takes one void pointer argument and returns a void pointer. 26 / 30
Lets do that again! void *(*)(void *) A type The middle: nothing (meaning unnamed argument) Right: nothing Left: * pointer Right: (void *) function taking a void pointer argument Left: void * returning void* a pointer to a function which takes one void pointer argument and returns a void pointer. E.g. void* bar(void* hest) {} 26 / 30
Calling pthread_create int pthread_create(pthread_t * restrict, const pthread_attr_t * restrict, void *(*)(void *), void * restrict); Argument 3 is a function pointer to a function taking void* as it s argument void* baz(void* k) { // h e l l o world } int main(...) { pthread_t thread; pthread_create(&thread, NULL, baz, NULL); } Demo! 27 / 30
Overview 1 C Pre Processor Compiler Assembler Linker Frontend 2 Simple directives Headers Macros 3 4 Trigraphs Digraphs Trigraphs Digraphs 28 / 30
Trigraphs Trigraphs Digraphs Trigraphs: When times began, 7-bit characters was enough for everybody... except outside The U.S. Some keyboards have local characters instead of special characters. Therefore, ANSI created a way to express these characters using the standard characters Trigraph Equivalent??= #??/????( [??) ]??!??< {??> }??- 29 / 30
Digraphs Trigraphs Digraphs Trigraphs was too weird Digraphs was introduced Only 2 characters for most graphs Digraphs and Trigraphs are mostly useless Unless you have a weird keyboard Cheap obfuscation! Digraphs Digraph Equivalent <: [ :> ] <% { %> } %: # 30 / 30