【问题标题】:C order of evaluation: function designator vs actual argumentsC 求值顺序:函数指示符与实际参数
【发布时间】:2022-01-21 10:29:57
【问题描述】:

众所周知,实际参数的计算顺序因一个 C 编译器而异。但正如 ISO 9899:1999 在 §6.5.2.2.10 中所述:

未指定函数指示符实际参数和实际参数中的子表达式的计算顺序,但在实际调用之前有一个顺序点。

自从我在 80 年代开始使用 C 语言以来,我从来没有遇到过这样的编译器,它生成的代码是在实际参数之后评估函数指示符或与实际参数交错。因此在应用程序中使用以下(简化的)代码是否“安全”:

void* self;
(self = getPointerToObject())->classPtr->methodX(self);

或者真的有必要这样做:

void* self;
int (*methodPtr)(void*);
(methodPtr = (self = getPointerToObject())->classPtr->methodX, methodPtr(self));

在函数指示符和参数评估之间获得一个明确的序列点(以一定的性能为代价)?

是否有 C 编译器会在第一个代码被剪断时生成代码不起作用(即,将未定义的 self 参数提供给 methodX)?

【问题讨论】:

  • 评论不用于扩展讨论;这个对话是moved to chat
  • @Machavity 我必须做什么才能看到整个讨论?移动后,部分讨论似乎丢失了。
  • 一些 cmets 因不具建设性而被移除。如果您觉得有问题,请在帖子中提出版主标记。
  • 关于“我从来没有遇到过这样的编译器……”:你怎么知道的?假设你有编译器 X,并且你用它编译了程序 P0、P1、P2、... Pn,并且在这些程序中,X 都没有生成在相关函数指示符之前评估函数参数的代码。这不会让您知道 X 不会这样做,因为 X 可能会为某些未经测试的程序 P973 这样做......
  • ... 例如,也许对于一些足够复杂的函数指示符表达式,其中存在与参数基本相同或重叠的子表达式,优化将在完成其余复杂函数指示符表达式之前完成参数评估.您无法知道仅通过测试不会发生这种情况,因此需要编译器文档或检查源代码或其他文档。是你做的吗?如果不是,你怎么知道?

标签: c sequence-points


【解决方案1】:

C99 定义了以下序列点:

  • 在计算参数后调用函数 (6.5.2.2)。
  • 以下运算符的第一个操作数的结尾:逻辑与 && (6.5.13); 逻辑或 || (6.5.14);有条件的? (6.5.15);逗号 , (6.5.17)。
  • 完整声明符的结尾:声明符 (6.7.5);
  • 完整表达式的结尾:初始化程序 (6.7.8);表达式中的表达式 声明(6.8.3);选择语句的控制表达式(if 或 switch) (6.8.4); while 或 do 语句的控制表达式 (6.8.5);每个 for 语句的表达式 (6.8.5.3); return 语句中的表达式 (6.8.6.4)。
  • 就在库函数返回之前 (7.1.4)。
  • 在与每个格式化输入/输出函数转换相关的动作之后 说明符(7.19.6、7.24.2)。
  • 在每次调用比较函数之前和之后,以及 也在对比较函数的任何调用和对象的任何移动之间 作为参数传递给该调用 (7.20.5)。

结论:不安全。它在您测试时偶然运行的事实并不意味着所有编译器都将始终如此。

【讨论】:

  • 这不是问题的答案;它只是重复标准所说的内容。
  • @Rochus 这不是问题的答案;它只是重复了标准所说的内容。 Huh!?!?!如果引用 C 标准的引文回答了有关 C 编程语言的问题,则没有更权威的 answer。这样的引用的答案。
  • @Andrew Henle 如您所见,我自己对标准很熟悉。问题不在于标准里面有什么,而是标准如何在实际编译器中应用。 C11 标准中关于同一主题的措辞引发了更多此类问题。
  • @Andrew Henle 显然;你还没有回答这个问题;无论如何,感谢您的宝贵时间。
  • 这个答案使用了附件 C 中的非规范性文本,因此它不具有权威性,并且它没有说明用预期的前提(编译器将评估函数指示符)补充标准的后果首先)将是。
猜你喜欢
  • 2019-10-10
  • 2011-02-25
  • 1970-01-01
相关资源
最近更新 更多