Функция main в статической библиотеке

Собираю статическую библиотеку, в которую попадает функция main, находящаяся в одном из исходных файлов. Вопрос в следующем: почему при сборке другого исходного файлов с функцией main и библиотекой не возникает ошибка компиляции из-за множественного определения?


Ответы (1 шт):

Автор решения: Stanislav Volodarskiy

Ошибки компиляции быть не может, в каждой единице трансляции только одно определение функции main что совершенно законно. Вопросы возникают во время компоновки. А компоновщик работает просто: если в коде остался не разрешенный символ, он просматривает доступные библиотеки и добавляет первое найденное определение. В результате получается программа на C которая не содержит неопределённого поведения - определения всех функций на этапе компоновки разрешаются однозначно.

NB: Cтандарт считает сборку с множественными определениями неопределённым поведением. То что в моих примерах и у вас получаются работающие программы - определяется тем как работает конкретный компоновщик в конкретной операционной системе. В нормальной разработке такого быть не должно.

Иллюстрация:

// f1.c --------------------------------------
int f() { return 1; }


// f2.c --------------------------------------
int f() { return 2; }


// main.c ------------------------------------    
#include <stdio.h>

int f();

int main() {
    printf("%d\n", f());
}

Подготовка:

$ ls
f1.c  f2.c  main.c

$ gcc -c -o f1.o f1.c
$ ar r libf1.a f1.o

$ gcc -c -o f2.o f2.c
$ ar r libf2.a f2.o

$ gcc -c -o main.o main.c

$ ls
f1.c  f1.o  f2.c  f2.o  libf1.a  libf2.a  main.c  main.o

Результат зависит от порядка библиотек в командной строке. Компоновщик выбирает первый подходящий символ не проверяя его уникальность:

$ gcc main.o -L. -lf1 -lf2
$ ./a.out
1

$ gcc main.o -L. -lf2 -lf1
$ ./a.out
2

Ваша ситуация другая: функция main уже определена в одном из объектных файлов и в библиотеке. Проделаем тоже самое для функции f. Компоновщик не заглядывает в libf2.a - нет необходимости:

$ gcc main.o f1.o -L. -lf2
$ ./a.out
1

Ошибка возникнет только если оба определения будут в объектных файлах, не в библиотеках. Такие символы компоновщик обязан собрать все:

$ gcc main.o f1.o f2.o
f2.o: In function `f':
f2.c:(.text+0x0): multiple definition of `f'
f1.o:f1.c:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status

P.S. Fat-Zer подсказал простой способ диагностировать множественные определения в библиотеках. Пара ключей -Wl,-whole-archive/-Wl,-no-whole-archive вокруг библиотек заставляет компоновщик включать все символы из библиотек (не только необходимые):

$ gcc main.o -L. -Wl,-whole-archive -lf1 -lf2 -Wl,-no-whole-archive
./libf2.a(f2.o): In function `f':
f2.c:(.text+0x0): multiple definition of `f'
./libf1.a(f1.o):f1.c:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status
→ Ссылка