什么是"序列点"?
未定义的行为和序列点之间的关系是什么?
我经常使用有趣和复杂的表达方式a[++i] = i;,让自己感觉更好.我为什么要停止使用它们?
如果您已阅读此内容,请务必访问后续问题重新加载未定义的行为和序列点.
(注意:这是Stack Overflow的C++常见问题解答的一个条目.如果你想批评在这种形式下提供常见问题解答的想法,那么发布所有这些的元数据的发布将是这样做的地方.这个问题在C++聊天室中受到监控,其中FAQ的想法一开始就出现了,所以你的答案很可能被那些提出想法的人阅读.)
#include <stdio.h>
int main(void)
{
int i = 0;
i = i++ + ++i;
printf("%d\n", i); // 3
i = 1;
i = (i++);
printf("%d\n", i); // 2 Should be 1, no ?
volatile int u = 0;
u = u++ + ++u;
printf("%d\n", u); // 1
u = 1;
u = (u++);
printf("%d\n", u); // 2 Should also be one, no ?
register int v = 0;
v = v++ + ++v;
printf("%d\n", v); // 3 (Should be the …Run Code Online (Sandbox Code Playgroud) c increment operator-precedence undefined-behavior sequence-points
将此主题视为以下主题的续篇:
上一部分
未定义的行为和序列点
让我们重新审视这个有趣而复杂的表达(斜体短语取自上述主题*smile*):
i += ++i;
Run Code Online (Sandbox Code Playgroud)
我们说这会调用undefined-behavior.我假定说这个的时候,我们隐含假设型的i是内置的类型之一.
如果什么类型的i是用户定义类型?比如它的类型是Index在本文后面定义的(见下文).它还会调用未定义的行为吗?
如果是,为什么?它不等同于写作i.operator+=(i.operator++());甚至语法上更简单 i.add(i.inc());吗?或者,他们是否也调用未定义的行为?
如果不是,为什么不呢?毕竟,对象在连续的序列点之间i被修改两次.请回想一下经验法则:表达式只能在连续的"序列点"之间修改一个对象的值.如果 i += ++i是表达式,那么它必须调用未定义的行为.如果是,那么它的等价物i.operator+=(i.operator++());也 i.add(i.inc());必须调用undefined-behavior似乎是不真实的!(据我所知)
或者,i += ++i不是一个开头的表达?如果是这样,那么它是什么以及表达式的定义是什么?
如果它是一个表达式,并在同一时间,其行为也是定义良好的,那么就意味着与表达相关序列点的数量在某种程度上取决于该类型的参与表达操作数.我是否正确(甚至部分)?
顺便问一下,这个表达怎么样?
//Consider two cases:
//1. If a is an array of a built-in type
//2. If a is user-defined type which overloads the …Run Code Online (Sandbox Code Playgroud) 我偶然发现了这个代码,用于交换两个整数而不使用临时变量或使用按位运算符.
int main(){
int a=2,b=3;
printf("a=%d,b=%d",a,b);
a=(a+b)-(b=a);
printf("\na=%d,b=%d",a,b);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
但我认为这段代码在swap语句中有未定义的行为,a = (a+b) - (b=a);因为它不包含任何序列点来确定评估的顺序.
我的问题是:这是交换两个整数的可接受的解决方案吗?
我以前认为在C99中,即使函数的副作用f和g干扰,虽然表达式f() + g()不包含序列点,f并且g会包含一些,所以行为将是未指定的:要么f()之前调用f()之前的g()或g().
我不再那么肯定了.如果编译器内联函数(即使未声明函数,编译器可能决定这样做inline)然后重新排序指令,该怎么办?可能有人得到上述两种不同的结果吗?换句话说,这是未定义的行为吗?
这不是因为我打算写这种东西,而是在静态分析器中为这样的语句选择最佳标签.
c c99 undefined-behavior sequence-points unspecified-behavior
我一直在寻找更多适合的习语std::exchange。
今天我发现自己在一个答案中写下了这个:
do {
path.push_front(v);
} while (v != std::exchange(v, pmap[v]));
Run Code Online (Sandbox Code Playgroud)
我比说更喜欢它
do {
path.push_front(v);
if (v == pmap[v])
break;
v= pmap[v];
} while (true);
Run Code Online (Sandbox Code Playgroud)
希望有明显的原因。
然而,我对标准语言并不热衷,我不禁担心这并lhs != rhs不能保证右侧表达式在左侧表达式之前没有被完全求值。这将使其成为同义反复的比较 - 根据定义将返回true.
然而,代码确实运行正确,显然lhs首先进行评估。
有人知道吗
附言。f(a,b)我意识到这是where fis的一个特例operator!=。我尝试使用此处找到的信息回答我自己的查询,但迄今为止未能得出结论:
我已经查看了一系列有关序列点的问题,并且无法x*f(x)确定f修改后的评估顺序是否有保证x,这是不同的f(x)*x.
考虑以下代码:
#include <iostream>
int fx(int &x) {
x = x + 1;
return x;
}
int f1(int &x) {
return fx(x)*x; // Line A
}
int f2(int &x) {
return x*fx(x); // Line B
}
int main(void) {
int a = 6, b = 6;
std::cout << f1(a) << " " << f2(b) << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
这打印49 42在g ++ 4.8.4(Ubuntu 14.04)上.
我想知道这是保证行为还是未指明.
具体来说,在这个程序中,fx两次x=6都被调用两次,并且两次都返回7次.所不同的是线A计算7×7(取的值x之后 …
在下面的代码中,在set()a 上调用一个成员函数model,它是一个空指针。这将是未定义的行为。然而,成员函数的参数是另一个函数调用的结果,该函数调用检查是否为model空指针并在这种情况下抛出异常。是否保证estimate()总是在访问之前被调用model,还是仍然是未定义行为(UB)?
#include <iostream>
#include <memory>
#include <vector>
struct Model
{
void set(int x)
{
v.resize(x);
}
std::vector<double> v;
};
int estimate(std::shared_ptr<Model> m)
{
return m ? 3 : throw std::runtime_error("Model is not set");
}
int main()
{
try
{
std::shared_ptr<Model> model; // null pointer here
model->set(estimate(model));
}
catch (const std::runtime_error& e)
{
std::cout << e.what();
}
return 0;
}
Run Code Online (Sandbox Code Playgroud) C 中的定义是否int a = 0, b = a++, c = a++;定义了行为?
或者几乎等价地,对象定义中的逗号是否像表达式中的逗号运算符一样引入序列点?
对于 C++ 也提出了类似的问题:
C++ 被广泛接受的答案是“是”,它在 C++11 标准第 8/3 段中有完整定义:
声明中的每个初始化声明符都会被单独分析,就像它本身在声明中一样
尽管本段仅涉及语法分析阶段,对于运行时的操作顺序还不够精确。
C语言的情况如何?C 标准是否定义了行为?
之前也有人问过类似的问题:
然而,答案似乎专门针对 C11 草案,并且可能不适用于 C 标准的最新版本,因为自 C11 草案以来,信息性附录 C 的措辞已发生变化,并且似乎也不与标准文本完全一致。
编辑:当然这样的初始化器似乎毫无用处地扭曲。我绝对不会容忍这样的编程风格和结构。这个问题源于关于一个琐碎定义的讨论:int res = 0, a = res;行为似乎没有完全定义(!)。具有副作用的初始化器并不少见,例如考虑以下一个:int arg1 = pop(), arg2 = pop();
命令式编程中的序列点定义了计算机程序执行中的任何点,在该点处保证先前评估的所有副作用都已执行,并且尚未执行后续评估的副作用.
这是什么意思?有人可以用简单的语言解释一下吗?