问题标签 [sequence-points]
For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.
c - 调用 C 中的函数和未定义/未指定行为时的序列点
我试图确定我对 C 中序列点的理解——只是想检查一些东西。目前,我认为 (1) 是未定义的,而 (2) 只是未指定的,基于在 (2) 中,在评估g
and的参数后存在序列点h
(因此我们不会i
在序列点之间修改两次) ,但参数的评估顺序f
仍未指定。我的理解正确吗?
编辑:
似乎这里的关键点是编译器是否可以在调用g
或h
调用之前自由执行这两个增量 - 我从下面的答案中理解是,尽管我很感激确认是这种情况。
c++ - 为什么像“a[i] = i++;”这样的递增操作 导致未定义的行为?
可能重复:
未定义的行为和序列点
键盘给了我这个: 第 9 行:警告:“i”上的操作可能未定义 为什么操作未定义?
c++ - 序列点从何而来?
我知道写类似的东西
不仅不可读,而且违反了 c/c++ 序列点。
这些限制从何而来?在发现它们是错误之前,如何才能看到这些“问题”?
c - i = post_increment_i() 的行为是指定的、未指定的还是未定义的?
考虑以下 C 程序:
对于 2011 版本的 C 标准(称为 C11),以下哪个替代方案是正确的:
- C11 保证 main 返回 0。
- C11 保证 main 返回 0 或 1。
- 根据 C11,该程序的行为未定义。
C11 标准的相关片段:
5.1.2.3 程序执行
访问 volatile 对象、修改对象、修改文件或调用执行任何这些操作的函数都是副作用,它们是执行环境状态的变化。表达式的评估通常包括值计算和副作用的启动。左值表达式的值计算包括确定指定对象的身份。
之前排序是由单个线程执行的评估之间的不对称、传递、成对关系,这导致这些评估之间存在偏序。给定任意两个评估 A 和 B,如果 A 在 B 之前排序,则 A 的执行应先于 B 的执行。(相反,如果 A 在 B 之前排序,则 B 在 A 之后排序。)如果 A 没有排序在 B 之前或之后,则 A 和 B 是无序的。当 A 在 B 之前或之后排序时,评估 A 和 B 的排序不确定,但未指定哪个。13在表达式 A 和 B 的评估之间存在一个序列点意味着与 A 相关的每个值计算和副作用都在与 B 相关的每个值计算和副作用之前排序。(序列点的摘要在附件 C 中给出.)
13) 未排序评估的执行可以交错。不确定顺序的评估不能交错,但可以以任何顺序执行。
6.5 表达式
表达式是一系列运算符和操作数,它们指定值的计算,或指定对象或函数,或产生副作用,或执行它们的组合。运算符的操作数的值计算在运算符结果的值计算之前排序。
如果标量对象的副作用相对于同一标量对象的不同副作用或使用同一标量对象的值的值计算是未排序的,则行为未定义。如果表达式的子表达式有多个允许的排序,则如果在任何排序中出现这种未排序的副作用,则行为未定义。
6.5.2.2 函数调用
在函数指示符和实际参数的评估之后但在实际调用之前有一个序列点。调用函数(包括其他函数调用)中的每个求值,如果在被调用函数的主体执行之前或之后没有特别排序,则相对于被调用函数的执行是不确定的。94
94) 换句话说,函数执行不会相互“交错”。
6.5.2.4 后缀递增和递减运算符
后缀 ++ 运算符的结果是操作数的值。作为副作用,操作数对象的值会递增(即,将相应类型的值 1 添加到其中)。[...]结果的值计算在更新操作数的存储值的副作用之前排序。对于不确定顺序的函数调用,后缀 ++ 的操作是单次求值。
6.5.16 作业
赋值运算符将值存储在左操作数指定的对象中。[...]更新左操作数的存储值的副作用是在左操作数和右操作数的值计算之后排序的。操作数的评估是无序的。
6.8 语句和块
完整表达式是不属于另一个表达式或声明符的表达式。以下每一项都是完整的表达式: [...] 表达式语句中的表达式;[...] return 语句中的(可选)表达式。在完整表达式的评估和要评估的下一个完整表达式的评估之间存在一个序列点。
上述三种备选方案分别对应以下三种情况:
- 后缀增量运算符的副作用在 main 中的赋值之前排序。
- 后缀自增运算符的副作用在 main 赋值之前或之后排序,C11 没有指定哪个。(换句话说,这两种副作用的顺序是不确定的。)
- 这两种副作用是无序的。
通过以下推理链,第一种选择似乎成立:
考虑以下规则:调用函数(包括其他函数调用)中的每个求值,如果在被调用函数的主体执行之前或之后没有特别排序,则相对于被调用函数的执行是不确定的。在 6.5.2.2 中。假设 A:main 中赋值运算符的副作用就是这样的“评估”。假设B:短语“被调用函数的执行”既包括后缀自增算子的值计算,也包括后缀自增算子的副作用。根据这些假设和上述规则,要么 I) 后缀增量运算符的值计算和副作用都排在 main 中赋值运算符的副作用之前,要么 II) 值计算和副作用后缀自增运算符都在 main 中赋值运算符的副作用之后排序。
考虑规则更新左操作数的存储值的副作用是在左操作数和右操作数的值计算之后排序的。该规则排除了上述情况 I。因此,情况 II 成立。量子点
总的来说,这看起来是一个非常有力的论点。此外,它对应于人们直觉上认为最可能的替代方案。
但是,它确实依赖于对术语“评估”和“被调用函数的执行”(假设 A 和 B)的具体解释以及不完全直截了当的推理,所以我想把它放在那里看看人们是否有有理由相信这种解释是不正确的。请注意,脚注 94 仅在它也适用于调用者不与被调用者交错的情况下才与此解释等效,这反过来意味着“交错”意味着“abab”意义上的交错,因为显然调用者与较弱的“aba”意义上的被调用者。还,i = i++
c - 如何使用“x = x++”之类的内容获得 VS 或 Xcode 警告?
本着与诸如“x = ++x”之类的序列点相关的未定义行为的精神,它真的是未定义的吗?,如何让编译器抱怨这样的代码?
具体来说,我正在使用 Visual Studio 2010 和 Xcode 4.3.1,后者用于 OSX 应用程序,但都没有警告我这一点。我什至将 VS2010 上的警告调高为“全部”,它愉快地编译了这个。(作为记录,VS2010 的版本在变量中添加了 1,而 Xcode 的版本保持变量不变。)
c - GCC 中的后增量、函数调用、序列点概念
有一个代码片段,GCC 产生了我没想到的结果:
(我正在为目标 i686-linux-gnu 使用 gcc 版本 4.6.1 Ubuntu/Linaro 4.6.1-9ubuntu3)
[测试.c]
据我了解,函数调用有一个序列点。后增量应在 f() 之前进行。
参见 C99 5.1.2.3:“......称为序列点,之前评估的所有副作用都应完整,后续评估的副作用不应发生。”
对于这个测试用例,也许评估的顺序是不确定的,但最终的结果应该是一样的。所以我预计 b 的最终结果是 5。但是,在使用 'gcc test.c -std=c99' 编译此案例后,输出显示 b = 3。
然后我使用“gcc test.c -std=c99 -S”看看发生了什么:
似乎 GCC 在 f() 之前使用评估值并在两次 f() 调用之后执行 '++' 操作。
我也用llvm-clang编译了这个case,结果显示b=5,也就是我所期望的。
我对后增量和序列点行为的理解是否不正确?或者这是 GCC461 的一个已知问题?
c - 以下表达式的行为是否定义明确?
考虑以下表达式中的序列点
如果我是正确的,执行步骤如下:
1)
2)
3)
对于步骤 1 中的评估,i 的值应该只修改一次,以便将构造称为定义(因为在评估逗号运算符之后有一个序列点)。但我认为情况并非如此。因此它应该是未定义的。请看这个答案。这里上述表达式被称为定义。我错过了什么吗?
c++ - 序列点与运算符优先级
我仍在努力思考以下表达式如何导致未定义的行为:
在对此进行搜索时,我发现了以下问题:
我通读了所有答案,但我仍然对细节有困难。其中一个答案将我上面的代码示例的行为描述为模棱两可,就如何a
修改而言。例如,它可能归结为以下任何一种:
究竟是什么让a
' 的修改模棱两可?这是否与不同平台上的 CPU 指令有关,以及优化器如何利用未定义的行为?换句话说,由于生成的汇编程序,它似乎未定义?
我看不出编译器使用的理由a=(a+1);a++;
,它看起来很古怪而且没有多大意义。什么将拥有编译器使其行为如此?
编辑:
为了清楚起见,我确实了解正在发生的事情,但我只是不明白当有运算符优先级规则时(它本质上定义了表达式的评估顺序),它怎么可能是未定义的。在这种情况下,分配发生在最后,因此a++
需要首先进行评估,以确定要分配给的值a
。所以我期望的是a
在后修复增量期间首先修改它,然后产生一个值来分配回a
(第二次修改)。但是运算符优先级的规则似乎使我的行为非常清楚,我找不到任何“摆动空间”让它具有未定义的行为。
assembly - 优化后对象的破坏顺序
我试图在我的软件中计时一些函数调用来比较不同的实现。
为了获得所需的时序,我正在使用 RAII 结构在函数的开头和结尾读取我的 ARM A9 处理器的 PCCNTR 寄存器。
问题是编译器不尊重我的对象的构造破坏顺序。这是编译器中的错误还是允许的优化?
编译器是一个arm-none-eabi-g++ (Sourcery CodeBench Lite 2012.03-56) 4.6.3
生成的汇编代码:
c - 编译器可能不会跨序列点移动对 volatile 变量的访问;这是什么意思?
将变量声明为“易失性”意味着直接从内存位置读取/写入,而不是从寄存器变量。我对“序列点”有所了解。但我不明白标题中提到的陈述。
有人可以解释一下,并给出一些代码片段吗?