是(++i)++未定义的行为?在检索增量对象以进行后缀增量操作后,是否可能发生前缀增量的副作用?这对我来说似乎很奇怪.
我的直觉是,这在C++ 03中是未定义的,在C++ 11中定义良好.我对吗?
c++ undefined-behavior sequence-points language-lawyer c++11
我的表达式如下所示: -
while (count)
{
...
...
index = ((count == 20)? 0 : index++);
...
...
}
Run Code Online (Sandbox Code Playgroud)
现在三元运算符是C中的序列点,但我相信序列点在测试部分结束.
这种理解是否正确,因此这种说法会导致未定义的行为?
考虑以下C程序:
int i = 0;
int post_increment_i() { return i++; }
int main() {
i = post_increment_i();
return i;
}
Run Code Online (Sandbox Code Playgroud)
关于2011版C标准(称为C11),以下哪种替代方案是正确的:
C11标准的相关摘要:
5.1.2.3程序执行
访问易失性对象,修改对象,修改文件或调用执行任何这些操作的函数都是副作用,这些都是执行环境状态的变化.表达式的评估通常包括值计算和副作用的开始.左值表达式的值计算包括确定指定对象的身份.
之前排序的是由单个线程执行的评估之间的不对称,传递,成对关系,其在这些评估中引起部分顺序.给定任何两个评估A和B,如果A在B之前被排序,那么A的执行应该在B的执行之前.(相反,如果A在B之前被排序,则B在A之后被排序.)如果A没有排序在B之前或之后,A和B都没有排序.当A在B之前或之后进行测序时,评估A和B是不确定的,但是未指定哪一个.13表示式A的评价与B之间的序列点的存在意味着,在附件中给出与B.(序列点的摘要相关联的每一个值的计算和副作用与前A相关联的每一个值的计算和副作用进行测序C.)
13)未经测试的评估的执行可以交错.不确定顺序的评估不能交错,但可以按任何顺序执行.
6.5表达式
表达式是操作符和操作数的序列,其指定值的计算,或指定对象或函数,或者生成副作用,或执行其组合.在运算符的结果的值计算之前,对运算符的操作数的值计算进行排序.
如果相对于对同一标量对象的不同副作用或使用相同标量对象的值进行值计算,对标量对象的副作用未被排序,则行为未定义.如果表达式的子表达式有多个允许的排序,则如果在任何排序中发生这种未测序的副作用,则行为是不确定的.
6.5.2.2函数调用
在评估函数指示符和实际参数之后但在实际调用之前有一个序列点.调用函数(包括其他函数调用)中的每个评估(在执行被调用函数的主体之前或之后没有特别排序)对于被调用函数的执行是不确定地排序的.94
94)换句话说,函数执行不会相互"交错".
6.5.2.4后缀增量和减量运算符
postfix ++运算符的结果是操作数的值.作为副作用,操作数对象的值递增(即,将相应类型的值1添加到其中).[...]在更新操作数的存储值的副作用之前,对结果的值计算进行排序.对于不确定顺序的函数调用,后缀++的操作是单个评估.
6.5.16分配
赋值运算符将值存储在左操作数指定的对象中.[...]在左右操作数的值计算之后,对更新左操作数的存储值的副作用进行排序.对操作数的评估是不确定的.
6.8声明和块
完整表达式是不属于另一个表达式或声明符的表达式.以下各项都是完整表达式:[...]表达式语句中的表达式; [...] return语句中的(可选)表达式.在完整表达式的评估和要评估的下一个完整表达式的评估之间存在序列点.
上述三种选择分别对应于以下三种情况:
似乎第一种选择通过以下推理链来保持:
考虑规则调用函数中的每个评估(包括其他函数调用)在执行被调用函数体之前或之后没有特别排序,关于被调用函数的执行是不确定的.在6.5.2.2中.假设A:主要的赋值运算符的副作用是这样的"评估".假设B:短语"被调用函数的执行"包括后缀增量运算符的值计算和后缀增量运算符的副作用.根据这些假设和上述规则,可以得出:I)值计算和后缀增量运算符的副作用都是在赋值运算符在main中的副作用之前排序,或者II)值计算和副作用在主要的赋值运算符的副作用之后,后缀增量运算符的顺序排序.
考虑规则更新左操作数的存储值的副作用在左右操作数的值计算之后排序.该规则排除了上述情况.因此,案例II成立.QED
总的来说,这看起来非常强烈.而且,它对应于人们直觉上认为最可能的替代方案.
然而,它确实依赖于对"评估"和"被调用函数的执行"(假设A和B)这两个术语的特定解释以及一个不完全直接的推理线,所以我想把它放在那里看人们是否有有理由相信这种解释是不正确的.注意,脚注94仅在它也适用于呼叫者不与被叫者交错的意义上同样适用于此解释,这反过来暗示"交错"意味着在"abab"意义上交错,因为显然呼叫者与在较弱的"aba"意义上的被叫者.此外,在编译器内联函数然后执行相同类型的优化以激发表达式i = i++具有未定义行为的原因的情况下,备选方案2和3似乎是合理的.
c operator-precedence undefined-behavior sequence-points language-lawyer
将变量声明为"volatile"意味着直接从内存位置读取/写入,而不是从寄存器变量读取/写入.我对'序列点'有所了解.但我不明白标题中提到的陈述.
有人可以解释一下,并给出一些代码片段吗?
我对整个序列点的理解是基本的.我所拥有的只是一些粗略直观的想法,"一旦遇到序列点,我们就可以确定先前评估的所有副作用都已完成".我还读到,在语句中printf("%d",a++,++a,a++),行为未定义,因为逗号不表示序列点,而分号则表示.因此,我觉得一个非常严谨和确凿的答案对我有很大帮助,而不是通过直觉猜测和思考.
C语言中的以下类型的语句也是安全可靠的:
int a=4,*ptr=&a; //Statement 1
x+=4,y=x*2; //Statement 2 (Assume x and y are integer variables)
Run Code Online (Sandbox Code Playgroud)
如果有,怎么样?特别是在第二种情况下,如果逗号不是一个序列点,那么在我们在赋值中使用它之前,我们怎么能确定它x是否增加了?对于第一个语句,在分配地址之前,如何确定已初始化并分配内存?我是否应该安全地使用以下内容:4y=x*2aptr
int a=4,*ptr;
ptr=&a;
Run Code Online (Sandbox Code Playgroud)
和
x+=4;
y=x*2;
Run Code Online (Sandbox Code Playgroud)
编辑我对逗号运算符的理解告诉我这些语句是安全的.但是在阅读了关于序列点以及类似事物printf("%d",a++,++a,a++)未定义之后,我有了第二个想法.
标准规定:
在前一个和下一个序列点之间,对象的存储值最多只能通过表达式的计算来修改一次.此外,只能访问先前值以确定要存储的值.
在例子中
i = i++;
a[i] = i++;
Run Code Online (Sandbox Code Playgroud)
从声明的第一句可以清楚地看出,这些例子是未定义行为的结果.
在解释声明的第二句时,据说;
第二句话说:如果一个对象被写入一个完整的表达式,那么同一个表达式中对它的任何和所有访问都必须直接参与计算要写入的值.此规则有效地将法律表达式约束为在修改之前明显存在访问的表达式.例如,旧备用
i = i + 1
Run Code Online (Sandbox Code Playgroud)
是允许的,因为i的访问用于确定i的最终值.这个例子
a[i] = i++
Run Code Online (Sandbox Code Playgroud)
是不允许的,因为i的一个访问(a [i]中的一个)与最终存储在i中的值无关(在i ++中发生),因此没有好的方法来定义.
我的问题是;
1.它是什么意思,如果一个对象被写入一个完整的表达式,那么在同一个表达式中对它的任何和所有访问必须直接参与计算要写入的值.?
2.它是什么意思,该示例a[i] = i++
是不允许的,因为i的一个访问(a [i]中的一个)与最终存储在i中的值无关(在i ++中发生)
可能有人以一种简单的方式解释它吗?
在准备考试时,ANSI CI遇到了以下问题 -
以下声明是否有效?
如果没有,请进行必要的更改以使其有效.
原始陈述是:test(i++,i++);它无效,因为根据K&R p202,行为未定义
参数的评估顺序未指定
但我可以将其更改为以下声明吗?test(i+=2, i+=3)?
我在K&R或任何其他来源中没有看到这样的记录的问题.但XCode编译它并在没有任何警告的情况下运行.
为什么(*p=*p) & (*q=*q);在当C触发未定义行为p和q相等.
int f2(int * p, int * q)
{
(*p=*p) & (*q=*q);
*p = 1;
*q = 2;
return *p + *q;
}
Run Code Online (Sandbox Code Playgroud)
来源(顺便说一句好文章):http://blog.frama-c.com/index.php?post/2012/07/25/On- the- redundancy-of-C99-s-restrict
在C/C++中,第二个语句在
int i = 0;
int j = i++ + i++ + ++i;
Run Code Online (Sandbox Code Playgroud)
调用两者
i相对于彼此是无序的.例如,参见
现在,鉴于Swift被设计为一种安全的语言,这里的相应情况是什么?结果是
var i = 0
let j = i++ + i++ + ++i
Run Code Online (Sandbox Code Playgroud)
明确界定?可以从Swift书中的语言参考中得出结论j == 4吗?
众所周知,标准C++ 11保证传递给函数的临时对象将在函数调用之前创建:标准C++ 11是否保证在函数调用之前创建传递给函数的临时对象?
但是,标准C++ 11是否保证传递给函数的临时对象在函数结束后(之前没有)被销毁?
工作草案,编程语言标准C++ 2016-07-12:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/n4606.pdf
§12.2临时对象
§12.2/ 5
有三种情况下,临时表在与完整表达结束时不同的点被摧毁.第一个上下文是调用默认构造函数来初始化没有相应初始值设定项的数组元素(8.6).第二个上下文是在复制整个数组时调用复制构造函数来复制数组的元素(5.1.5,12.8).在任何一种情况下,如果构造函数具有一个或多个默认参数,则在构造下一个数组元素(如果有)之前,对默认参数中创建的每个临时的销毁进行排序.第三个上下文是引用绑定到临时的.
也:
§1.9/ 10
甲全表达为不是另一种表达的子表达式的表达式.[注意:在某些情况下,例如未评估的操作数,语法子表达式被视为完整表达式(第5条). - 结束注释]如果定义语言构造以产生函数的隐式调用,则语言构造的使用被认为是用于该定义目的的表达式.在临时对象以外的对象的生命周期结束时生成的析构函数的调用是隐式的完整表达式.应用于表达式结果的转换以满足表达式出现的语言构造的要求也被认为是完整表达式的一部分.
这是否意味着标准C++ 11保证传递给函数的临时对象不会在函数结束之前被破坏 - 并且恰好在完整表达式的末尾?
#include <iostream>
using namespace std;
struct T {
T() { std::cout << "T created \n"; }
int val = 0;
~T() { std::cout << "T destroyed \n"; }
};
void function(T t_obj, T &&t, int &&val) {
std::cout << "func-start \n";
std::cout << t_obj.val << ", " << t.val << ", …Run Code Online (Sandbox Code Playgroud)