简体   繁体   English

为什么g ++链接器不警告这个不一致的函数声明?

[英]Why doesn't the g++ linker warn about this inconsistent function declaration?

This was tested on Debian squeeze with g++ 4.4 and g++ 4.7. 这是使用g ++ 4.4和g ++ 4.7在Debian squeeze上测试的。 Consider two C++ source files. 考虑两个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;
}
##################

If I compile and run this, predictably there are problems. 如果我编译并运行它,可以预见有问题。 I'm using scons. 我正在使用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"])
#################################

Compiling and running... 编译并运行......

$ 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

Eww. 好恶。 This could have been avoided if the linker had warned that foo was being declared in two different ways. 如果链接器警告foo以两种不同的方式声明,则可以避免这种情况。 Even with -Wall it doesn't. 即使是-Wall它也没有。 So, is there a reason why it doesn't, and is there some flag that I can turn on to make it warn? 那么,有没有理由说它没有,是否有一些旗帜可以让我发出警告? Thanks in advance. 提前致谢。

EDIT: Thanks for all the answers. 编辑:谢谢你的所有答案。 The linker does issue a warning when there are conflicting function definitions, as opposed to a conflicting function definition and declaration as in my example above. 当存在冲突的函数定义时,链接器发出警告,而不是像上面的示例中的冲突函数定义和声明。 I don't understand the reason for this different behavior. 我不明白这种不同行为的原因。

The C++ linker only identifies functions as far as it needs to for unique identification. C ++链接器仅根据需要识别函数以进行唯一标识。

This is from the following in-depth article on the C++ linker. 这来自以下有关C ++链接器的深入文章

...the names of the symbols are decorated with additional strings. ...符号的名称用其他字符串装饰。 This is called name mangling. 这称为名称修改。

The decoration before the identifier name is needed because C++ supports namespaces. 需要标识符名称前面的装饰,因为C ++支持名称空间。 For example the same function name can occur multiple times in different namespaces while denoting a different entity each time. 例如,相同的函数名称可以在不同的名称空间中多次出现,而每次表示不同的实体。 To enable the linker to differentiate between those entities the name of each identifier is prepended with tokens representing its enclosing namespaces. 为了使链接器能够区分这些实体,每个标识符的名称前面都有代表其封闭命名空间的标记。

The decoration after the identifier name is needed because C++ allows function overloading. 需要标识符名称后面的装饰,因为C ++允许函数重载。 Again the same function name can denote different identifiers, which differ only in their parameter list. 同样的函数名称可以表示不同的标识符,这些标识符仅在它们的参数列表中有所不同。 To enable the linker to differentiate between those, tokens representing the parameter list are appended to the name of the identifier. 为了使链接器能够区分它们,表示参数列表的标记将附加到标识符的名称。 The return type of a function is disregarded, because two overloaded functions must not differ only in their return type. 忽略函数的返回类型,因为两个重载函数的返回类型不能有所不同。

So the point is that the name mangling applied to functions disregards return type as overloaded functions cannot differ by return type. 所以重点是应用于函数的名称修改忽略了返回类型,因为重载函数不能因返回类型而不同。 As such the linker is unable to spot the problem. 因此,链接器无法发现问题。

This is the best example of the reason to have a local project header file (perhaps foobar.h ) which includes all such functions. 这是拥有包含所有此类函数的本地项目头文件(可能是foobar.h )的最佳示例。 That way the compiler can see such problems. 这样编译器就可以看到这样的问题。

Linkers were never intended to identify such an issue. 连接器从未打算确定这样的问题。 Gotta leave something for Real Engineers™ to do. 要为Real Engineers™留下一些东西。 :-) :-)

The linker just acts on the names that the compiler says are defined in modules are or are referenced (needed) by modules. 链接器只对编译器所说的在模块中定义的名称起作用,或者由模块引用(需要)。 GCC apparently uses the "Itanium C++ ABI" for mangling function names (starting with GCC 3). GCC显然使用“Itanium C ++ ABI”来修改函数名称(从GCC 3开始)。 For most functions, the return type isn't incorporated into the mangled name, so that's why the linker doesn't take it into account: 对于大多数函数,返回类型未包含在受损名称中,因此链接器不会将其考虑在内:

Itanium C++ ABI 安腾C ++ ABI

Function types are composed from their parameter types and possibly the result type. 函数类型由其参数类型和可能的结果类型组成。 Except at the outer level type of an , or in the of an otherwise delimited external name in a or function encoding, these types are delimited by an "F..E" pair. 除了外部级别的类型,或者在函数编码中的其他分隔的外部名称中,这些类型由“F..E”对分隔。 For purposes of substitution (see Compression below), delimited and undelimited function types are considered the same. 出于替换目的(请参阅下面的压缩),分隔和不限函数类型被认为是相同的。

Whether the mangling of a function type includes the return type depends on the context and the nature of the function. 函数类型的重整是否包括返回类型取决于上下文和函数的性质。 The rules for deciding whether the return type is included are: 决定是否包含返回类型的规则是:

  • Template functions (names or types) have return types encoded, with the exceptions listed below. 模板函数(名称或类型)具有编码的返回类型,但下面列出了例外情况。
  • Function types not appearing as part of a function name mangling, eg parameters, pointer types, etc., have return type encoded, with the exceptions listed below. 不作为函数名称修改的一部分出现的函数类型,例如参数,指针类型等,具有返回类型编码,具有下面列出的例外。
  • Non-template function names do not have return types encoded. 非模板函数名称没有编码的返回类型。

The exceptions mentioned in (1) and (2) above, for which the return type is never included, are 上面(1)和(2)中提到的例外情况,其中包括从未包含的返回类型

  • Constructors. 构造函数。
  • Destructors. 析构函数。
  • Conversion operator functions, eg operator int 转换运算符函数,例如operator int

In general in C++ the return type of a function isn't considered when the compiler performs name lookup (for example for overload resolution). 通常在C ++中,当编译器执行名称查找时(例如,用于重载解析),不考虑函数的返回类型。 This might be part of the reason why the return type isn't usually included in the name mangling. 这可能是返回类型通常不包含在名称修改中的部分原因。 I don't know if there's a stronger reason for not incorporating the return type into the mangled name. 我不知道是否有更强的理由不将返回类型合并到错位名称中。

$ 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

Could not reproduce. 无法重现。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM