名称呼叫与宏扩展呼叫之间的区别 [英] Difference between Call-by-name and Call-by-macro-expansion

查看:135
本文介绍了名称呼叫与宏扩展呼叫之间的区别的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

让我们假设我们以类似于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]);
}

然后,预处理程序会将宏中的文本剪切并粘贴到使用宏的位置,然后将xy替换为您提供的参数,以便(在预处理之后)源代码如下所示:

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 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).

有关(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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆