我有一个关于编译器可能做的优化的问题。
下面的代码将说明一下(这是一个例子):
typedef struct test
{
short i;
} s_test;
int function1(char *bin)
{
s_test foo;
lock(gmutex);
foo.i = *(int*)bin * 8;
unlock(gmutex);
sleep(5);
//
// Here anything can happen to *bin in another thread
// an inline example here could be: *(volatile int *)bin = 42;
//
int b = foo.i + sizeof(char*);
return (b > 1000);
}
编译器是否可以用
替换最后一行return ((*(int*)bin * 8 + sizeof(char*)) > 1000);
使用-O2或-O3与gcc 4.4似乎不是这种情况,但是其他编译器和其他编译标志可能是这种情况吗?
答案 0 :(得分:5)
我认为编译器不会做这种优化。
unlock(gmutex)
这是函数,编译器不能假设bin指向的值会在解锁函数中改变。
例如,也许bin来自地球仪。因此bin的优化不能跨越函数调用。答案 1 :(得分:4)
您的示例不必要地复杂,因为您通过与声明的类型不同的类型阅读bin
。别名规则非常复杂,char
甚至是特殊的,我不会对此发表评论。
假设您的声明为int* bin
(因此您不必抛出指针)编译器无权跨函数调用重新排序语句,它们形成所谓的序列点。调用*bin
之前和之后unlock
的值可能不同,因此 在调用后加载值。
编辑:,对于这个参数,unlock
是(或包含)函数调用是必不可少的,而不仅仅是一个解析为序列的宏编译器的操作。
答案 2 :(得分:1)
正如我在直接回复中所说的那样:你的互斥锁不会保护任何恕我直言。 * bin指向的char数组可以在int访问期间被修改,因此根据您的机器,您甚至无法获得对要访问的内存的一致视图。 回到你的问题:编译器不会将源代码转换为你想象的序列但是它可能很好地产生机器语言,这实际上将像你的源代码那样运行。如果它能够检查函数lock,unlock和sleep(无论如何似乎都是宏)并且可以推断出对所涉及的存储器位置没有副作用并且没有实现定义对于调用例如sleep()会呈现临时(“缓存”,尽管标准不使用此术语)值无效,然后它有权生成类似于您给出的指令序列。 C(最高为C99)本质上是单线程的,编译器可以采用它想要的任何优化策略,只要代码行为“好像”它将在理想的假设C机器上运行。 Jens提到的序列点不会影响单线程条件下的正确性:编译器可以在整个函数期间将foo保存在寄存器中,或者它甚至可以用* bin指向的内存位置对foo.i进行别名,所以这段代码本质上是危险的(虽然我认为它不会在大多数编译器上表现出这种行为)。