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

发布于 2025-01-01 09:50:20 字数 3233 浏览 3 评论 0原文

这是在 Debian scrape 上使用 g++ 4.4 和 g++ 4.7 进行测试的。考虑两个 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

Eww。如果链接器警告 foo 正在以两种不同的方式声明,则可以避免这种情况。即使使用 -Wall 也没有。那么,是否有理由不这样做,是否有一些标志我可以打开以使其发出警告?提前致谢。

编辑:感谢您的所有答案。当存在冲突的函数定义时,链接器确实会发出警告,而不是像上面的示例中那样存在冲突的函数定义和声明。我不明白这种不同行为的原因。

This was tested on Debian squeeze with g++ 4.4 and g++ 4.7. Consider two C++ source files.

################
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.

################################
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. Even with -Wall it doesn't. 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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

独自←快乐 2025-01-08 09:50:20

C++ 链接器仅根据唯一标识所需来标识函数。

这是来自以下关于 C++ 链接器的深入文章

...符号的名称用附加字符串修饰。
这称为名称修改。

标识符名称前需要修饰,因为 C++
支持命名空间。例如,可能会出现相同的函数名称
在不同的命名空间中多次表示不同的
每次实体。使链接器能够区分这些
实体每个标识符的名称前面都带有标记
代表其封闭的命名空间。

标识符名称后面的修饰是必需的,因为 C++ 允许
函数重载。同样,相同的函数名称可以表示
不同的标识符,仅在参数列表上有所不同。到
使链接器能够区分这些标记,它们代表
参数列表附加到标识符的名称后面。这
函数的返回类型被忽略,因为两个重载
函数不能仅在返回类型上有所不同。

因此,关键在于,应用于函数的名称修饰会忽略返回类型,因为重载函数不能因返回类型而异。因此链接器无法发现问题。

The C++ linker only identifies functions as far as it needs to for unique identification.

This is from the following in-depth article on the C++ linker.

...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. 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. 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.

遥远的她 2025-01-08 09:50:20

链接器仅作用于编译器所说的模块中定义的名称或模块引用(需要)的名称。 GCC 显然使用“Itanium C++ ABI”来修改函数名称(从 GCC 3 开始)。对于大多数函数,返回类型不会合并到重整名称中,因此链接器不考虑它:

安腾 C++ ABI

函数类型由它们的参数类型和可能的
结果类型。除了在 , 或 的外层类型
在 a 中以其他方式分隔的外部名称中
或函数编码,这些类型
由“F..E”对分隔。出于替代目的(参见
下面的压缩),带分隔符和不带分隔符的函数类型是
认为是相同的。

函数类型的修饰是否包括返回类型
取决于上下文和功能的性质。规则为
判断是否包含返回类型有:

  • 模板函数(名称或类型)具有编码的返回类型,但下面列出的例外情况。
  • 不作为函数名称修改的一部分出现的函数类型(例如参数、指针类型等)具有返回类型编码,但下面列出的例外情况。
  • 非模板函数名称没有编码的返回类型。

上面(1)和(2)中提到的异常,返回
类型从不包括在内,是

  • 构造函数。
  • 析构函数。
  • 转换运算符函数,例如operator int

通常,在C++ 中,当编译器执行名称查找(例如重载解析)时,不会考虑函数的返回类型。这可能是返回类型通常不包含在名称修改中的部分原因。我不知道是否有更强有力的理由不将返回类型合并到损坏的名称中。

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). 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

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. 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, e.g. 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

  • Constructors.
  • Destructors.
  • Conversion operator functions, e.g. 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). 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.

凉城已无爱 2025-01-08 09:50:20

这是拥有包含所有此类函数的本地项目头文件(可能是 foobar.h )的最佳示例。这样编译器可以发现此类问题。

链接器从来没有打算识别这样的问题。必须给 Real Engineers™ 留下一些事情去做。 :-)

This is the best example of the reason to have a local project header file (perhaps foobar.h) which includes all such functions. 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. :-)

我只土不豪 2025-01-08 09:50:20
$ 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

无法重现。

$ 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文