So, I know that in C
you need to link the code to the math library, libm
, to be able to use its functions. Today, while I was trying to demonstrate this to a friend, and explain why you need to do this, I came across the following situation that I do not understand.
Consider the following code:
#include <math.h>
#include <stdio.h>
/* #define VARIABLE */
int main(void)
{
#ifdef VARIABLE
double a = 2.0;
double b = sqrt(a);
printf("b = %lf\n",b);
#else
double b = sqrt(2.0);
printf("b = %lf\n",b);
#endif
return 0;
}
If VARIABLE
is defined, you need to link against libm
as you would normally expect; otherwise you get the usual main.c:(.text+0x29): undefined reference to sqrt
linking error indicating that the compiler cannot find the definition for the function sqrt
. I was surprised to see that if I comment #define VARIABLE
, the code runs fine and the result is correct!
Why is it that I need to link to libm
when variables are used but I don't need to do so when literal constants are used? How does the compiler find the definition of sqrt
when the library is not linked? I'm using gcc 4.4.5
under linux.
Variables are mutable, i.e., their values can be changed and updated. Constants are immutable, i.e. their values cannot be changed or updated. Literals are both mutable or immutable depending on the type of literal used. Here, 'Hello' is literal.
A constant does not change over time and has a fixed value. For example, the size of a shoe or cloth or any apparel will not change at any point. In an algebraic expression, x+y = 8, 8 is a constant value, and it cannot be changed. Variables: Variables are the terms which can change or vary over time.
A literal is notation for representing a fixed ( const ) value. A variable is storage location associated with a symbolic name (pointed to, if you'd like). Identifier on the other hand is the name assigned to a variable in a python statement.
Literal constants let you define and refer to data directly in machine instruction operands. You do not need to define a constant separately in another part of your source module. The differences between a literal, a data constant, and a self-defining term are described in Literals.
GCC can do constant folding for several standard-library functions. Obviously, if the function is folded at compile-time, there is no need for a run-time function call, so no need to link to libm. You could confirm this by taking a looking at the assembler that the compiler produces (using objdump
or similar).
I guess these optimizations are only triggered when the argument is a constant expression.
As everyone mentions, yes it has to do with constant folding.
With optimizations off, GCC only seems to do it when sqrt(2.0)
is used. Here's the evidence:
Case 1: With the variable.
.file "main.c"
.section .rodata
.LC1:
.string "b = %lf\n"
.text
.globl main
.type main, @function
main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $32, %esp
fldl .LC0
fstpl 24(%esp)
fldl 24(%esp)
fsqrt
fucom %st(0)
fnstsw %ax
sahf
jp .L5
je .L2
fstp %st(0)
jmp .L4
.L5:
fstp %st(0)
.L4:
fldl 24(%esp)
fstpl (%esp)
call sqrt
.L2:
fstpl 16(%esp)
movl $.LC1, %eax
fldl 16(%esp)
fstpl 4(%esp)
movl %eax, (%esp)
call printf
movl $0, %eax
leave
ret
.size main, .-main
.section .rodata
.align 8
.LC0:
.long 0
.long 1073741824
.ident "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
.section .note.GNU-stack,"",@progbits
You can see that it emits a call to the sqrt
function. So you'll get a linker error if you don't link the math library.
Case 2: With the Literal.
.file "main.c"
.section .rodata
.LC1:
.string "b = %lf\n"
.text
.globl main
.type main, @function
main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $32, %esp
fldl .LC0
fstpl 24(%esp)
movl $.LC1, %eax
fldl 24(%esp)
fstpl 4(%esp)
movl %eax, (%esp)
call printf
movl $0, %eax
leave
ret
.size main, .-main
.section .rodata
.align 8
.LC0:
.long 1719614413
.long 1073127582
.ident "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
.section .note.GNU-stack,"",@progbits
There's no call to sqrt
. Hence no linker error.
With optimizations on, GCC will do constant propagation in both cases. So no linker error in either case.
$ gcc main.c -save-temps
main.o: In function `main':
main.c:(.text+0x30): undefined reference to `sqrt'
collect2: ld returned 1 exit status
$ gcc main.c -save-temps -O2
$
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With