我们看一下代码
#include <stdint.h>
#pragma pack (push,1)
typedef struct test_s
{
uint64_t a1;
uint64_t a2;
uint64_t a3;
uint64_t a4;
uint64_t a5;
uint64_t a6;
uint64_t a7;
uint8_t b1;
uint64_t a8;
}test;
int main()
{
test t;
__atomic_store_n(&(t.a8), 1, __ATOMIC_RELAXED);
}
Run Code Online (Sandbox Code Playgroud)
由于我们有打包结构,a8 不是自然对齐的,也应该在不同的 64 字节缓存边界之间分割,但生成的程序集 GCC 12.2 是
main:
push rbp
mov rbp, rsp
mov eax, 1
mov QWORD PTR [rbp-23], rax
mov eax, 0
pop rbp
ret
Run Code Online (Sandbox Code Playgroud)
为什么它翻译成简单的MOV?在这种情况下 MOV 不是原子的吗?
添加:clang 16 调用原子函数的相同代码并转换为
main: # @main
push rbp
mov rbp, rsp
sub …Run Code Online (Sandbox Code Playgroud) 假设我有以下数组
unsigned char array[5];
Run Code Online (Sandbox Code Playgroud)
所以我试图了解访问中到底什么是未定义的
array[6]
Run Code Online (Sandbox Code Playgroud)
据我了解,编译器将尝试读取内部的值*(array+6),因此它被定义。所以,如果我理解正确的话,未定义的是当你尝试读取未初始化的内存时会发生什么。
如果是这种情况,那么这里的最佳答案说,除非有陷阱表示,否则定义了读取未初始化的值,但我们知道 unsigned char 没有这种表示。
那么这里定义得好吗?它总是会读到吗*(array+6)?
假设我们有以下代码
int test(bool* flag, int* y)
{
if(*y)
{
*flag = false;
else
{
*flag = true;
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,编译器可以在这里证明写入标志总是会发生,所以我认为以下一个是允许的(我认为这根本不是优化,但只是为了示例)
int test(bool* flag, int* y)
{
*flag = true;
if(*y)
{
*flag = false;
}
}
Run Code Online (Sandbox Code Playgroud)
所以现在,我们也将 true 写入 flag if y!=0,但从 as-if 规则的角度来看,这看起来是有效的。
但我仍然认为,这种优化很奇怪,假设*y = true总是这样,所以标志总是假的,所以如果其他线程读取标志变量,他可能会看到 true,尽管它永远不应该发生,所以它会破坏 as-如果统治?优化是否有效?
另外:非原子的情况很清楚,因为它是 UB,并且所有的赌注都取消了,但是如果标志是具有宽松排序的原子的,那么怎么办?