是否有可能看到gcc和g ++编译过程背后发生了什么? 我有以下程序:
#include <stdio.h>
#include <unistd.h>
size_t sym1 = 100;
size_t *addr = &sym1;
size_t *arr = (size_t*)((size_t)&arr + (size_t)&addr);
int main (int argc, char **argv)
{
(void) argc;
(void) argv;
printf("libtest: addr of main(): %p\n", &main);
printf("libtest: addr of arr: %p\n", &arr);
while(1);
return 0;
}
为什么使用gcc时出错可能会产生没有错误的二进制文件? 我正在寻找一种方法来追踪使它们表现不同的原因。
# gcc test.c -o test_app
test.c:7:1: error: initializer element is not constant
# g++ test.c -o test_app
我认为原因可能是gcc使用cc1作为编译器而g ++使用cc1plus
有没有办法更准确地输出实际做的事情?
我试过使用-v标志,但输出非常相似。是否有不同的标志传递给链接器?
比较两个编译过程并发现其中的差异的最简单方法是什么?
答案 0 :(得分:10)
在这种情况下,gcc
不会产生任何结果,因为您的程序无效C.正如编译器所解释的那样,初始化元素(用于初始化全局变量arr
的表达式)不是常量。
C要求初始化表达式是编译时常量,以便局部变量的内容可以放在可执行文件的数据段中。这不能用于arr
,因为在链接时间之前不知道所涉及的变量的地址,并且动态链接器不能简单地填充它们的总和,就像addr1
的情况一样。 C ++允许这样,因此g++
生成初始化代码,用于计算非常量表达式并将它们存储在全局变量中。此代码在调用main()
之前执行。
可执行文件cc1
和cc1plus
是编译器实现的内部细节,因此与观察到的行为无关。相关的事实是gcc
期望有效的C代码作为其输入,g++
期望有效的C ++代码。您提供的代码是有效的C ++,但不是有效的C,这就是g++
编译它而gcc
没有编译的原因。
答案 1 :(得分:2)
这里有一个更有趣的问题。请考虑以下测试用例:
#include <stdint.h>
#if TEST==1
void *p=(void *)(unsigned short)&p;
#elif TEST==2
void *p=(void *)(uintptr_t)&p;
#elif TEST==3
void *p=(void *)(1*(uintptr_t)&p);
#elif TEST==4
void *p=(void *)(2*(uintptr_t)&p);
#endif
gcc(即使是非常保守的标志-ansi -pedantic-errors
)拒绝测试1但接受测试2,并接受测试3但拒绝测试4。
由此我得出结论,在检查初始化程序是否为常量表达式之前,一些易于优化的操作(如转换为相同大小的对象,或乘以1)将被消除。
因此,根据C标准,gcc可能会接受一些它应该拒绝的事情。但是当你使它们稍微复杂一些时(比如将一个强制转换的结果添加到另一个强制转换的结果中 - 无论如何添加两个地址可能会产生什么有用的值?)它会注意到问题并拒绝表达。