C / C ++ неиспользуемая встроенная функция неопределенная ссылка - PullRequest
2 голосов
/ 04 сентября 2011

Рассмотрим следующий код (он не является специфичным для других; другие примеры, например, связанные с библиотекой реального времени, демонстрируют аналогичное поведение):

#define _GNU_SOURCE
#include <pthread.h>

inline void foo() {
    static cpu_set_t cpuset;
    pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
}

int main(int argc, char *argv[]) { }

Это допустимая программа на C и C ++. Поэтому я сохраняю содержимое этого файла в testc.c и testcpp.cpp и пытаюсь собрать.

Когда я встраиваю C++, я не получаю ошибки. Когда я встраиваю C, я получаю неопределенную ошибку ссылки. Теперь эта ошибка возникает в -O1 и в -O3. В любом случае можно ли дать команду gcc правильно поступить (см., Что foo не используется и пропущено требование для определения pthread_setaffinity_np)?

РЕДАКТИРОВАТЬ: Я думал, что это было очевидно из контекста, но сообщение об ошибке:

/tmp/ccgARGVJ.o: In function `foo':
testc.c:(.text+0x17): undefined reference to `pthread_setaffinity_np'

Обратите внимание, что, поскольку на foo нет ссылки в основном пути, g++ корректно полностью игнорирует функцию, а gcc - нет.

РЕДАКТИРОВАТЬ 2: Позвольте мне попробовать это еще раз. Функция foo и последующий вызов pthread_setaffinity_np не используются. Основная функция пуста. Просто посмотрите на это! Каким-то образом g++ выяснил, что foo не нужно было включать, и впоследствии процесс сборки не запустился, когда мы намеренно пропустили -lpthread (а проверка экспортированных символов с помощью nm подтверждает, что ни foo ни ссылка на pthread_setaffinity_np не требовалась). Результирующий вывод gcc не подхватил этот факт.

Я задаю этот вопрос, потому что интерфейсы C ++ и C, похоже, дают разные результаты для одного и того же ввода. Похоже, это не проблема ld prima facie, потому что я ожидаю, что оба пути приведут к одной и той же ошибке компоновки, поэтому я подчеркнул, что это проблема компилятора. Если бы и C ++, и C создавали проблемы, тогда да, я бы согласился, что это проблема связывания

Ответы [ 4 ]

8 голосов
/ 04 сентября 2011

Ну, очевидно, ваша программа содержит ошибку: вы объявляете и вызываете функцию pthread_setaffinity_np, но вы никогда не определяете ее. Видимо, вы забыли предоставить библиотеку, которая содержит определение. Это ошибка как в C, так и в C ++.

Другими словами, не допустимая программа на C и C ++. Он нарушает правило единого определения C ++ (и как бы оно ни называлось в C).

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

То, пойманы они или нет, может зависеть от многих факторов. В данном конкретном случае, очевидно, важным фактором является различие между свойствами встроенных функций языков C и C ++. (И да, они действительно отличаются между C и C ++). Я предполагаю, что в режиме C ++ компилятор решил, что эта встроенная функция не нуждается в реальном теле, а в режиме C он все равно решил сгенерировать тело.

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

Принимая во внимание вышесказанное, вы можете спросить себя, действительно ли вы заботитесь о том, почему вы получили разные отчеты об ошибках в режимах C и C ++. Если вы это сделаете, это потребует некоторых исследований внутренней механики этой конкретной реализации и не будет иметь большого отношения к самим языкам.

6 голосов
/ 04 сентября 2011

В C ключевое слово inline не влияет на связь функции. Таким образом, foo имеет внешнюю связь и не может быть оптимизирована, потому что она может быть вызвана из другой единицы перевода. Если компилятор / ассемблер помещает функции в свои отдельные секции и компоновщик может отбрасывать ненужные секции функций во время компоновки, он может избежать ошибки компоновки, но будет правильным, поскольку эта программа ссылается на pthread_setaffinity_np, она в любом случае вы должны где-то содержать определение этой функции, то есть вы должны использовать -lpthread или эквивалентный.

В C ++ функции inline имеют внутреннюю некоторую странную псевдо-внешнюю связь по умолчанию, поэтому gcc оптимизировал ее. Подробности смотрите в комментариях.

Короче говоря, отсутствие ошибки в некоторых конфигурациях - это сбой gcc в диагностике недопустимой программы. Это не то поведение, которого вы должны ожидать.

Другой урок, который вы должны извлечь из этого, заключается в том, что C и C ++ совсем не похожи на одно и то же. Выберите, какой вы пишете, и придерживайтесь его! Не пытайтесь писать код, который «взаимозаменяем» между ними, или вы, вероятно, сделаете его слегка некорректным в обоих случаях ...

1 голос
/ 04 сентября 2011

foo может не использоваться в вашем исходном коде, но почти наверняка на него ссылаются в других местах процесса сборки, и, следовательно, его необходимо скомпилировать.

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

Если внутри компоновщик решит собрать всю программу за один проход, а затем выполнить оптимизацию за другой, я ожидаю, что вычтобы увидеть эту ошибку (как она может собрать всю программу?)

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

Похоже, вы полагаетесь на специфическое поведение компилятора / компоновщика.

1 голос
/ 04 сентября 2011

inline - это только предложение, а не то, что компилятор обязан слушать, поэтому он не может предположить, что foo не используется в другом модуле компиляции.

Но, да, этобыло бы неплохо точно знать, какая именно неопределенная ссылка, учитывая, что вы не опубликовали ошибку, и странно, что она появляется в C, а не в C ++ компиляции.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...