这是使用g ++ 4.4和g ++ 4.7在Debian squeeze上测试的。考虑两个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;
}
##################
如果我编译并运行它,可以预见会出现问题。我正在使用scons。
################################
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
也没有。那么,有没有理由说它没有,是否有一些旗帜可以让我发出警告?提前谢谢。
编辑:感谢所有答案。当存在冲突的函数定义时,链接器会发出警告,而不是像上面示例中的冲突函数定义和声明。我不明白这种不同行为的原因。
答案 0 :(得分:4)
C ++链接器仅根据需要识别函数以进行唯一标识。
这是来自C ++链接器上的以下深入article。
...符号的名称用其他字符串修饰。 这称为名称修改。
因为C ++需要标识符名称之前的装饰 支持命名空间。例如,可以出现相同的函数名称 在不同的命名空间中多次,同时表示不同的 实体每一次。使链接器能够区分它们 实体每个标识符的名称前缀为标记 表示其封闭的命名空间。
因为C ++允许,需要在标识符名称后面的装饰 函数重载。同样的函数名称可以表示 不同的标识符,仅在参数列表中有所不同。至 使链接器能够区分那些代表的令牌 参数列表附加到标识符的名称。该 函数的返回类型被忽略,因为两个重载 函数的返回类型不能有所不同。
所以关键是应用于函数的名称修改忽略了返回类型,因为重载函数不能因返回类型而不同。因此,链接器无法发现问题。
答案 1 :(得分:2)
这是拥有包含所有此类功能的本地项目头文件(可能是foobar.h
)的最佳示例。这样编译器可以看到这样的问题。
Linkers从未打算确定这样的问题。要为Real Engineers™留下一些东西。 : - )
答案 2 :(得分:2)
链接器只对编译器所说的在模块中定义的名称起作用,或者由模块引用(需要)。 GCC显然使用“Itanium C ++ ABI”来修改函数名称(从GCC 3开始)。对于大多数函数,返回类型未包含在受损名称中,因此链接器不会将其考虑在内:
函数类型由它们的参数类型组成,也可能由它们组成 结果类型。除了在外层的类型,或 在一个以其他方式分隔的外部名称中 或函数编码,这些类型 由“F..E”对分隔。出于替代目的(见 下面的压缩),分隔和不受限制的函数类型 考虑相同。
函数类型的重整是否包含返回类型 取决于上下文和函数的性质。规则 决定是否包含返回类型是:
- 模板函数(名称或类型)具有编码的返回类型,但下面列出了例外情况。
- 函数类型不作为函数名称修改的一部分出现,例如参数,指针类型等具有返回类型编码,但下面列出了例外情况。
- 非模板函数名称没有编码的返回类型。
上述(1)和(2)中提到的例外情况,其中的回报 <永远不包括类型
- 构造
- 析构函数。
- 转换运算符函数,例如operator int
通常在C ++中,当编译器执行名称查找(例如,用于重载解析)时,不会考虑函数的返回类型。这可能是返回类型通常不包含在名称修改中的部分原因。我不知道是否有更强的理由不将返回类型合并到受损的名称中。
答案 3 :(得分:0)
$ 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
无法重现。