为什么 g++ 链接器不警告这种不一致的函数声明?
Why doesn't the g++ linker warn about this inconsistent function declaration?
这是在使用g++4.4和g++4.7的Debian挤压上测试的。考虑两个C++源文件。
################
foo.cc
#################
#include <string>
using std::string;
int foo(void)
{
return 0;
}
#################
bar.cc
#################
#include <string>
using std::string;
//int foo(void);
string foo(void);
int main(void)
{
foo();
return 0;
}
##################
如果我编译并运行这个,可以预见会出现问题。我在用烤饼。
################################
SConstruct
################################
#!/usr/bin/python
env = Environment(
CXX="g++-4.7",
CXXFLAGS="-Wall -Werror",
#CXX="g++",
#CXXFLAGS="-Wall -Werror",
)
env.Program(target='debug', source=["foo.cc", "bar.cc"])
#################################
正在编译和运行。。。
$ scons
g++-4.7 -o bar.o -c -Wall -Werror bar.cc
g++-4.7 -o foo.o -c -Wall -Werror foo.cc
g++-4.7 -o debug foo.o bar.o
$ ./debug
*** glibc detected *** ./debug: free(): invalid pointer: 0xbff53b8c ***
======= Backtrace: =========
/lib/i686/cmov/libc.so.6(+0x6b381)[0xb7684381]
/lib/i686/cmov/libc.so.6(+0x6cbd8)[0xb7685bd8]
/lib/i686/cmov/libc.so.6(cfree+0x6d)[0xb7688cbd]
/usr/lib/libstdc++.so.6(_ZdlPv+0x1f)[0xb7856c5f]
/lib/i686/cmov/libc.so.6(__libc_start_main+0xe6)[0xb762fca6]
./debug[0x8048461]
======= Memory map: ========
08048000-08049000 r-xp 00000000 fd:10 7602195 /home/faheem/corrmodel/linker/debug
08049000-0804a000 rw-p 00000000 fd:10 7602195 /home/faheem/corrmodel/linker/debug
09ae0000-09b01000 rw-p 00000000 00:00 0 [heap]
b7617000-b7619000 rw-p 00000000 00:00 0
b7619000-b7759000 r-xp 00000000 fd:00 1180005 /lib/i686/cmov/libc-2.11.3.so
b7759000-b775a000 ---p 00140000 fd:00 1180005 /lib/i686/cmov/libc-2.11.3.so
b775a000-b775c000 r--p 00140000 fd:00 1180005 /lib/i686/cmov/libc-2.11.3.so
b775c000-b775d000 rw-p 00142000 fd:00 1180005 /lib/i686/cmov/libc-2.11.3.so
b775d000-b7760000 rw-p 00000000 00:00 0
b7760000-b777c000 r-xp 00000000 fd:00 4653173 /lib/libgcc_s.so.1
b777c000-b777d000 rw-p 0001c000 fd:00 4653173 /lib/libgcc_s.so.1
b777d000-b777e000 rw-p 00000000 00:00 0
b777e000-b77a2000 r-xp 00000000 fd:00 1179967 /lib/i686/cmov/libm-2.11.3.so
b77a2000-b77a3000 r--p 00023000 fd:00 1179967 /lib/i686/cmov/libm-2.11.3.so
b77a3000-b77a4000 rw-p 00024000 fd:00 1179967 /lib/i686/cmov/libm-2.11.3.so
b77a4000-b7889000 r-xp 00000000 fd:00 2484736 /usr/lib/libstdc++.so.6.0.17
b7889000-b788d000 r--p 000e4000 fd:00 2484736 /usr/lib/libstdc++.so.6.0.17
b788d000-b788e000 rw-p 000e8000 fd:00 2484736 /usr/lib/libstdc++.so.6.0.17
b788e000-b7895000 rw-p 00000000 00:00 0
b78ba000-b78bc000 rw-p 00000000 00:00 0
b78bc000-b78bd000 r-xp 00000000 00:00 0 [vdso]
b78bd000-b78d8000 r-xp 00000000 fd:00 639026 /lib/ld-2.11.3.so
b78d8000-b78d9000 r--p 0001b000 fd:00 639026 /lib/ld-2.11.3.so
b78d9000-b78da000 rw-p 0001c000 fd:00 639026 /lib/ld-2.11.3.so
bff41000-bff56000 rw-p 00000000 00:00 0 [stack]
Aborted
哇。如果链接器警告foo
是以两种不同的方式声明的,那么这本来是可以避免的。即使使用-Wall
,它也不会。那么,它不这样做有原因吗?有没有什么标志可以让我打开它发出警告?提前谢谢。
编辑:谢谢你的回答。当存在冲突的函数定义时,链接器确实会发出警告,而不是像我上面的例子中那样,函数定义和声明冲突。我不明白这种不同行为的原因。
C++链接器只识别唯一标识所需的函数。
这是来自以下关于C++链接器的深入文章。
符号的名称用附加字符串进行装饰。这就是所谓的名字篡改。
需要在标识符名称之前进行修饰,因为C++支持命名空间。例如,可以出现相同的函数名在不同的命名空间中多次,同时表示不同的实体。使链接器能够区分实体-每个标识符的名称前面都有标记表示其封闭的命名空间。
需要在标识符名称后面进行修饰,因为C++允许函数重载。同样,相同的函数名称可以表示不同的标识符,它们仅在参数列表上不同。到使链接器能够区分这些,表示参数列表被附加到标识符的名称。这个函数的返回类型被忽略,因为两个重载函数不能仅在返回类型上有所不同。
因此,关键是应用于函数的名称篡改忽略了返回类型,因为重载函数不能因返回类型而不同。因此,链接器无法发现问题。
链接器只作用于编译器所说的在模块中定义的名称,这些名称被模块引用(需要)。GCC显然使用了"安腾C++ABI"来破坏函数名(从GCC 3开始)。对于大多数函数,返回类型没有合并到损坏的名称中,所以链接器没有考虑它:
安腾C++ABI
函数类型由其参数类型组成,并且可能结果类型。除了在外部级别类型的,或中以其他方式分隔的外部名称的或函数编码,这些类型由一对"F..E"分隔。出于替换的目的(参见压缩如下)、分隔和未限制的函数类型被认为是一样的。
函数类型的篡改是否包括返回类型取决于上下文和功能的性质。的规则决定是否包括退货类型有:
- 模板函数(名称或类型)具有编码的返回类型,下面列出的例外情况除外
- 未作为函数名称篡改的一部分出现的函数类型,例如参数、指针类型等,都对返回类型进行了编码,以下列出的例外情况除外
- 非模板函数名称没有编码返回类型
上述(1)和(2)中提到的例外情况类型从不包括在内,是吗
- 施工人员
- 破坏者
- 转换运算符函数,例如运算符int
通常,在C++中,编译器执行名称查找时不考虑函数的返回类型(例如重载解析)。这可能是返回类型通常不包含在名称篡改中的部分原因。我不知道是否有更有力的理由不将返回类型合并到损坏的名称中。
这是拥有一个本地项目头文件(可能是foobar.h
)的最佳示例,该文件包含所有此类函数。这样编译器就可以看到这样的问题。
链接者从来没有打算发现这样一个问题。必须为Real Engineers留下一些东西™要做:-)
$ cat foo.cpp
#include <string>
using std::string;
int foo(void)
{
return 0;
}
$ cat bar.cpp
#include <string>
using std::string;
//int foo(void);
string foo(void);
int main(void)
{
foo();
return 0;
}
$ g++ -c -o bar.o bar.cpp
$ g++ -c -o foo.o foo.cpp
$ g++ foo.o bar.o
$ ./a.out
$ echo $?
0
$ g++ --version
g++ (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1
无法复制。
- 填充上编译器生成的复制构造函数之间的不一致
- 从 C++ 函数与 Python 函数返回的不一致值用于偏斜正态分布
- 在C++17中,为什么类模板和函数模板的指针类型推导明显不一致
- void 函数中的指针参数返回不一致的值
- 参数包推导不一致 int 和 int& 在可变参数模板化成员函数中创建运行成员函数的线程
- 为什么函数的返回类型与实际句子不一致?
- 函数模板和函数之间奇怪的不一致"normal"
- 跨编译器的 constexpr 成员函数的重载解析不一致
- GDI打印API StartDoc函数给出的结果不一致
- 当指定初始化程序的顺序和字段声明不一致时,clang可以删除函数调用
- 我对GLM lookAt函数的理解不一致
- 异步函数产生不一致的结果
- Visual Studio中嵌套模板函数的typeid不一致
- C C++函数名称冲突和崩溃不一致
- 与GCC/MSVC中的lambda转换构造函数不一致
- __LINE__在传递给类似函数的宏的 lambda 中不一致地计算
- 构造函数不一致的多级类继承
- c++ 11构造函数重载解析和initialiser_lists: clang++和c++不一致.< / h1 &
- 为什么 g++ 链接器不警告这种不一致的函数声明?
- 函数原型和函数实现签名不一致地使用const可以吗?