Почему компоновщик g ++ не предупреждает об этом несовместимом объявлении функции? - PullRequest
4 голосов
/ 31 января 2012

Это было протестировано на Debian squeeze с 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 это не так.Итак, есть ли причина, почему это не так, и есть ли какой-нибудь флаг, который я могу включить, чтобы предупредить?Заранее спасибо.

РЕДАКТИРОВАТЬ: Спасибо за все ответы.Компоновщик действительно выдает предупреждение, когда есть конфликтующие определения функций, в отличие от конфликтующего определения функции и объявления, как в моем примере выше.Я не понимаю причину такого различного поведения.

Ответы [ 4 ]

4 голосов
/ 31 января 2012

Компоновщик C ++ идентифицирует функции настолько, насколько это необходимо для уникальной идентификации.

Это из следующей углубленной статьи о компоновщике C ++.

... названия символов украшены дополнительными строками. Это называется искажением имени.

Украшение перед именем идентификатора необходимо, потому что C ++ поддерживает пространства имен. Например, может встречаться одно и то же имя функции несколько раз в разных пространствах имен, одновременно обозначая разные сущность каждый раз. Чтобы компоновщик мог различать лица, к имени каждого идентификатора добавляется токен представляющий его вмещающие пространства имен.

Украшение после имени идентификатора необходимо, потому что C ++ позволяет перегрузка функции. Опять то же имя функции может обозначать разные идентификаторы, которые отличаются только списком параметров. к разрешить компоновщику различать токены, представляющие список параметров добавляется к имени идентификатора. возвращаемый тип функции не учитывается, потому что два перегружены функции не должны отличаться только типом возвращаемого значения.

Таким образом, дело в том, что искажение имени, применяемое к функциям, игнорирует тип возвращаемого значения, поскольку перегруженные функции не могут различаться по типу возвращаемого значения. Таким образом, компоновщик не может определить проблему.

2 голосов
/ 31 января 2012

Компоновщик просто воздействует на имена, которые, как говорит компилятор, определены в модулях или на которые ссылаются (необходимы) модули. GCC, очевидно, использует «Itanium C ++ ABI» для искажения имен функций (начиная с GCC 3). Для большинства функций возвращаемый тип не включен в искаженное имя, поэтому компоновщик его не учитывает:

Itanium C ++ ABI

Типы функций составлены из их типов параметров и, возможно, тип результата. За исключением на внешнем уровне типа, или в другом внешнем имени с разделителями в или кодирование функции, эти типы ограничены парой "F..E". В целях замены (см. Сжатие ниже), разделенные и неограниченные типы функций считается тем же.

Включает ли искажение типа функции тип возвращаемого значения зависит от контекста и характера функции. Правила для решить, включен ли тип возвращаемого значения:

  • Шаблонные функции (имена или типы) имеют закодированные возвращаемые типы, с исключениями, перечисленными ниже.
  • Типы функций, которые не отображаются как часть искажения имени функции, например параметры, типы указателей и т. д. имеют закодированный тип возврата, за исключением перечисленных ниже.
  • Имена не шаблонных функций не имеют закодированных типов возврата.

Исключения, упомянутые в (1) и (2) выше, для которых возврат тип никогда не включается,

  • Конструкторы.
  • деструкторы.
  • Функции оператора преобразования, например, оператор int

Как правило, в C ++ тип возвращаемого значения функции не учитывается, когда компилятор выполняет поиск по имени (например, для разрешения перегрузки). Это может быть частью причины, по которой тип возвращаемого значения обычно не включается в искажение имени. Я не знаю, есть ли более веская причина не включать возвращаемый тип в искаженное имя.

2 голосов
/ 31 января 2012

Это лучший пример причины наличия локального файла заголовка проекта (возможно, foobar.h), который включает в себя все такие функции. Таким образом, компилятор может увидеть такие проблемы.

Линкеры никогда не предназначались для выявления такой проблемы. Должен оставить что-то для настоящих инженеров и торговли; сделать. : -)

0 голосов
/ 31 января 2012
$ 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

Не удалось воспроизвести.

...