English 中文(简体)
在C99中,f()+g()是未定义的还是仅未指定的?
原标题:In C99, is f()+g() undefined or merely unspecified?

我曾经认为,在C99中,即使函数fg的副作用受到干扰,尽管表达式f()+g()不包含序列点,但f/code>和g会包含一些,因此行为将是未指定的:要么在g()之前调用f(),要么在f()之前呼叫g()。

我不再那么确定了。如果编译器内联函数(即使函数没有声明为inline,编译器也可能决定这样做),然后重新排序指令,该怎么办?是否可以得到与上述两种不同的结果?换句话说,这是未定义的行为吗?

这并不是因为我打算写这种东西,这是为了在静态分析器中为这样的语句选择最好的标签。

最佳回答

表达式f()+g()至少包含4个序列点;在调用f()之前的一个(在对其所有零个参数求值之后);在调用g()之前的一个(在对其所有零个参数求值之后);一个是对f()的调用返回;一个是对g()的调用返回。此外,与f()相关联的两个序列点出现在与g()相关的两个顺序点之前或之后。你无法判断的是,序列点将按哪个顺序出现——f点出现在g点之前,反之亦然。

即使编译器内联了代码,它也必须遵守“好像”规则——代码的行为必须与函数没有交错时的行为相同。这限制了损坏的范围(假设编译器没有错误)。

因此,对f()g()求值的顺序是未指定的。但其他一切都很干净。


在评论中,超级跑车问道:

我希望源代码中的函数调用保持为序列点,即使编译器自己决定内联它们。对于声明为“内联”的函数,这仍然是真的吗?还是编译器获得了额外的自由度?

我相信,如果规则适用,编译器就不会有多余的余地来省略序列点,因为它使用了显式inline函数。认为(懒得在标准中寻找确切的措辞)的主要原因是允许编译器根据其规则内联或不内联函数,但程序的行为不应改变(性能除外)。

此外,关于(a(),b())+(c(),d())的排序,可以说什么?c()和/或d(?

  • 显然,a在b之前执行,c在d之前执行。我相信c和d有可能在a和b之间执行,尽管编译器不太可能生成这样的代码;类似地,a和b可以在c和d之间执行。尽管我在c和d中使用了和,但这可能是一个或,也就是说,这些操作序列中的任何一个都满足约束:

    • Definitely allowed
    • abcd
    • cdab
    • Possibly allowed (preserves a ≺ b, c ≺ d ordering)
    • acbd
    • acdb
    • cadb
    • cabd

     
    I believe that covers all possible sequences. See also the chat between Jonathan Leffler and AnArrayOfFunctions — the gist is that AnArrayOfFunctions does not think the possibly allowed sequences are allowed at all.

如果这样的事情是可能的,那就意味着内联函数和宏之间有很大的区别。

内联函数和宏之间有很大的区别,但我不认为表达式中的排序是其中之一。也就是说,函数a、b、c或d中的任何一个都可以用宏来代替,并且可以对宏体进行相同的排序。在我看来,主要的区别在于,对于内联函数,在函数调用(如主要答案中所述)以及逗号运算符处都有保证的序列点。使用宏时,会丢失与函数相关的序列点。(所以,也许这是一个显著的区别……)然而,在很多方面,这个问题就像是关于有多少天使可以在别针头上跳舞的问题——在实践中并不重要。如果有人在代码审查中向我提供了(a(),b())+(c(),d())表达式,我会告诉他们重写代码以使其清楚:

a();
c();
x = b() + d();

这假设b()d()没有关键的排序要求。

问题回答

序列点列表见附录C。函数调用(所有正在计算的参数和传递给函数的执行之间的点)是序列点。正如您所说,没有指定先调用哪个函数,但这两个函数中的每一个都会看到另一个的所有副作用,或者根本看不到。

@dmckee公司

好吧,这不适合发表评论,但事情是这样的:

首先,编写一个正确的静态分析器。在这种情况下,“正确”意味着,如果分析的代码有任何可疑之处,它不会保持沉默,所以在这个阶段,你可以愉快地将未定义和未指定的行为混为一谈。它们在关键代码中都是糟糕的和不可接受的,你正确地警告了它们。

但你只想对一个可能的错误发出一次警告,而且你知道,与其他可能不正确的分析器相比,你的分析器将在基准测试中根据“精度”和“召回率”进行判断,所以你不能对同一个问题发出两次警告。。。无论是真警报还是假警报(你不知道是哪一个。你永远不知道是哪个,否则太容易了)。

所以你想发出一个警告

*p = x;
y = *p;

因为只要<code>p</code>在第一条语句中是有效指针,就可以假定它在第二条语句中为有效指针。不推断这一点会降低你在精度指标上的得分。

因此,只要您在上面的代码中第一次警告了p,您就可以教您的分析器假设它是一个有效的指针,这样您就不会在第二次警告它了。更一般地说,您学会忽略与您已经警告过的内容相对应的值(和执行路径)。

然后,你会意识到没有多少人在写关键代码,所以你会根据最初正确的分析结果,对其他人进行其他轻量级的分析。比如说,一个C程序切片器。

你告诉“他们”:你不必检查第一次分析发出的所有警报(可能是错误的)。只要未触发任何程序,切片程序的行为与原始程序相同。切片器为“定义的”执行路径生成等价于切片标准的程序。

用户愉快地忽略警报并使用切片器。

然后你意识到也许存在误解。例如,memmove的大多数实现(您知道,处理重叠块的实现)在使用不指向同一块的指针(比较不指向同一块的地址)进行调用时,实际上会调用未指定的行为。分析器会忽略这两个执行路径,因为这两个路径都是未指定的,但实际上这两个运行路径是等效的,一切都很好。

因此,不应该对警报的含义有任何误解,如果人们打算忽略它们,只有明确无误的未定义行为才应该被排除在外。

这就是你最终对区分未指明的行为和未定义的行为产生强烈兴趣的原因。没有人可以责怪你忽视了后者。但是程序员会不假思索地编写前者,当你说你的切片器排除了程序的“错误行为”时,他们不会有他们所关心的感觉。

这是一个绝对不适合发表评论的故事的结尾。向那些读到这么远的人道歉。





相关问题
Fastest method for running a binary search on a file in C?

For example, let s say I want to find a particular word or number in a file. The contents are in sorted order (obviously). Since I want to run a binary search on the file, it seems like a real waste ...

Print possible strings created from a Number

Given a 10 digit Telephone Number, we have to print all possible strings created from that. The mapping of the numbers is the one as exactly on a phone s keypad. i.e. for 1,0-> No Letter for 2->...

Tips for debugging a made-for-linux application on windows?

I m trying to find the source of a bug I have found in an open-source application. I have managed to get a build up and running on my Windows machine, but I m having trouble finding the spot in the ...

Trying to split by two delimiters and it doesn t work - C

I wrote below code to readin line by line from stdin ex. city=Boston;city=New York;city=Chicago and then split each line by ; delimiter and print each record. Then in yet another loop I try to ...

Good, free, easy-to-use C graphics libraries? [closed]

I was wondering if there were any good free graphics libraries for C that are easy to use? It s for plotting 2d and 3d graphs and then saving to a file. It s on a Linux system and there s no gnuplot ...

Encoding, decoding an integer to a char array

Please note that this is not homework and i did search before starting this new thread. I got Store an int in a char array? I was looking for an answer but didn t get any satisfactory answer in the ...