以下是由于序列点规则而导致未定义行为的两个常见问题:
a[i] = i++; //has a read and write between sequence points
i = i++; //2 writes between sequence points
关于序列点,您还遇到过哪些其他事情?
当编译器无法警告我们时,很难找出这些问题。
以下是由于序列点规则而导致未定义行为的两个常见问题:
a[i] = i++; //has a read and write between sequence points
i = i++; //2 writes between sequence points
关于序列点,您还遇到过哪些其他事情?
当编译器无法警告我们时,很难找出这些问题。
达里奥的例子的一个变体是这样的:
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
永远不会被删除。
有一些关于参数列表或添加的执行顺序的模棱两可的情况。
#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() 先执行?;-)
这是 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
一个类似于达里奥的例子,我也看到人们陷入:
printf("%s %s\n", inet_ntoa(&addr1), inet_ntoa(&addr2));
这不仅会打印“ addr1 addr1
”或“ addr2 addr2
”(因为inet_ntoa
返回指向被进一步调用覆盖的静态缓冲区的指针),而且还没有定义哪种情况会出现(因为 C 没有在参数中指定评估顺序列表)。
以下是适用于大多数 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!
我最近看到的一个是由于程序员希望节省类格式化时间,完全错了:
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 :) */