4 Stimmen

Warum warnt der G++-Linker nicht vor dieser inkonsistenten Funktionsdeklaration?

Dies wurde auf Debian Squeeze mit g++ 4.4 und g++ 4.7 getestet. Betrachten Sie zwei C++-Quelldateien.

################
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;
}
##################

Wenn ich das kompiliere und ausführe, gibt es vorhersehbar Probleme. Ich verwende 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"])
#################################

Kompilieren und Ausführen...

$ 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

Igitt. Dies hätte vermieden werden können, wenn der Linker gewarnt hätte, dass foo wurde auf zwei verschiedene Arten erklärt. Auch bei -Wall tut es nicht. Gibt es einen Grund, warum dies nicht der Fall ist, und gibt es ein Flag, das ich einschalten kann, um eine Warnung zu erhalten? Vielen Dank im Voraus.

EDIT: Danke für die vielen Antworten. Der Linker tut eine Warnung ausgeben, wenn es widersprüchliche Funktionsdefinitionen gibt, im Gegensatz zu einer widersprüchlichen Funktionsdefinition und Deklaration wie in meinem obigen Beispiel. Ich verstehe den Grund für dieses unterschiedliche Verhalten nicht.

4voto

Tim Gee Punkte 1042

Der C++-Linker identifiziert Funktionen nur so weit, wie es für eine eindeutige Identifizierung erforderlich ist.

Dies ist aus der folgenden ausführlichen article auf den C++-Linker.

...werden die Namen der Symbole mit zusätzlichen Zeichenfolgen versehen. Dies wird als Namensvermischung bezeichnet.

Die Verzierung vor dem Bezeichnernamen ist erforderlich, weil C++ Namespaces unterstützt. Zum Beispiel kann derselbe Funktionsname mehrfach in verschiedenen Namensräumen vorkommen und jedes Mal eine andere Entität bezeichnen. Um es dem Linker zu ermöglichen, zwischen diesen Entitäten zu unterscheiden Entitäten zu unterscheiden, wird dem Namen jedes Bezeichners ein Token vorangestellt vorangestellt, die die umgebenden Namespaces repräsentieren.

Die Verzierung nach dem Bezeichnernamen wird benötigt, weil C++ erlaubt Funktionsüberladung erlaubt. Auch hier kann derselbe Funktionsname für verschiedene Bezeichner bezeichnen, die sich nur durch ihre Parameterliste unterscheiden. An den Linker in die Lage zu versetzen, diese zu unterscheiden, werden Token, die die Parameterliste repräsentieren, an den Namen des Bezeichners angehängt. Der Rückgabetyp einer Funktion wird nicht beachtet, da zwei überladene Funktionen dürfen sich nicht nur durch ihren Rückgabetyp unterscheiden.

Der Punkt ist also, dass die auf Funktionen angewendete Namensmanipulation den Rückgabetyp außer Acht lässt, da sich überladene Funktionen nicht durch den Rückgabetyp unterscheiden können. Daher ist der Linker nicht in der Lage, das Problem zu erkennen.

2voto

wallyk Punkte 55322

Dies ist das beste Beispiel dafür, warum man eine lokale Projekt-Header-Datei haben sollte (vielleicht foobar.h ), die alle diese Funktionen umfasst. Auf diese Weise kann der Compiler kann sehen solche Probleme.

Linker waren nie dazu gedacht, ein solches Problem zu erkennen. Man muss den echten Ingenieuren™ etwas überlassen :-)

2voto

Michael Burr Punkte 320591

Der Linker arbeitet nur mit den Namen, die laut Compiler in Modulen definiert sind oder von Modulen referenziert (benötigt) werden. GCC verwendet anscheinend die "Itanium C++ ABI" für das Manipulieren von Funktionsnamen (beginnend mit GCC 3). Bei den meisten Funktionen ist der Rückgabetyp nicht in den gemangelten Namen integriert, weshalb der Linker ihn nicht berücksichtigt:

Itanium C++ ABI

Funktionstypen setzen sich aus ihren Parametertypen und eventuell dem Ergebnistyp. Außer auf der äußeren Ebene ist der Typ einer , oder in der eines ansonsten abgegrenzten externen Namens in einer oder Funktionskodierung, werden diese Typen durch ein "F..E"-Paar abgegrenzt. Für die Zwecke der Substitution (siehe Komprimierung unten) werden abgegrenzte und nicht abgegrenzte Funktionstypen als gleichwertig betrachtet.

Ob das Mangling eines Funktionstyps den Rückgabetyp einschließt enthält, hängt vom Kontext und von der Art der Funktion ab. Die Regeln für die ob der Rückgabetyp einbezogen wird, sind:

  • Schablonenfunktionen (Namen oder Typen) haben kodierte Rückgabetypen, mit den unten aufgeführten Ausnahmen.
  • Funktionstypen, die nicht als Teil einer Funktionsnamensvermischung erscheinen, z.B. Parameter, Zeigertypen, usw., haben einen kodierten Rückgabetyp, mit den unten aufgeführten Ausnahmen.
  • Bei Funktionsnamen, die nicht der Vorlage entsprechen, sind die Rückgabetypen nicht kodiert.

Die unter (1) und (2) genannten Ausnahmen, bei denen die Rückgabe Typ nie enthalten ist, sind

  • Konstrukteure.
  • Zerstörer.
  • Umrechnungsoperatorfunktionen, z. B. Operator int

In C++ wird der Rückgabetyp einer Funktion im Allgemeinen nicht berücksichtigt, wenn der Compiler eine Namenssuche durchführt (z. B. für die Überladungsauflösung). Dies könnte einer der Gründe sein, warum der Rückgabetyp in der Regel nicht in das Namensmangling einbezogen wird. Ich weiß nicht, ob es einen triftigeren Grund dafür gibt, den Rückgabetyp nicht in den gemangelten Namen einzubeziehen.

0voto

Andrew Tomazos Punkte 62162
$ 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

Konnte nicht reproduziert werden.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X