But how does call-by-macro-expansion differs from this method? What does it change?
For C, "call-by-macro-expansion" doesn't exist. Instead, for macros the preprocessor does a glorified "cut&paste" operation on raw text.
For example, if you have this:
int A[2];
A[0]=4;
A[1]=7;
#define MACRO(x, y) { \
x++; A[1]++; y++; \
printf(x, y, A[0], A[1]); \
}
void main() {
int k = 0;
MACRO(k, A[k]);
print(k, A[0], A[1]);
}
Then the preprocessor will cut&paste the text from the macro to where the macro is used and then replace x
and y
with the arguments you provided, so that (after preprocessing) the source code looks like this:
int A[2];
A[0]=4;
A[1]=7;
void main() {
int k = 0;
{ \
k++; A[1]++; A[k]++; \
printf(k, A[k], A[0], A[1]); \
}
print(k, A[0], A[1]);
}
Of course the macros don't need to contain valid code, and the source doesn't even need to be C at all (e.g. you could use the preprocessor to preprocess assembly language source code by telling the compiler "don't compile, just output the preprocessed text"); and there's no real reason why you can't use a completely different preprocessor (with completely different features and/or macro syntax) and feed the resulting text into a C compiler (telling the compiler "don't preprocesses, just compile").
In practice; the main differences for macros and functions are:
- for macros, there's no type-checking on the parameters, so bugs end up being more annoying to find
- for macros, a debugger will only say the line number where the macro was expanded and won't say where the code actually came from, so bugs end up being more annoying to find
- for macros, because they don't have to be valid C you can do some bizarre shenanigans (e.g.
#define forever while(1) {
so you can use forever i++; }
as an infinite loop), and can be powerful for code obfuscation (deliberately making it hard to read the code).
- for functions, the compiler can decide not to inline the function to reduce code size
- for functions, you can have recursion (with macros you can't - it'd end up being an infinite amount of text)
- for functions, you can have function pointers and/or have external functions (where the linker figures out where the function is, either with static linking or dynamic linking).
For a simpler example of (a) difference, consider this code:
#define f(x) { \
x++; \
}
void g(int x) {
x++;
}
void main() {
int a = 1;
int b = 1;
f(a);
printf("%d\n", a);
g(b);
printf("%d\n", b);
}
These look the same, but are not. After expanding the macro and inlining the function, it becomes more like this:
void main() {
int a = 1;
int b = 1;
a++;
printf("%d\n", a); // Will print "2' because the original `a` was changed
int x = b;
x++;
printf("%d\n", b); // Will print "1' because the original `b` was not changed
}
Note that this is exactly the same problem with the example above (for the macro, the x++;
modifies the original k
and not a copy of the original k
; and for the function the x++;
modifies a copy and not the original).
k
has no meaning insidef
. – Callant