数学库中的文字常量与变量

Gra*_*Guy 5 c linker

所以,我知道C你需要将代码链接到数学库libm,以便能够使用它的功能.今天,当我试图向朋友证明这一点,并解释为什么你需要这样做时,我遇到了以下我不理解的情况.

请考虑以下代码:

#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;
}
Run Code Online (Sandbox Code Playgroud)

如果VARIABLE已定义,则需要libm按照通常的预期进行链接; 否则你得到通常的main.c:(.text+0x29): undefined reference to sqrt链接错误,表明编译器找不到该函数的定义sqrt.我很惊讶地看到,如果我评论#define VARIABLE,代码运行正常,结果是正确的!

为什么libm在使用变量时我需要链接到但是在使用文字常量时我不需要这样做?编译器如何找到sqrt未链接库的定义?我gcc 4.4.5在linux下使用.

Oli*_*rth 5

GCC可以为多个标准库函数进行常量折叠.显然,如果函数在编译时折叠,则不需要运行时函数调用,因此不需要链接到libm.您可以通过查看编译器生成的汇编程序(使用objdump或类似)来确认这一点.

我猜这些优化仅在参数是常量表达式时触发.


Mys*_*ial 4

正如每个人都提到的,是的,这与不断折叠有关。

关闭优化后sqrt(2.0),GCC 似乎仅在使用时才执行此操作。证据如下:

情况1:有变量。

    .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
Run Code Online (Sandbox Code Playgroud)

您可以看到它发出了对该sqrt函数的调用。因此,如果不链接数学库,则会出现链接器错误。

情况 2:使用文字。

    .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
Run Code Online (Sandbox Code Playgroud)

没有人打电话给sqrt。因此没有链接器错误。


通过对 的优化,GCC 将在这两种情况下进行恒定传播。所以在任何一种情况下都没有链接器错误。

$ 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
$ 
Run Code Online (Sandbox Code Playgroud)