Функция main в статической библиотеке
Собираю статическую библиотеку, в которую попадает функция main, находящаяся в одном из исходных файлов. Вопрос в следующем: почему при сборке другого исходного файлов с функцией main и библиотекой не возникает ошибка компиляции из-за множественного определения?
Ответы (1 шт):
Ошибки компиляции быть не может, в каждой единице трансляции только одно определение функции 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