我在GCC 4.8.2中遇到了一个奇怪的优化 - -O2,它删除了一个函数。
请参阅以下代码。
increase()和increase2()是相同的,只是后者有一个printf()。
但是如果在GCC中使用-O2,则会删除increase()。
#include <stdio.h>
#include <stdint.h>
#include <arpa/inet.h>
void swap(uint64_t *vector)
{
uint32_t *p = (uint32_t *)vector;
uint32_t tmp = p[0];
p[0] = htonl(p[1]);
p[1] = htonl(tmp);
}
void increase(uint64_t *vector)
{
swap(vector);
(*vector)++;
swap(vector);
}
void increase2(uint64_t *vector)
{
swap(vector);
(*vector)++;
printf("touch...\n");
swap(vector);
}
int main()
{
uint64_t vector = 0xa;
increase(&vector);
printf("%lx\n", vector);
increase2(&vector);
printf("%lx\n", vector);
return 1;
}
输出是:
a
touch...
10000000000000a
为什么?
提前致谢
很抱歉,我没有说清楚。
问题不在于“删除功能”,而是“功能不会影响参数”。
Matt McNabb指出了原因,即严格的混叠规则。 非常感谢你。
答案 0 :(得分:5)
此代码导致未定义的行为:
uint32_t *p = (uint32_t *)vector;
uint32_t tmp = p[0];
vector
指向的内存是uint64_t
类型的对象,但是您通过类型为uint32_t
的左值读取它。这违反了严格的别名规则。
由于您的程序始终调用此函数,因此未定义整个程序的行为。因此,当优化器切断导致UB的路径时,您可能会看到奇怪的优化工件。
另一个问题是您使用%lx
打印uint64_t
。除非您的系统使用64位长,否则这不会起作用。要获得正确的说明符,请使用printf("%" PRIx64 "\n", vector);
。您可能需要#include <inttypes.h>
。
修复printf格式说明符后,我的系统会在-O2
及更低版本提供以下内容,或者如果使用-fno-strict-aliasing
开关:
10000000000000a
touch...
20000000000000a
和-O3
处的垃圾。
答案 1 :(得分:0)
如果实际删除了该功能,您的输出将更像是:
touch...
a
touch...
10000000000000a
要回答你的问题,它会打印出来,因为你告诉它按顺序打印这三行。
您对increase()
的来电不会输出任何内容,然后您使用a
输出printf("%lx\n", vector)
。
然后,您拨打increase2()
并按设计输出touch...
,然后使用10000000000000a
输出printf("%lx\n", vector)
。
您是否检查了非优化输出?
答案 2 :(得分:0)
我知道你的问题是“为什么”。 @Matt McNabb很好地解释了这一点。但是如何解决呢?
通常编译器和CPU比指针更喜欢值,因为它们具有更多(和更安全)的优化空间。因此,让我们完全避免指针技巧并进行价值转换:
#include <endian.h>
void swap(uint64_t *vector)
{
*vector = htobe64(le64toh(*vector));
}
这使您的程序行为对优化不敏感。