Ошибка связывания кода с `floor ();`, `ceil ();` и `pow ();`

Я кодирую под GNU / Linux Debian 8.5

У меня простая программа.

Если я скомпилирую это с gcc prog.c, все в порядке!

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>

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

    float _f = 3.1415f;

    floor(_f);
    ceil(_f);

    return 0;
}

Бад, если я добавлю pow(), он говорит, что не может найти pow, и мне нужно добавить gcc prog.c -lm, чтобы все было правильно.

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>

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

    float _f = 3.1415f;

    floor(_f);
    ceil(_f);
    pow(_f, 2);

    return 0;
}

Если я прав, pow(), ceil(), floor() все из <math.h>?

Так почему же floor() и ceil() не выдают ошибку компиляции, а pow() делает это без -lm флага?


person ch3ll0v3k    schedule 20.08.2016    source источник
comment
Не используйте идентификаторы с префиксом подчеркивания: stackoverflow.com/questions/38997919/   -  person 2501    schedule 20.08.2016
comment
Обратите внимание, что вам нужно будет присвоить возвращаемые значения этих функций переменной с плавающей запятой, чтобы они могли быть полезны. Они не меняют аргументацию, которую вы передаете.   -  person Weather Vane    schedule 20.08.2016
comment
@ 2501: подчеркивание, за которым следует строчная буква, можно.   -  person Dani    schedule 20.08.2016
comment
@Dani Только для локальных переменных.   -  person melpomene    schedule 20.08.2016
comment
@ Lưu Vĩnh Phúc Вопрос не в том, почему. Я знаю почему.   -  person ch3ll0v3k    schedule 20.08.2016
comment
@ LưuVĩnhPhúc Это не дубликат. Вопрос в том, почему floor и ceil не нужны -lm, в отличие от pow.   -  person melpomene    schedule 20.08.2016
comment
@Weather Vane, я знаю! Для этого образца это не имеет значения.   -  person ch3ll0v3k    schedule 20.08.2016
comment
@ ch3ll0v3k Может иметь значение, если компилятор решит, что, поскольку вы не используете результаты этих вызовов, он может просто их оптимизировать.   -  person melpomene    schedule 20.08.2016
comment
@melpomene Да, и в этом случае вы потенциально можете затенять переменные, что нежелательно.   -  person 2501    schedule 20.08.2016
comment
@ ch3ll0v3k, как указано, единственный способ для читателей узнать, что вы знаете, - это опубликовать MCVE   -  person Weather Vane    schedule 20.08.2016
comment
@melpomene Да, я понял это сразу после публикации вышеупомянутых комментариев. Лучшие дупсы: Необходимо использовать -lm в Clang с pow (), но не sqrt (), log(10.0) может компилироваться, но журнал (0.0) не может?, Почему в некоторых случаях при компиляции и компоновке кода C не требуется -lm?   -  person phuclv    schedule 20.08.2016
comment
также связанная функция sqrt () не работает с переменными аргументами   -  person phuclv    schedule 20.08.2016
comment
... все из ‹math.h› ни один из них не является. Их реализация находится в библиотеке, в системах IX'а обычно внутри libm. math.h просто предоставляет прототипы, чтобы компилятор понимал, о чем говорит ваш код. В конце концов, компоновщик связывает libm, как сообщает параметр -lm.   -  person alk    schedule 20.08.2016


Ответы (3)


Технически все они требуют -lm для работы. На всех их man страницах есть эта строка:

Ссылка на -lm.

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

Вероятно, это связано с тем, что в вашей архитектуре / конфигурации ceil() является встроенной или внутренней функцией, возможно, для этого есть простая инструкция ЦП, поэтому библиотеки не требуется. Но pow() это не так, нужно связывать libm.

ОБНОВЛЕНИЕ: я только что провел несколько экспериментов, и с -O0 все ваши функции требуют -lm, а с -O2 только pow(). Поразмыслив над этим, я нашел файл /usr/include/bits/mathinline.h со встроенными реализациями ceil() и _15 _...

person rodrigo    schedule 20.08.2016
comment
ваша ошибка не компилятора, а ошибка компоновщика - откуда вы знаете? - person melpomene; 20.08.2016
comment
@melpomene: Потому что -lm - это вариант компоновщика. Если бы OP включил актуальное точное сообщение undefined reference to 'pow', это было бы более очевидно, поскольку неопределенные ссылки являются компоновщиком. - person rodrigo; 20.08.2016
comment
@ ch3ll0v3k: Обратите внимание, что <math.h> - это заголовок, а не библиотека. В заголовке объявляется некоторая функциональность, доступная из библиотеки, и при линковке с библиотекой все будет в порядке. В первых примерах компилятор может вычислить результаты так, как он это делает. Он не вычисляет результат для pow() (что слегка удивительно, но полностью его прерогатива), поэтому ему необходимо найти библиотеку во время компоновки. Многие функции находятся в стандартной библиотеке C; математической функции нет в Linux, в соответствии с древней историей Unix (но Mac OS X не нуждается в -lm). - person Jonathan Leffler; 21.08.2016

Компилятор жалуется только на pow(), а не на floor() или ceil(), вероятно, потому что он генерирует встроенный код для floor() и ceil() и внешний вызов для pow(), который не может быть разрешен во время компоновки, потому что вы забыли библиотеку m в командной строке: -lm означает ссылка на libm.a.

Между прочим, поскольку вы не сохраняете возвращаемое значение этих функций, компилятор может использовать свои внутренние знания об этих чистых функциях (каким-то образом переданные в <math.h>), чтобы полностью удалить вызовы. Это могло бы сделать это для ceil() и floor(), но не для pow(), что также объяснило бы наблюдаемое поведение.

Действительно, как можно проверить на http://gcc.goldbolt.org/#, без командной строки варианты, ваш код компилируется как:

main:
        pushq   %rbp
        movq    %rsp, %rbp
        subq    $32, %rsp
        movl    %edi, -20(%rbp)
        movq    %rsi, -32(%rbp)
        movss   .LC0(%rip), %xmm0
        movss   %xmm0, -4(%rbp)
        cvtss2sd        -4(%rbp), %xmm0
        movsd   .LC1(%rip), %xmm1
        call    pow
        movl    $0, %eax
        leave
        ret
.LC0:
        .long   1078529622
.LC1:
        .long   0
        .long   1073741824

По какой-то причине компилятор генерирует встроенный код только для floor и ceil, как и было замечено.

А с -O2 удаляет все:

main:
        xorl    %eax, %eax
        ret

Если вы изменяете код для сохранения значений в глобальных переменных, вызовы библиотек к floor(), ceil() и pow() генерируются без оптимизации, а значения вычисляются компилятором, если вы оптимизируете с -O2.

person chqrlie    schedule 20.08.2016
comment
я действительно включаю ‹math.h›, черт возьми - person ch3ll0v3k; 20.08.2016
comment
@ ch3ll0v3k: Вы не отправляли _1 _... Я не могу догадаться, что у вас есть в вашем файле, поэтому я использовал условное выражение Если вы не включаете ... - person chqrlie; 20.08.2016
comment
Я говорю, если я использую floor (); он компилируется. - person ch3ll0v3k; 20.08.2016
comment
@ ch3ll0v3k: некоторые компиляторы по умолчанию не выдают предупреждений. Тот факт, что он компилируется, мало что доказывает. - person chqrlie; 20.08.2016

Ошибка, которую вы получаете, является ошибкой компоновки, а не ошибкой компиляции.
Floor и ceil могут быть в какой-то другой библиотеке, обычно компилятор не требуется для диагностики отсутствующих заголовков или библиотек.

person Dani    schedule 20.08.2016
comment
Вы получаете ошибку при компоновке, а не при компиляции - откуда вы знаете? - person melpomene; 20.08.2016