gpt4 book ai didi

c - 为什么这个程序永远不会以标志 `-O3` 终止?

转载 作者:行者123 更新时间:2023-12-05 08:28:46 27 4
gpt4 key购买 nike

下面的程序在不同的选项级别下有不同的行为。当我用 -O3 编译它时,它永远不会终止。当我用 -O0 编译它时,它总是很快就会终止。

#include <stdio.h>
#include <pthread.h>

void *f(void *v) {
int *i = (int *)v;
*i = 0;
printf("set to 0!\n");
return NULL;
}

int main() {
const int c = 1;
int i = 0;
pthread_t thread;
void *ptr = (void *)&c;
while (c) {
i++;
if (i == 1000) {
pthread_create(&thread, NULL, &f, ptr);
}
}
printf("done\n");
}

这是使用不同的优化标志运行它的结果。

username@hostname:/src$  gcc -O0 main.c -o main
username@hostname:/src$ ./main
done
set to 0!
set to 0!
username@hostname:/src$ gcc -O3 main.c -o main
username@hostname:/src$ ./main
set to 0!
set to 0!
set to 0!
set to 0!
set to 0!
set to 0!
^C
username@hostname:/src$

教授幻灯片给出的答案是这样的:

  • 它会一直终止吗?

  • 取决于 gcc 选项

  • 使用 –O3(所有优化):否

为什么?

  • 变量 c 可能会保留在寄存器中,因此不会被共享。

解决方案 « volatile »


感谢您的回复。我现在意识到 volatileC 中的关键字。 volatile关键字说明:

A volatile specifier is a hint to a compiler that an object may change its values in ways not specified by the language so that aggressive optimizations must be avoided.

据我了解,当我们使用-O3标志时,有一个共享寄存器存储c值。所以主线程和子线程会共享它。在这种情况下,如果子线程将c修改为0,主线程要读取c进行比较时,会得到0 while(c) 语句。然后,循环停止。

当我们使用-O0标志时,没有寄存器存储c可以被主线程和子线程共享。虽然 c 被子线程修改了,但是这个变化可能不会写入内存而只是存储在寄存器中,或者它被写入内存而主线程只使用旧值被读取并保存在寄存器中。结果,循环是无限的。

如果我用 const 声明 c 值:const volatile int c = 1;,即使我们编译了程序也会最终终止它与 -O3。我猜想所有线程都会从主内存读取 c 并在更改 c 值时写回主内存。


我知道,根据 C 语言的规范或规则,我们不允许修改 const 关键字声明的值。但我不明白什么是非行为

我写了一个测试程序:

#include "stdio.h"

int main() {
const int c = 1;
int *i = &c;
*i = 2;
printf("c is : %d\n", c);
}

输出

username@hostname:/src$ gcc test.c -o test
test.c: In function ‘main’:
test.c:9:14: warning: initialization discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
9 | int *i = &c;
| ^
username@hostname:/src$ ./test
c is : 2
username@hostname:/src$

结果是 2,这意味着可以修改用 const 声明的变量,但不建议这样做,对吗?


我也试过改变判断条件。如果从while(c){改成while (1){,不管用-O0都会无限循环或 -O3


这个程序不是一个好程序,因为它违反了 C 语言的规范或规则。其实它来自关于软件安全的讲座。

我能这么理解吗?当我们用-O0编译程序时,所有线程共享同一个寄存器存储c

虽然值c在非共享寄存器中,所以当我们使用-O3<时子线程修改值c时不会通知主线程。或者,当我们使用 -O3 时,while(c){ 被替换为 while(1){,因此循环是无限的。

我知道如果我检查生成的汇编代码,这个问题可以很容易地解决。但我不擅长。

最佳答案

这是未定义的行为。每6.7.3 Type qualifiers, paragraph 6 of the (draft) C11 standard :

If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined.

程序没有任何特定行为要求。它的行为完全超出了 C 语言的规范。

您的教授对其如何行为的观察可能是正确的。但他偏离了轨道。对于未定义的行为没有“为什么”。发生的事情可能会随着编译器选项、源代码细节、一天中的时间或月相的变化而变化。任何事物。对任何特定行为的任何期望都是没有根据的。

Solution « volatile »

完全错误

volatile 没有为多线程访问提供足够的保证。参见 Why is volatile not considered useful in multithreaded C or C++ programming? .

volatile 可以看起来“工作”是因为系统的特殊性,或者只是因为任何竞争条件都不会碰巧以可观察的方式触发,但这并不能使它正确。它没有“工作”——你只是没有观察到任何失败。 “我没看到它坏了”并不意味着“它有效”。

请注意,一些 C 实现确实定义volatile 比C 标准要求的要广泛得多。特别是微软defines volatile much more expansively ,使 volatile 在多线程程序中更加有效,甚至有用和正确。

但这并不适用于所有 C 实现。如果您阅读该链接,您会发现它甚至不适用于在 ARM 硬件上运行的 Microsoft 编译代码...

关于c - 为什么这个程序永远不会以标志 `-O3` 终止?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75346472/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com