Свързване на множество C изходни файлове

Аз съм на gentoo linux с инсталиран GCC 4.4.5. Мога да компилирам и свържа такава програма без никакви грешки, като използвам gcc main.c -o main, а командата ./main връща резултата правилно.

[main.c] 
#include <math.h>
#include <stdio.h>
int main(void)
{
    double c = ceil(2.5);
    printf("The ceil of 2.5 is %f\n", c);
    return 0;
}

Но когато поставя извикването на ceil в друг изходен файл, възниква проблемът.

[calc.h]
#ifndef _CALC_H_
#define _CALC_H_
double myceil(double n);
#endif

[calc.c]
#include <math.h>
#include "calc.h"
double myceil(double n)
{
    return ceil(n);
}

[main1.c]
#include <stdio.h>
#include "calc.h"
int main(void)
{
    double c = myceil(2.5);
    printf("The ceil of 2.5 is %f\n", c);
    return 0;
}

С помощта на командата gcc calc.c main1.c -o main1 възниква такава грешка:

/tmp/cc6GhJvZ.o: In function `myceil':
calc.c:(.text+0x19): undefined reference to `ceil'
collect2: ld returned 1 exit status

Така че защо досадната грешка „недефинирани препратки“ се случи в последния случай? И знам, че грешката може да бъде елиминирана чрез добавяне на библиотека -lm, но просто искам да знам защо gcc ще изведе грешката във втория случай.


person machinarium    schedule 22.08.2011    source източник
comment
Тази грешка не се показва в osx или в RHEL 6 ...   -  person Foo Bah    schedule 22.08.2011
comment
@Foo, наскоро инсталирах rhel-workstation-6.0-i386-dvd.iso на Vmware Workstation 7.0.0, но грешката все още е там.   -  person machinarium    schedule 01.09.2011
comment
оо, използвах x64 сървърното издание   -  person Foo Bah    schedule 02.09.2011


Отговори (3)


Моето предположение е, че GCC оптимизира ceil(2.5) до константа, докато ceil(n) не е константа, защото n не е известно при компилирането на calc.c и трябва да препраща към функцията. Можете да потвърдите това, като погледнете изхода на асемблирането (gcc -S).

Актуализация: Ето какво ми даде gcc 4.2.1 на x86 за нещо подобно на първия ви пример:

.LC1:
    .string "%f\n"
    // [snip]
main:
    // [snip]
    fldl    .LC0
    fstpl   4(%esp)
    movl    $.LC1, (%esp)
    call    printf
    // [snip]
.LC0:
    .long   0
    .long   1074266112

Тук виждаме printf да се извиква с double константа.

Сега, ако направя нещо подобно на вашия втори пример:

myceil:
    // [snip]
    fldl    -8(%ebp)
    fstpl   (%esp)
    call    ceil
    // [snip]

Тук виждаме ceil да се препраща.

Така че, да. Бих казал, че вашето обаждане се оптимизира до константа в това, което работи без -lm.

person asveikau    schedule 22.08.2011
comment
Бихте ли ми казали как да използвам gcc -S конкретно или някои уроци? Благодаря. - person machinarium; 22.08.2011
comment
Примерна употреба: gcc -S foo.c. Това ще изведе файл, наречен foo.s. Разбира се, известно познаване на асемблерния език на вашата платформа също ще ви помогне много. - person asveikau; 22.08.2011
comment
Благодаря ти много. Проблемът е точно в това, което казваш. Когато заменя ceil(2.5) с ceil(x), където стойността на x е 2,5, грешката при препращане възниква точно като моя последен случай. - person machinarium; 22.08.2011
comment
Проверката с -fno-builtin може да представлява интерес, ако си падате по подобни неща (и хей, кой не си пада по това?). - person mu is too short; 22.08.2011

gcc има списък с вградени функции и ceil е един от тях. В моята версия на OSX gcc използва вградения ceil и в двата ви случая, така че -lm не е необходим. Очевидно вашият компилатор Gentoo се държи различно и използва вградения ceil само в някои случаи. Ако опитате да компилирате с -fno-builtin, тогава ще трябва да използвате -lm и за двете си компилации.

person mu is too short    schedule 22.08.2011
comment
Благодаря @mu. Когато използвам gcc -fnobuilt-in main.c -o main, възниква грешка при препращане. Изглежда, че в моя gentoo вградените функции приемат само постоянния параметър, напр. 2.5 в моя предишен случай. - person machinarium; 22.08.2011
comment
@machinarium: Това е страхотно, реших, че също е оптимизирано, но реших да спомена вградените, тъй като те могат да причинят известно объркващо поведение. Може да е и проблемът с множеството файлове или нещо в конфигурацията на компилатора. - person mu is too short; 22.08.2011

Работи ли, ако първо компилирате main.c към main.o и calc.c към calc.o, след което ги свържете? Обикновено това е, което бих очаквал (свързване на обектни файлове, вместо да се опитвам да компилирам множество C файлове на един команден ред).

person Vatine    schedule 22.08.2011
comment
Благодаря за вашия отговор. Използвам следните команди gcc -c calc.c gcc -c main1.c и gcc -o main1 calc.o main1.o, но същата грешка все още се появява. - person machinarium; 22.08.2011