名称呼叫与宏扩展呼叫之间的区别 [英] Difference between Call-by-name and Call-by-macro-expansion
问题描述
让我们假设我们以类似于C的语言编写以下代码.
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.
因此,在按名称调用方法中,将k初始化为0,然后调用f()
函数. x等于"k",y等于"A [k]".被调用函数中的第一个命令是x++
,它将"k"的值增加1.因此k等于1.然后增加A [1],因此A [1]变为7 + 1 = 8. x,y均不受影响.最后,我们有命令y++
将"A [k]"的值增加1,因此将A [1]的值(因为现在k = 1)增加1,所以A [1]变为现在8 + 1 = 9.
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.
然后f()
打印:1,9,4,9
Then f()
prints: 1,9,4,9
然后返回到main()
函数,该函数显示:1,4,9
And then we return to the main()
fuction which prints: 1,4,9
所以,如果我没记错的话,程序的输出为1,9,4,9,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?
推荐答案
但是宏扩展通话与该方法有何不同?它会发生什么变化?
But how does call-by-macro-expansion differs from this method? What does it change?
对于C,宏扩展调用"不存在.相反,对于宏,预处理器对原始文本进行了美化的剪切和粘贴"操作.
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]);
}
然后,预处理程序会将宏中的文本剪切并粘贴到使用宏的位置,然后将x
和y
替换为您提供的参数,以便(在预处理之后)源代码如下所示:
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]);
}
当然,宏不需要包含有效的代码,甚至根本不需要源代码是C(例如,您可以通过告诉编译器不要"来使用预处理器对汇编语言源代码进行预处理.编译后,只输出经过预处理的文本");并没有理由没有理由不能使用完全不同的预处理器(具有完全不同的功能和/或宏语法)并将结果文本输入C编译器(告诉编译器不要进行预处理,只进行编译").
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:
- 对于宏,没有类型检查参数,因此错误最终变得更烦人查找
- 对于宏,调试器仅会说出宏的扩展行号,而不会说代码的实际来源,因此最终导致发现bug更加烦人
- 对于宏,因为它们不一定是有效的C,所以您可以执行一些奇怪的操作(例如
#define forever while(1) {
,因此您可以将forever i++; }
用作无限循环),并且可以对代码进行模糊处理(故意使很难阅读代码. - 对于函数,编译器可以决定不内联函数以减小代码大小
- 对于函数,您可以进行递归操作(对于宏,您不能进行递归操作-最终将产生无数的文本)
- 对于函数,您可以具有函数指针和/或具有外部函数(通过静态链接或动态链接,链接器可在其中指出函数的位置).
- 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).
有关(a)差异的简单示例,请考虑以下代码:
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
}
请注意,这与上面的示例完全相同(对于宏,x++;
修改原始的k
而不是原始k
的副本;对于函数x++;
修改副本,而不是原始副本.
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).
这篇关于名称呼叫与宏扩展呼叫之间的区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!