【问题标题】:Scope rules for C macrosC 宏的作用域规则
【发布时间】:2012-07-18 03:12:58
【问题描述】:

我不是一个 C 程序员,但我假设 C 宏几乎是一种查找和替换功能,预处理器获取宏定义并将其放在它看到宏名称的任何位置。

这是 Dragon Book 的动态范围规则示例以及它们如何应用于宏:

#define a (x + 1)

int x = 2;
void b () { int x = 1; printf("%d\n", a); }
void c () { printf("%d\n", a); }
void main () { b(); c(); }

他们还讨论了动态范围规则如何应用于宏 a 中的名称 x。我假设它基本上会用(x + 1) 替换a,然后编译程序,因此范围规则与您编写(x + 1) 而不是a 完全相同(这将是静态的范围规则)。

谁能澄清一下?

编辑:参考书是编译器:原理、技术和工具第二版。引用的示例来自第 31-32 页。

【问题讨论】:

    标签: c macros


    【解决方案1】:

    您对#define 行为的理解是正确的。

    我认为这本书所说的“动态范围”是指名称 x 是根据调用宏的环境而不是定义它的环境来解析的。因此,如果您在#define 之前设置了一个全局变量 x=3,这与 #define 中 x 的值无关 - 无论您在哪里使用宏,它都会使用 x 的值 - 如果还有其他一些本地使用宏的函数中的变量 x,则将使用本地值。

    这与词法范围(C 语言和几乎所有现代语言中实际使用的)形成对比,其中名称指的是其本地词法环境。例如,如果您将示例中的#define 替换为简单语句a = x+1,那么函数中a 的值将比代码中a = x+1 出现处的x 值大一。如果在您使用值 a 的位置碰巧存在其他名为 x 的局部变量,则无关紧要。类似地,如果您定义了一个函数int f() { return x + 1; },x 将引用全局变量 x,而不是其他一些名为 x 的局部变量,该变量恰好存在于调用 f() 的位置。如果这看起来非常明显,那是因为,正如我所说,几乎所有语言都使用词法作用域(尽管 Perl,例如,也允许使用 local 函数的动态作用域)。

    请参阅http://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping_and_dynamic_scoping 了解更多有关该概念的说明。

    【讨论】:

    • 我想我理解什么是动态作用域与词法作用域 - 我主要是困惑为什么他们使用 c 宏来解释它,因为当程序实际被编译时,宏早已不复存在,并且语句 x+1 就位。不过,这是一个很好的解释。
    • 我同意,我从未真正听说过应用于宏的术语动态范围。但我想这个想法是,从概念上讲,预处理步骤是一个实现细节 - 预处理器使您能够创建动态范围名称的事实是预处理器(部分)真正强大的地方。
    • 没错,我想我只是按字面意思举了这个例子。只要我没有遗漏一些奇怪的细节。谢谢!
    • @user12345613 他们使用 C 宏来解释它,因为它是用熟悉的语言进行动态作用域的简单而明显的示例。而且您正在对 C 编译器的工作方式做出不一定正确且不相关的假设。许多 C 编译器不会在整个源代码中进行宏替换然后编译它,它们会在遇到宏时扩展宏并按需将它们传递给语法解析器。它的编译方式不会影响 x 的抽象,它们正在动态范围内寻址。
    【解决方案2】:

    您的理解是正确的:找出标识符a 的所有用法,并将a 替换为(x + 1)。这正是预处理器所做的。

    关于类对象宏(如a),我们可以讨论的唯一“范围”是宏本身的范围:宏的范围来自行在其上定义它(使用#define 指令)直到它未定义的行(使用#undef 指令)或直到翻译单元的末尾(.cpp 及其包含的所有标题),如果它从未未定义。

    【讨论】:

    • 我很困惑为什么要讨论动态范围规则以及它们如何应用于 x...
    • 此答案未能解决有关澄清 Aho et 的问题。 al.的例子,错过了它的全部意义。请参阅happydave 的回复或我的简洁回答。
    【解决方案3】:

    他们的观点是 a 所指的 x 在 b() 中是局部的,但在 c() 中是全局的——它是动态范围的。 #define 可以解释为“在评估 a 时使用动态范围解析 x”。

    【讨论】:

    • 但这确实是我的困惑所在,因为如果预处理器将 a 替换为 x + 1,则无法解析 x。对吗?
    • 你正在犯一个基本的程序员错误,“大脑上的实现”。你应该更抽象地思考。想象一下 C 的一个子集,其中没有宏的文本替换,但给定的示例是有效的——只有名称可以#defined,并且只能使用格式良好的表达式。 a 的值是根据 x 定义的。哪个x?它是动态范围的。
    • P.S.我认为你可能犯了另一个错误,可能被称为“坚持你的枪”。你说你很困惑,但你似乎在暗示一个人应该感到困惑......那Aho et。 al. 的例子是坏的或错误的。考虑到他们可能比你对这个主题有更好的理解,在这种情况下,你应该更加努力地认为他们的例子是有效的,而不是寻求反对。
    • @Jim 好点。但是,我真的不认为我在“坚持我的枪”。我发了一篇关于它的帖子,因为我真的很困惑。我正在阅读任何编译器书籍的第一章,我想我会尝试理解他们为什么选择这个例子。这里的答案声称我对宏的理解是正确的——这让我对这个例子更加困惑。我现在看到他们不是。我知道这本书的作者知道的比我多,否则我不会为此付钱。我明白你关于实施细节无关紧要的观点,我知道他们现在正在做什么。
    • @user12345613 太好了!你在比赛中遥遥领先。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-03-31
    • 2013-12-25
    • 2015-02-06
    • 2018-04-19
    • 1970-01-01
    • 1970-01-01
    • 2012-05-09
    相关资源
    最近更新 更多