4

以下是由于序列点规则而导致未定义行为的两个常见问题:

a[i] = i++; //has a read and write between sequence points
i = i++;   //2 writes between sequence points

关于序列点,您还遇到过哪些其他事情?

当编译器无法警告我们时,很难找出这些问题。

4

6 回答 6

10

达里奥的例子的一个变体是这样的:

void Foo(shared_ptr<Bar> a, shared_ptr<Bar> b){ ... }

int main() {
  Foo(shared_ptr<Bar>(new Bar), shared_ptr<Bar>(new Bar));
}

这可能会泄漏内存。两个参数的求值之间没有顺序点,因此不仅第二个参数可以在第一个参数之前求值,而且两个 Bar 对象也可以在任何shared_ptr's之前创建

也就是说,而不是被评估为

Bar* b0 = new Bar();
arg0 = shared_ptr<Bar>(b0);
Bar* b1 = new Bar();
arg1 = shared_ptr<Bar>(b1);
Foo(arg0, arg1);

(这将是安全的,因为如果b0成功分配,它会立即包裹在 a 中shared_ptr),它可以被评估为:

Bar* b0 = new Bar();
Bar* b1 = new Bar();
arg0 = shared_ptr<Bar>(b0);
arg1 = shared_ptr<Bar>(b1);
Foo(arg0, arg1);

这意味着如果b0成功分配并b1抛出异常,则b0永远不会被删除。

于 2009-05-13T18:21:19.647 回答
6

有一些关于参数列表或添加的执行顺序的模棱两可的情况。

#include <iostream>

using namespace std;

int a() {
    cout << "Eval a" << endl;
    return 1;
}

int b() { 
    cout << "Eval b" << endl;
    return 2;
}

int plus(int x, int y) {
    return x + y;
}

int main() {

    int x = a() + b();
    int res = plus(a(), b());

    return 0;
}

a() 还是 b() 先执行?;-)

于 2009-05-13T18:09:47.027 回答
3

这是 Bjarne Stroustup 使用 c++ 的编程原则和实践中的一个简单规则

“如果您在表达式中更改变量的值。不要在同一个表达式中读取或写入两次”

a[i] = i++; //i's value is changed once but read twice
i = i++;   //i's value is changed once but written twice
于 2009-05-14T16:56:56.130 回答
1

一个类似于达里奥的例子,我也看到人们陷入:

printf("%s %s\n", inet_ntoa(&addr1), inet_ntoa(&addr2));

这不仅会打印“ addr1 addr1”或“ addr2 addr2”(因为inet_ntoa返回指向被进一步调用覆盖的静态缓冲区的指针),而且还没有定义哪种情况会出现(因为 C 没有在参数中指定评估顺序列表)。

于 2009-05-13T18:47:19.197 回答
1

以下是适用于大多数 C 编译器的两个很好的表达式,但由于序列点而含糊不清:

x ^= y ^= x ^= y; // in-place swap of two variables

并且

int i=0;
printf("%d %d %d", ++i, ++i, ++i);  // usually prints out 3 2 1... but not for all compilers!
于 2009-05-14T17:37:51.977 回答
1

我最近看到的一个是由于程序员希望节省类格式化时间,完全错了:


class A
{
public:

    ...

    const char* Format( const string& f ) const
    {
        fmt = Print( f, value );
        return fmt.c_str();
    }

    operator const char* () const { return fmt.c_str(); }

private:

    struct timeval value;
    mutable string fmt;
};

A a( ... );
printf( "%s %s\n", a.Format( x ), a.Format( y );

最后一行要么总是为两种格式打印相同的值(要么使程序崩溃,因为内部字符串会释放返回的内存)。

另一个来自我很久以前的一次采访:


void func( int x, int y, int z )
{
    printf( "%d %d %d\n", x, y, z );
}

...
int i = 0;
func( i, ++i, i++ ); /* don't do this in real software :) */

于 2009-05-14T18:30:34.393 回答