挥发性和序列点
Volatile and sequence point
给定以下代码:
unsigned int global_flag = 0;
void exception_handle()
{
global_flag = 1;
}
void func()
{
/* access will cause exception which will assign global_flag = 1
then execution continues */
volatile unsigned int x = *(unsigned int *)(0x60000000U); /* memory protection unit configured to raise exception upon accessing this address */
if (global_flag == 1)
{
/* some code */
}
}
鉴于 volatile
must not be reordered across sequence points:
The minimum requirement is that at a sequence point all previous
accesses to volatile objects have stabilized and no subsequent
accesses have occurred
并给出以下关于 sequence points 的信息:
sequence points occur in the following places ... (1) .. (2) .. (3) At the end of a full expression. This category includes expression
statements (such as the assignment a=b;), return statements, the
controlling expressions of if, switch, while, or do-while statements,
and all three expressions in a for statement.
是否承诺volatile unsigned int x = *(unsigned int *)(0x60000000U);
会在if (global_flag == 1)
之前发生(在二进制asm中,CPU乱序执行与这里无关)?
根据上面的引文,volatile unsigned int x = *(unsigned int *)(0x60000000U);
必须在下一个序列点结束之前计算,而volatile unsigned int x = *(unsigned int *)(0x60000000U);
本身就是一个序列点,所以这意味着每个volatile
赋值是在赋值时求值的?
如果上述问题的答案是否定的,那么下一个序列点在if
的end,是否意味着可以执行类似的事情:
if (global_flag == 1)
{
volatile unsigned int x = *(unsigned int *)(0x60000000U);
/* some code */
}
系统是嵌入式one-ARM cortex m0,单核,单线程应用。
Is it promised that volatile unsigned int x = *(unsigned int *)(ILLEGAL_ADDRESS); will take place before if (global_flag == 1)
来自信息性 C11 AnnexC(添加 newlines/formatting 以提高可读性):
The following are the sequence points described in 5.1.2.3:
...
- Between the evaluation of a full expression and the next full expression to be evaluated.
- The following are full expressions:
- an initializer that is not part of a compound literal (6.7.9);
- the expression in an expression statement (6.8.3);
- the controlling expression of a selection statement (if or switch) (6.8.4);
- the controlling expression of a while or do statement (6.8.5);
- each of the (optional) expressions of a for statement (6.8.5.3);
- the (optional) expression in a return statement (6.8.6.4).
因为 *(unsigned int *)(ILLEGAL_ADDRESS);
是一个初始化器(赋值表达式)并且初始化器不是复合文字的一部分,所以它是一个完整的表达式。下一个完整表达式是 if
中的控制语句,因此在 if
和 x
的初始化之间有一个序列点。
来自著名的C11 5.1.2.3p6:
The least requirements on a conforming implementation are:
Accesses to volatile objects are evaluated strictly according to the rules of the abstract machine.
...
由于x
是一个volatile对象,它被严格初始化为抽象机,所以在序列点之后它的右值必须等于*(unsigned int *)(ILLEGAL_ADDRESS)
操作的结果。
所以是的,x
对象的初始化必须发生在 if
.
中的控制表达式之前
关于未定义的行为,引用自C11 6.5.3.2p4:
If an invalid value has been assigned to the pointer, the behavior of the unary * operator is undefined.
正如您评论的那样:
accessing address 0x60000000 is not permitted in my system memory model
可以推断出 (unsigned int*)0x60000000
是无效指针,因此一元 * 运算符应该 spawn dragons.
在您的代码片段中,变量 global_flag
不是易变的,因此没有什么可以阻止编译器跨序列点移动对 global_flag
的访问,或者如果情况允许,则完全删除它。讨论访问x
和访问global_flag
的顺序是没有意义的,因为后者不是可观察事件,只有前者是。
(另请注意,表达式 *(unsigned int *)(0x60000000U)
中没有 volatile
限定符。我认为这确实是您希望特别对待的表达式,但您的代码不会那样做。允许编译器生成预先评估 *(unsigned int *)(0x60000000U)
的代码,然后执行其盘子上的大量其他内容,然后将获得的值分配给 x
,这将满足以下约束C 标准放在 volatile
左值上。)
如果您的代码段有 unsigned int volatile global_flag = 0;
和 *(volatile unsigned int *)(0x60000000U)
,那么“是否承诺……”这个问题的答案将是明确的“是”。
给定以下代码:
unsigned int global_flag = 0;
void exception_handle()
{
global_flag = 1;
}
void func()
{
/* access will cause exception which will assign global_flag = 1
then execution continues */
volatile unsigned int x = *(unsigned int *)(0x60000000U); /* memory protection unit configured to raise exception upon accessing this address */
if (global_flag == 1)
{
/* some code */
}
}
鉴于 volatile
must not be reordered across sequence points:
The minimum requirement is that at a sequence point all previous accesses to volatile objects have stabilized and no subsequent accesses have occurred
并给出以下关于 sequence points 的信息:
sequence points occur in the following places ... (1) .. (2) .. (3) At the end of a full expression. This category includes expression statements (such as the assignment a=b;), return statements, the controlling expressions of if, switch, while, or do-while statements, and all three expressions in a for statement.
是否承诺volatile unsigned int x = *(unsigned int *)(0x60000000U);
会在if (global_flag == 1)
之前发生(在二进制asm中,CPU乱序执行与这里无关)?
根据上面的引文,volatile unsigned int x = *(unsigned int *)(0x60000000U);
必须在下一个序列点结束之前计算,而volatile unsigned int x = *(unsigned int *)(0x60000000U);
本身就是一个序列点,所以这意味着每个volatile
赋值是在赋值时求值的?
如果上述问题的答案是否定的,那么下一个序列点在if
的end,是否意味着可以执行类似的事情:
if (global_flag == 1)
{
volatile unsigned int x = *(unsigned int *)(0x60000000U);
/* some code */
}
系统是嵌入式one-ARM cortex m0,单核,单线程应用。
Is it promised that volatile unsigned int x = *(unsigned int *)(ILLEGAL_ADDRESS); will take place before if (global_flag == 1)
来自信息性 C11 AnnexC(添加 newlines/formatting 以提高可读性):
The following are the sequence points described in 5.1.2.3:
...
- Between the evaluation of a full expression and the next full expression to be evaluated.
- The following are full expressions:
- an initializer that is not part of a compound literal (6.7.9);
- the expression in an expression statement (6.8.3);
- the controlling expression of a selection statement (if or switch) (6.8.4);
- the controlling expression of a while or do statement (6.8.5);
- each of the (optional) expressions of a for statement (6.8.5.3);
- the (optional) expression in a return statement (6.8.6.4).
因为 *(unsigned int *)(ILLEGAL_ADDRESS);
是一个初始化器(赋值表达式)并且初始化器不是复合文字的一部分,所以它是一个完整的表达式。下一个完整表达式是 if
中的控制语句,因此在 if
和 x
的初始化之间有一个序列点。
来自著名的C11 5.1.2.3p6:
The least requirements on a conforming implementation are:
Accesses to volatile objects are evaluated strictly according to the rules of the abstract machine.
...
由于x
是一个volatile对象,它被严格初始化为抽象机,所以在序列点之后它的右值必须等于*(unsigned int *)(ILLEGAL_ADDRESS)
操作的结果。
所以是的,x
对象的初始化必须发生在 if
.
关于未定义的行为,引用自C11 6.5.3.2p4:
If an invalid value has been assigned to the pointer, the behavior of the unary * operator is undefined.
正如您评论的那样:
accessing address 0x60000000 is not permitted in my system memory model
可以推断出 (unsigned int*)0x60000000
是无效指针,因此一元 * 运算符应该 spawn dragons.
在您的代码片段中,变量 global_flag
不是易变的,因此没有什么可以阻止编译器跨序列点移动对 global_flag
的访问,或者如果情况允许,则完全删除它。讨论访问x
和访问global_flag
的顺序是没有意义的,因为后者不是可观察事件,只有前者是。
(另请注意,表达式 *(unsigned int *)(0x60000000U)
中没有 volatile
限定符。我认为这确实是您希望特别对待的表达式,但您的代码不会那样做。允许编译器生成预先评估 *(unsigned int *)(0x60000000U)
的代码,然后执行其盘子上的大量其他内容,然后将获得的值分配给 x
,这将满足以下约束C 标准放在 volatile
左值上。)
如果您的代码段有 unsigned int volatile global_flag = 0;
和 *(volatile unsigned int *)(0x60000000U)
,那么“是否承诺……”这个问题的答案将是明确的“是”。