Подтвердить что ты не робот

log (10.0) может компилироваться, но log (0.0) не может с неопределенной ссылкой?

Для следующего исходного кода C:

#include <math.h>

int main(void)
{
    double          x;

    x = log(0.0);

    return 0;
}

Когда я компилирую с gcc -lm, я получил:

/tmp/ccxxANVH.o: In function `main':
a.c:(.text+0xd): undefined reference to `log'
collect2: error: ld returned 1 exit status

Но если я заменил log(0.0) на log(10.0), он может скомпилироваться успешно.

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

На всякий случай, мой gcc -v вывод:

Configured with: ../src/configure -v --with-pkgversion='Ubuntu 4.8.2-19ubuntu1' --with-bugurl=file:///usr/share/doc/gcc-4.8/README.Bugs --enable-languages=c,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.8 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.8 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --disable-libmudflap --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-4.8-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.8.2 (Ubuntu 4.8.2-19ubuntu1)

Обратите внимание, что этот вопрос касается постоянной сгибания, но предлагаемый дублирующий вопрос касается недостающей библиотеки ссылок.

4b9b3361

Ответ 1

gcc во многих случаях может использовать встроенные функции, в их документации говорится:

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

поэтому gcc не будет связываться с математической библиотекой при использовании встроенной функции, но поскольку log(0) не определен он, вероятно, заставляет gcc оценивать его во время выполнения, так как он имеет побочный эффект.

Если мы рассмотрим черновик стандарта C99 раздел 7.12.1 Обработка условий ошибки в пункте 4 гласит (акцент мой):

Плавный результат переполняется, если величина математического результат конечен, но настолько велик, что математический результат не может быть представленный без чрезвычайной ошибки округления в объекте указанного типа. Если поток с плавающим результатом и округление по умолчанию в действительности, или если математический результат является точной бесконечностью из конечные аргументы (например, log (0.0)), то функция возвращает значение макроса HUGE_VAL, HUGE_VALF или HUGE_VALL в соответствии с тип возврата, с тем же знаком, что и правильное значение функции; , если целочисленное выражение math_errhandling и MATH_ERRNO отличны от нуля, целочисленное выражение errno приобретает значение ERANGE;, если целое число выражение math_errhandling и MATH_ERREXCEPT отличны от нуля, '' Исключение с плавающей запятой деление на ноль возникает, если математический результат - точная бесконечность, а переполнение исключение с плавающей запятой создается иначе.

Мы можем увидеть из живого примера, используя флаг -S для генерации сборки и grep log для фильтрации вызовов на log.

В случае log(0.0) создается следующая инструкция (видеть ее в прямом эфире):

call    log

но в случае команды log(10.0) no call log генерируется команда (видеть ее в прямом эфире).

Обычно мы можем запретить gcc использовать встроенную функцию с помощью - fno-builtin flag, который, вероятно, является более быстрым способом тестирования используется ли встроенный модуль.

Обратите внимание, что -lm должен идти после исходного файла, например (взято из связанного ответа), если main.c требуется математическая библиотека, тогда вы будете использовать

 gcc main.c -lm 

Ответ 2

Компиляция в порядке, это просто отсутствует компоновщик -lm.

Вторая версия, вероятно, компилируется и связывается, потому что gcc заменяет log(10.0) константой, поэтому вызов математической библиотеки не требуется. Во втором случае результат математически undefined, и оценка приводит к ошибке домена. В этом случае выражение не может быть заменено константой, так как обработка ошибок домена может отличаться во время выполнения.

Цитата из C-стандарта (черновик):

При ошибке домена функция возвращает определенный реализацией стоимость; если целочисленное выражение math_errhandling и MATH_ERRNO отличное от нуля, целочисленное выражение errno приобретает значение EDOM; если целочисленное выражение math_errhandling и MATH_ERREXCEPT отличны от нуля, '' Недопустимое исключение с плавающей запятой.

Таким образом, оценка log(0.0) приводит к возврату значения HUGE_VAL (не NAN, как я утверждал ранее), или исключение с плавающей запятой.

EDIT: я исправил свой ответ на основе полученных комментариев и добавил ссылку на описание в стандарте C.