Компоновщик в C принимает функции с одинаковыми параметрами и разными типами возвращаемых данных, но я получаю ошибку SEGFAULT

#c #linker-errors

#c #компоновщик-ошибки

Вопрос:

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

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

 std::string myFunction(int *);
  

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

 const std::stringamp; myFunction(int *address) {...}
  

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

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

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

Комментарии:

1. Это неопределенное поведение . Ваш код должен порождать мелких демонов. Для what made this issue at runtime? вам нужно проверить сгенерированный ассемблерный код компилятором. Вам нужно будет указать архитектуру, компилятор, версию компилятора и параметры компилятора и предоставить небольшой MCVE или опубликовать сгенерированную сборку вашим компилятором, чтобы другие могли копировать и проверять поведение.

2. «понимание того, как работает C » — обратите внимание, что это выходит за рамки языка C (с этой точки зрения, ваша программа просто неправильная, точка). Следовательно, это зависит от конкретного соглашения о вызове / ABI, которое зависит от платформы и, возможно, также зависит от компилятора.

Ответ №1:

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

6.5 Программа и компоновка [basic.link]

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

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

Ответ №2:

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

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

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

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

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