簡體   English   中英

為什么g ++鏈接器不警告這個不一致的函數聲明?

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

這是使用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它也沒有。 那么,有沒有理由說它沒有,是否有一些旗幟可以讓我發出警告? 提前致謝。

編輯:謝謝你的所有答案。 當存在沖突的函數定義時,鏈接器發出警告,而不是像上面的示例中的沖突函數定義和聲明。 我不明白這種不同行為的原因。

C ++鏈接器僅根據需要識別函數以進行唯一標識。

這來自以下有關C ++鏈接器的深入文章

...符號的名稱用其他字符串裝飾。 這稱為名稱修改。

需要標識符名稱前面的裝飾,因為C ++支持名稱空間。 例如,相同的函數名稱可以在不同的名稱空間中多次出現,而每次表示不同的實體。 為了使鏈接器能夠區分這些實體,每個標識符的名稱前面都有代表其封閉命名空間的標記。

需要標識符名稱后面的裝飾,因為C ++允許函數重載。 同樣的函數名稱可以表示不同的標識符,這些標識符僅在它們的參數列表中有所不同。 為了使鏈接器能夠區分它們,表示參數列表的標記將附加到標識符的名稱。 忽略函數的返回類型,因為兩個重載函數的返回類型不能有所不同。

所以重點是應用於函數的名稱修改忽略了返回類型,因為重載函數不能因返回類型而不同。 因此,鏈接器無法發現問題。

這是擁有包含所有此類函數的本地項目頭文件(可能是foobar.h )的最佳示例。 這樣編譯器就可以看到這樣的問題。

連接器從未打算確定這樣的問題。 要為Real Engineers™留下一些東西。 :-)

鏈接器只對編譯器所說的在模塊中定義的名稱起作用,或者由模塊引用(需要)。 GCC顯然使用“Itanium C ++ ABI”來修改函數名稱(從GCC 3開始)。 對於大多數函數,返回類型未包含在受損名稱中,因此鏈接器不會將其考慮在內:

安騰C ++ ABI

函數類型由其參數類型和可能的結果類型組成。 除了外部級別的類型,或者在函數編碼中的其他分隔的外部名稱中,這些類型由“F..E”對分隔。 出於替換目的(請參閱下面的壓縮),分隔和不限函數類型被認為是相同的。

函數類型的重整是否包括返回類型取決於上下文和函數的性質。 決定是否包含返回類型的規則是:

  • 模板函數(名稱或類型)具有編碼的返回類型,但下面列出了例外情況。
  • 不作為函數名稱修改的一部分出現的函數類型,例如參數,指針類型等,具有返回類型編碼,具有下面列出的例外。
  • 非模板函數名稱沒有編碼的返回類型。

上面(1)和(2)中提到的例外情況,其中包括從未包含的返回類型

  • 構造函數。
  • 析構函數。
  • 轉換運算符函數,例如operator int

通常在C ++中,當編譯器執行名稱查找時(例如,用於重載解析),不考慮函數的返回類型。 這可能是返回類型通常不包含在名稱修改中的部分原因。 我不知道是否有更強的理由不將返回類型合並到錯位名稱中。

$ 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

無法重現。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM