GCC -Os优化跳过extern变量未定义引用及编译疑问
问题与解答
示例代码
extern int var; void foo(int & param) { (void) param; } int main(void) { foo(*(&var)); return 0; }
分步编译命令
$ g++ -Os -o test.o -c test.cpp $ g++ test.o
问题1:-Os启用了哪种优化跳过未定义引用?
移除-Os标志后会出现var未定义引用错误,尝试用GCC文档中-Os包含的所有优化替代该标志,但无法复现此现象。
解答
-Os触发的是多步骤协同的优化链,核心是死代码消除结合地址-解引用折叠:
- 编译器先把
*(&var)直接折叠为var,因为取地址再解引用的操作没有实际意义; - 由于
foo函数内部仅对param做了(void)无操作转换,完全没有使用该参数的值或产生任何副作用,编译器判定整个foo调用属于死代码,直接将其消除; - 既然代码中已经没有任何对
var的实际引用,链接阶段就不会再检查var的定义。
单独使用-Os包含的单个优化选项无法复现,是因为这种消除需要多个优化步骤配合触发,单个选项无法完成完整的优化链。
问题2:编译test.c无优化也不报错的原因?
一次性编译示例代码时:
$ g++ -c test.c
即使没有优化标志也不会报错。
解答
关键在于文件后缀:GCC会将.c后缀的文件当作C语言代码处理,而C语言没有C++的引用(reference)语法:
- 函数声明
void foo(int & param)中的&会被C语言解析为指针声明的旧式写法,等价于void foo(int *param); - 调用
foo(*(&var))会被解析为传递var的地址(&var),函数接收指针参数,整个过程只是传递变量地址,并没有实际读取var的值; - 在C语言中,只要声明了
extern int var,变量的地址就是合法的编译期概念,链接阶段不需要var有实际定义——因为没有读取变量内容,也就不需要访问其存储空间。
而如果编译.cpp文件(C++),引用的语义要求必须绑定到实际存在的对象,即使函数内部没使用参数,编译器也会生成对var的引用,链接时就会要求var有定义。
内容的提问来源于stack exchange,提问作者Yuni Tsukiyama
相关产品推荐
相关产品推荐

