2

I am coding under GNU/Linux Debian 8.5

I have a simple program.

If I compile this with gcc prog.c it is OK!

#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;
}

Bud if I add pow(), it says that it cannot find pow and I need to add gcc prog.c -lm to make it right.

#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;
}

If I am right, the pow(), ceil(), floor() are all from <math.h>?

So why don't floor() and ceil() throw a compilation error, and pow() does, without -lm flag?

alk
  • 69,737
  • 10
  • 105
  • 255
ch3ll0v3k
  • 336
  • 3
  • 9
  • 1
    Don't use identifiers with an underscore prefix: https://stackoverflow.com/questions/38997919/is-it-permissible-for-global-static-identifiers-to-begin-with-a-single/38998123#38998123 – 2501 Aug 20 '16 at 16:59
  • Note that you will need to assign the return values from those functions to a floating point variable for them to be of any use. They do not alter the argument you pass. – Weather Vane Aug 20 '16 at 17:03
  • 1
    @2501: underscore followed by a lowercase letter is ok. – Daniel Aug 20 '16 at 17:04
  • [why the -lm flag is needed to link the math library?](http://stackoverflow.com/q/4606301/995714), [Why do you need an explicit `-lm` compiler option](http://stackoverflow.com/q/10371647/995714) – phuclv Aug 20 '16 at 17:05
  • 1
    @Dani Only for local variables. – melpomene Aug 20 '16 at 17:05
  • @Lưu Vĩnh Phúc The question is not "Why". I know why. – ch3ll0v3k Aug 20 '16 at 17:08
  • 1
    @LưuVĩnhPhúc That's not a duplicate. The question is why `floor` and `ceil` don't need `-lm`, unlike `pow`. – melpomene Aug 20 '16 at 17:08
  • @Weather Vane i know! It does not matter for this sample. – ch3ll0v3k Aug 20 '16 at 17:09
  • @ch3ll0v3k It may matter if the compiler decides that since you don't use the results of these calls, it can just optimize them out. – melpomene Aug 20 '16 at 17:10
  • @melpomene Yes, and in that case you potentially shadow variables, which is not desired. – 2501 Aug 20 '16 at 17:12
  • 1
    @ch3ll0v3k as stated, the only way for readers to know what you know is to post the [MCVE](http://stackoverflow.com/help/mcve) – Weather Vane Aug 20 '16 at 17:13
  • 2
    @melpomene Yes I realized that just after posting the above comments. Better dups: [Need to use -lm in Clang with pow(), but not sqrt()](http://stackoverflow.com/q/31974449/995714), [log(10.0) can compile but log(0.0) cannot?](http://stackoverflow.com/q/24294578/995714), [Why is -lm not necessary in some cases when compiling and linking C code?](http://stackoverflow.com/q/19486738/995714) – phuclv Aug 20 '16 at 17:15
  • also related [sqrt() function not working with variable arguments](http://stackoverflow.com/q/3533594/995714) – phuclv Aug 20 '16 at 17:21
  • 2
    "*...are all from *" none of them is. Their implementation is in a library, on IX'ish systems typically inside `libm`. `math.h` just gives prototypes, so the compiler understands what your code is "talking" about. The linker finally links `libm`, as told by the `-lm` option. – alk Aug 20 '16 at 17:24

3 Answers3

6

Technically all of them require -lm to work. All of their man pages include this line:

Link with -lm.

But yours is not a compiler error but a linker error, that is your program compiles fine, but then when linking if you don't use -lm it is unable to find the implementation of pow() but it actually finds the implementation of ceil().

That is probably because in your architecture/configuration ceil() is an inline or intrinsic function, maybe there is a simple CPU instruction to do it, so no library is necessariy. But pow() is not so you need to link libm.

UPDATE: I've just made some experiments, and with -O0 all your functions require the -lm but with -O2 only pow(). Tinkering about that I found the file /usr/include/bits/mathinline.h with the inline implementations of ceil() and floor()...

rodrigo
  • 94,151
  • 12
  • 143
  • 190
  • "*yours is not a compiler error but a linker error*" - how do you know? – melpomene Aug 20 '16 at 17:04
  • 1
    @melpomene: Because `-lm` is a linker option. If the OP had included the actuall exact message, `undefined reference to 'pow'` then it would have been more obvious, as undefined references are linker stuff. – rodrigo Aug 20 '16 at 17:06
  • @ch3ll0v3k: Note that `` is a header, not a library. The header declares some functionality that is available from a library, and when linking with the library, all will be fine. In the first examples, the compiler can compute the results so it does. It doesn't compute the result for `pow()` (which is mildly surprising, but entirely its prerogative), so it needs to find the library at link time. Many functions are in the standard C library; the maths function are not on Linux, in line with ancient Unix history (but Mac OS X doesn't need `-lm`). – Jonathan Leffler Aug 20 '16 at 22:31
3

The compiler only complains about pow() and not floor() or ceil() probably because it generates inline code for floor() and ceil() and an external call for pow() which cannot be resolved at link time because you forgot the m library on the command line: -lm stands for link with libm.a.

Incidentally, since you do not store the return value of these functions, the compiler might use its intrinsic knowledge of these pure functions (conveyed in some way in <math.h>) to remove the calls altogether. It might do that for ceil() and floor() and not for pow(), which would also explain the observed behavior.

Indeed, as can be verified on http://gcc.goldbolt.org/#, with no command line options, your code compiles as:

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

For some reason, the compiler generates inline code only for floor and ceil, as observed.

While with -O2 it removes everything:

main:
        xorl    %eax, %eax
        ret

If you modify the code to store the values into global variables, library calls to floor(), ceil() and pow() are generated without optimization and the values are computed by the compiler if you optimize with -O2.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
1

The error you get is a linking error, not a compilation error.
Floor and ceil might be in some other library, generally the compiler is not required to diagnose missing headers or libraries.

Daniel
  • 30,896
  • 18
  • 85
  • 139