免责声明:我不这样编码,我只是想了解c语言是如何工作的!!!!
输出是12。
该表达式(a-- == 10 && a-- == 9)从左到右计算,a 仍然是 10,a-- == 10但 a 是 9 a-- == 9。
1)增量后评估的时间是否有明确的规则?从这个例子来看,它似乎在 && 之前但在 == 之后进行评估。是不是因为&&逻辑运算符构成了a-- == 10一个完整的表达式,所以a执行完后就更新了?
2)同样对于c / c ++,某些运算符(例如前缀递减)从右到左发生,因此a == --a首先将a递减到9,然后比较9 == 9。c / c ++这样设计有原因吗?我知道对于 Java,情况正好相反(它从左到右计算)。
#include <stdio.h>
int main() {
int a = 10;
if (a-- == 10 && a-- == 9)
printf("1");
a = 10;
if (a == --a)
printf("2");
return 0;
}
Run Code Online (Sandbox Code Playgroud) c side-effects operator-precedence sequence-points post-increment
考虑以下表达式(带有说明声明):
int n = 42;
--n &= 0x01;
Run Code Online (Sandbox Code Playgroud)
这是否违反排序规则?
在我看来,预自增是左操作数“值计算”的一部分。如果这是真的,那么从 C++11 开始,这里就不存在 UB(并且从 C++17 开始,值计算和副作用都是相对于赋值进行排序的)。
如果它是后置增量,那么 的修改n将仅仅是一个副作用,并且我们不会有良好的排序(直到 C++17)。
我基本上有以下代码片段:
size_t counter = atomic_fetch_sub_explicit(&atomicCounter, 1, memory_order_release);
if (counter - 1 == 0
&& atomic_load_explicit(&anotherAtomicCounter, 1, memory_order_relaxed) == 0 {
//Some code
}
Run Code Online (Sandbox Code Playgroud)
为了正确性,重要的是 的原子加载anotherAtomicCounter发生在 的 fetch-and-sub (FAS) 之后atomicCounter。对于给定的内存顺序,通常无法保证这一点,并且加载可能会在 FAS 之前发生。但是,我想知道序列点如何影响这个特定的代码。标准中提到
如果评估 A 在评估 B 之前排序,则 A 评估将在 B 评估开始之前完成。
与规则 2 结合
在以下二元运算符的第一个(左)操作数评估之后和第二个(右)操作数评估之前有一个序列点:&&(逻辑与)、|| (逻辑或),和,(逗号)。
这意味着原子加载必须在比较之后发生,但只有在知道 FAS 的结果后才能完成比较。
我的问题是这些规则是否保证原子加载始终发生在 FAS 之后,即使使用更宽松的内存顺序也是如此?
提前致谢!
我对CPPReference说 postincrement\xe2\x80\x99s 值评估在其副作用之前排序这一事实感到困惑,但 preincrement 没有这样的保证。
\n我现在想出了一个例子,这很重要,但我不确定我的分析是否正确。
\n据我了解,这两个程序的不同之处在于第一个包含 UB,而第二个则不包含:
\n#include <stddef.h>\n#include <stdio.h>\n\nint main(void) {\n int arr[] = {0, 1, 2};\n int i = 1;\n int x = ++arr[arr[i]];\n}\nRun Code Online (Sandbox Code Playgroud)\n#include <stddef.h>\n#include <stdio.h>\n\nint main(void) {\n int arr[] = {0, 1, 2};\n int i = 1;\n int x = arr[arr[i]]++;\n}\nRun Code Online (Sandbox Code Playgroud)\n我对这个表达式的分析++arr[arr[i]]如下:
i值计算之前排序arr[i]arr[i]值计算之前排序arr[arr[i]]arr[arr[i]]值计算之前排序++arr[arr[i]]以下是由于序列点规则导致未定义行为的两个常见问题:
a[i] = i++; //has a read and write between sequence points
i = i++; //2 writes between sequence points
Run Code Online (Sandbox Code Playgroud)
您在序列点方面遇到的其他事情是什么?
当编译器无法警告我们时,很难找到这些问题.
C#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
int a = 11;
int b = 2;
a -= b -= a -= b += b -= a;
System.Console.WriteLine(a);
}
}
}
Run Code Online (Sandbox Code Playgroud)
输出:27
C++:
#include "stdafx.h"
#include<iostream>
int _tmain(int argc, _TCHAR* argv[])
{
int a = 11;
int b = 2;
a -= b -= a -= b += b -= a;
std::cout<<a<<std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
输出:76
相同的代码有不同的输出,有人可以告诉为什么会这样吗?帮助赞赏!!
我试图确定我对C中序列点的理解 - 只是想检查一下.目前,我认为(1)是未定义的,而(2)仅仅是未指定的,因为在(2)中,在评估参数g和h(因此我们不在i序列点之间修改两次)之后存在序列点,但是参数的评估顺序f仍未指定.我的理解是否正确?
#include <stdio.h>
int g(int i) {
return i;
}
int h(int i) {
return i;
}
void f(int x, int y) {
printf("%i", x + y);
}
int main() {
int i = 23;
f(++i, ++i); // (1)
f(g(++i), h(++i)); // (2)
return 0;
}
Run Code Online (Sandbox Code Playgroud)
编辑:
这里的关键点似乎是编译器是否可以自由地在任何一个g或者h被调用之前执行增量- 我从下面的答案中理解它是,尽管我很欣赏确认情况就是这样.
c undefined-behavior sequence-points language-lawyer unspecified-behavior
在回答了这个问题之后,对于有问题的代码是否是未定义的行为进行了长时间的讨论.这是代码:
std::map<string, size_t> word_count;
word_count["a"] = word_count.count("a") == 0 ? 1 : 2;
Run Code Online (Sandbox Code Playgroud)
首先,至少没有具体说明这一点已经确定.结果根据首先评估分配的哪一侧而不同.在我的回答中,我跟踪了四个结果案例中的每一个,其中包括首先评估哪一方的因素以及该元素之前是否存在.
还有一个简短的表格:
(x = 0) = (x == 0) ? 1 : 2; //started as
(x = 0) = (y == "a") ? 1 : 2; //changed to
Run Code Online (Sandbox Code Playgroud)
我声称它更像是这样的:
(x = 0, x) = (x == 0) ? 1 : 2; //comma sequences x, like [] should
Run Code Online (Sandbox Code Playgroud)
最后,我找到了一个似乎对我有用的例子:
i = (++i,i++,i); //well-defined per SO:Undefined Behaviour and Sequence Points
Run Code Online (Sandbox Code Playgroud)
回到原文,我把它分解成相关的函数调用,以便更容易理解:
operator=(word_count.operator[]("a"), word_count.count("a") == 0 …Run Code Online (Sandbox Code Playgroud) c++ operator-precedence undefined-behavior sequence-points unspecified-behavior
在以下代码中
int main(){
int a=3;
printf("%d %d %d",++a,a,a++);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
按照规定,从C99附录C:,
以下是5.1.2.3中描述的序列点:
计算函数参数的顺序是未定义的,如C标准所指定.
但是,在printf的函数调用中,我们有用逗号分隔的参数,它们被分类为序列点.那么为什么这个陈述对应于未指明的行为呢?
我的问题的上下文是一个简单的基于堆栈的虚拟机的实现.我对加法和乘法运算的实现如下所示:
case OP_ADD: Push(Pop() + Pop()); break;
case OP_MUL: Push(Pop() * Pop()); break;
Run Code Online (Sandbox Code Playgroud)
由于加法和乘法是可交换操作,只要第一个Pop调用(无论哪个是)的副作用(即,更新虚拟机的堆栈指针)将被完成,所以评估Pop调用的顺序并不重要.在另一个Pop电话之前.
通过减法和除法,顺序确实重要,因此我们必须确保控制首先执行哪个Pop.例如,这是减法操作的实现:
case OP_SUB: {
const auto subtrahend = Pop();
const auto minuend = Pop();
Push(minuend - subtrahend);
break;
}
Run Code Online (Sandbox Code Playgroud)
我听说模糊的说法C++ 17已经收紧了序列点和排序规则,但我没有听到细节.在这方面,我不再是语言律师自信地解析规范了.
C++ 17中的更改是否提供了足够的排序保证,减法可以作为单个表达式实现,如加法和乘法?Pop()调用的顺序及其副作用是定义的,实现定义的还是未指定的?
sequence-points ×10
c ×6
c++ ×5
atomic ×1
c# ×1
c++11 ×1
c++17 ×1
side-effects ×1
stdatomic ×1