【问题标题】:What's the consequence of a sequence-point "immediately before a library function returns"?“在库函数返回之前”序列点的后果是什么?
【发布时间】:2018-01-30 13:25:25
【问题描述】:

在this recent question 中,显示某些代码具有未定义的行为:

a[++i] = foo(a[i-1], a[i]);

因为即使foo() 的实际调用是序列点,赋值是未序列化 ,所以你不知道该函数是在++i的副作用发生之后还是之前调用的。

进一步考虑这一点,函数调用处的序列点仅保证在输入函数后执行评估函数参数的副作用,例如

int y = 1;
int func1(int x) { return x + y; }
int main(void)
{
    int result = func1( y++ ); // guaranteed to be 3
}

但是看看标准,还有§7.1.4 p3(在关于标准库的章节中):

在库函数返回之前有一个序列点。

我的问题是:这一段的结果是什么?为什么它只涉及库函数,什么样的代码实际上会依赖它?

简单的想法,例如(遵循无意义的代码)

errno = 0;
long result = ftell(file) * errno;

仍然是未定义的,因为这一次,乘法是未排序的。我正在寻找一个利用 §7.1.4 p3 为库函数制作的特殊保证的示例。


关于建议的重复,Sequence point after a return statement?,这确实是密切相关的,我在问这个问题之前发现了它。这不是重复的,因为

  • 它询问规范性文本,说明是紧跟在return 之后的序列点,而不询问有序列点时的后果。
  • 它只提到了库函数的特殊规则这个问题是关于,没有进一步详细说明。

因此,我在这里的问题在那里没有得到回答。接受的答案在未排序的表达式中使用返回值(在本例中为 addition)并解释结果如何取决于此加法的顺序,仅发现 如果您知道 加法的顺序,整个结果将在return 之后立即定义一个顺序点。它没有显示由于该规则而实际定义的代码示例,也没有说明库函数如何/为什么特别。

【问题讨论】:

  • “标准免责声明”:如果您因为我错过了一些“显而易见的”而投反对票,请发表评论指出,谢谢。
  • @MichaelFoukarakis 我检查了那个,它密切相关,但没有回答 this 问题。虽然这里提到了特殊的库函数规则,但没有任何代码示例该规则实际上有任何效果。
  • 该语句的实际使用是在程序中使用多线程时。
  • @hacks 我猜肯定还有别的原因,因为在 C99 中已经找到了相同的段落。

标签: c language-lawyer sequence-points


【解决方案1】:

库函数没有标准涵盖的实现它们的代码(它们甚至可能没有在 C 中实现)。该标准仅规定了它们的行为。所以关于return语句的规定不适用于库函数的实现。

这个子句的目的(结合库函数入口处的序列点)是说库函数的任何副作用在可能在任何其他评估之前或之后排序调用库函数的代码。

因此,您问题中的示例不是未定义的行为(除非乘法溢出!):errno 的读取在ftell 修改之前或之后排序,未指定哪个。

【讨论】:

  • 啊,“未定义”和“未指定”之间的细微差别......我想我倾向于使用它们太草率了。到目前为止感谢! (您的示例不是为完全不同的东西定义的:签名溢出吗?)
  • @FelixPalmen 我改变了我的答案以使用你的例子,在我原来的例子中,我认为没有签名溢出,但如果有的话,它会偏离我的观点,所以你的例子还是更好
  • 嗯,有,strtol 会返回 LONG_MAX 和 ERANGE 是肯定的 :) 至少,你的第一段非常合乎逻辑地解释了为什么库函数在这里是“特殊的”,谢谢和紫外线:)
猜你喜欢
  • 1970-01-01
  • 2021-05-24
  • 1970-01-01
  • 2021-08-15
  • 2019-05-14
  • 2022-01-26
  • 1970-01-01
  • 1970-01-01
  • 2022-12-31
相关资源
最近更新 更多