问题
Let's assume we have the following code in a language that looks a lot like C.
int A[2];
A[0]=4;
A[1]=7;
void f(int x, int y) {
x++; A[1]++; y++;
printf(x, y, A[0], A[1]);
}
void main() {
int k = 0;
f(k, A[k]);
print(k, A[0], A[1]);
}
I want to define the output of this program. I haven't understood well the difference between the call-by-name and the call-by-macro-expansion method.
So, in the call-by-name method, k is initialized to 0 and then f()
function is called. x becomes equal to "k" and y becomes equal to "A[k]". The first command in the called function is x++
which increases the value of "k" by 1. So k becomes equal to 1. Then A[1] is increased, so A[1] becomes 7+1=8. None of the x,y are affected. Finally, we have the command y++
which increases the value of "A[k]" by 1, so it increases the value of A[1] (since now k=1) by 1, so A[1] becomes now 8+1=9.
Then f()
prints: 1,9,4,9
And then we return to the main()
fuction which prints: 1,4,9
So, the output of the program is 1,9,4,9,1,4,9 if I am not mistaken.
But how does call-by-macro-expansion differs from this method? What does it change?
回答1:
There is nothing like "call-by-macro" in C language. There are only macros which take parameters. Macros are just textually replaced by the preprocessed tokens.
IMO macros should be used only if they are really needed, in most cases it better to use inline functions. Macros are dificult to debus (as compiler compiles the preprocessed .c file) and error prone.
Example
#define SUB(a,b) a-b
and the usage
printf("%d", SUB(3-2,4-5));
the result will not be 2 only -8
回答2:
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 useforever 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).
来源:https://stackoverflow.com/questions/56716884/difference-between-call-by-name-and-call-by-macro-expansion