Линкер в C ++ принимает функции с одинаковыми параметрами и разными типами возвращаемых данных, но я получаю SEGFAULT - PullRequest
3 голосов
/ 08 марта 2019

Предположим, у нас есть файлы hpp и cpp с объявлением и реализацией одной и той же функции с тем же именем / параметрами, но с разными типами возврата.В cpp не включается hpp.Таким образом, с компилятором все в порядке, и компоновщик не выдает никакой ошибки.

hpp объявляет функцию следующим образом:

std::string myFunction(int *);

cpp не включать hpp и реализовывать функцию, но с другим типом возврата:

const std::string& myFunction(int *address) {...}

Затем, когда код клиента включает hpp и использует функцию, и код выполняется, значение адреса, полученного функцией, не является тем, которое было отправлено, и я получаю SEGFAULT.Действительно, при отладке я получаю значение параметра адреса, отличное от того, что я отправил.

Я понимаю, что была допущена ошибка, поскольку этот код явно неверен: я буду использовать тот же тип возврата и чтобы компилятор предупреждал менячто тип возвращаемого значения на самом деле был другим.

Тем не менее, я хотел бы понять, что вызвало эту проблему во время выполнения?Для меня это связано с ожидаемой позицией аргументов в стеке или чем-то в этом роде.Но формальное объяснение было бы интересно для лучшего понимания того, как работает C ++.

Ответы [ 2 ]

3 голосов
/ 08 марта 2019

Согласно стандарту, ваша программа плохо сформирована. Раздел «Программа и связь» посвящен этой проблеме:

6,5 Программа и связь [basic.link]
...

10 . После всех корректировок типов (во время которых typedefs заменяются их определениями), типы указанные во всех объявлениях, ссылающихся на данную переменную или функцию, должны быть идентичными, за исключением того, что объявления для объекта массива можно указать типы массивов, которые отличаются наличием или отсутствием привязки основного массива. Нарушение этого правила для идентификации типа не требует диагностики.

В последнем предложении цитаты говорится, что для этого не требуется никакой диагностики от компилятора. Поэтому, даже если вы правильно включили заголовочный файл, компилятор, возможно, не пожаловался.

2 голосов
/ 08 марта 2019

Вторая функция возвращает указатель, первая (в предположении 64-битной среды) возвращает значение в 24 байта (в соответствии с указателем size + capacity + start).Когда компилятор компилирует клиентский код, он ожидает 24 байта и, вероятно, разыменовывает указатель start.Но функция, которую вы связали, возвращает только 8 байт (указатель на std::string), который нужно разыменовать дважды (один раз, чтобы получить std::string, а затем разыменовать указатель start).

Если бы start было бы в первых 8 возвращенных байтах, ваш клиентский код предположил бы, что там находится строка data , но именно там лежат значения std::string.Также было бы более или менее случайным, что возвращаемые size и capacity, и все было бы более или менее быстро взорваться.

И если бы start не было бы в возвращенных 8 первых байтахкомпилятор попытается разыменовать size, что, вероятно, является маленьким целым числом (возможно, 0).По сути, это всегда segfault.

На самом деле нет никакого способа, которым это может пойти правильно, даже в малейшей степени.Не нарушайте правила.

Это довольно неформальное объяснение, поскольку вы не указали, какую платформу / соглашение о вызовах и т. Д. Используете.Однако суть всегда одна и та же - возвращать ссылку (которая является просто указателем за кулисами) и значение, очевидно, несовместимо.Это как если бы вы ожидали, что пакет с едой полон, но вместо этого получил письмо, чтобы где-то забрать ваш пакет, поэтому вы начинаете есть письмо вместо того, чтобы сначала брать пакет - компьютеры такие «тупые».

...