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