несколько определений main -> как добавить только некоторые функции из другого заголовка?

#c #linker

#c #компоновщик

Вопрос:

В C я получаю ошибку компоновщика множественное определение `main’. Да, это верно, но:

Почему компоновщик пытается включить вторую функцию main (ext.c), хотя я только что включил заголовок ext.h? Я ожидаю, что компоновщик связывает только те функции, прототипы которых были найдены или которые необходимы первоначальному main?

Как я могу решить это, чтобы a) тест компилировался и связывался без проблем (просто используйте func () из ext.c) и b) также ext.c можно скомпилировать и связать как отдельное приложение?

(Пример) кода:

 //file: test.c
#include "/home/stefanm/test/test.h"

void main (int argc, char * argv[])
{
    uint8_t var = 123;
    printf ("main(): var= %in", var);
    func (var);
                                                                                                                    }
  
 //file: test.h
#ifndef TEST_H
#define TEST_H
#include <the rest>
#include "/home/stefanm/test/ext.h"                                                                                                                     
#endif
  

… и внешний модуль:

 //file: ext.c
#include "/home/stefanm/test/ext.h"
uint8_t func (uint8_t i){    
    printf ("func(): Variable i is %i", i); 
    return 0;
}

void main () {
    printf ("ext main func");
}   
  
 //file: ext.h
#ifndef EXT_H
#define EXT_H
#include "all needed headers"  

uint8_t func (uint8_t);
#endif    
  

Я вызываю компилятор с gcc test.c ext.c -o test

Комментарии:

1. Нет, вы думаете неправильно. Компилятор создаст символ для каждой найденной им функции (как правило, если она не встроена и / или не оптимизирована), а затем компоновщик попытается разрешить эти символы, и ему не понравятся дубликаты.

2. Какую команду вы используете для компиляции этого? Если вы скомпилируете только один из этих .c файлов, компилятор найдет только одну main функцию.

3. В дополнение к данному ответу я хотел бы отметить, что разделение тестируемого кода и конечного исполняемого файла почти всегда является хорошей идеей, особенно если у вас есть зависимости только для тестирования от внешних библиотек.

4. Кроме того, возвращаемое значение main должно быть int

Ответ №1:

Ваш внешний модуль не должен иметь main() , потому что это модуль, а не приложение. Вам следует просто переместить main() из вашего модуля в отдельный файл:

 //file: app.c
#include "/home/stefanm/test/ext.h" // <-- BTW, using absolute paths is not a good idea

void main () {
    //use function from ext here
    printf ("app main func");
}
  

А затем скомпилируйте ваше приложение следующим образом:

 gcc app.c ext.c
  

и ваш тест, подобный этому:

 gcc test.c ext.c
  

Комментарии:

1. Похоже, это правильный путь. Это идеально вписывается в мой проект (этот поток был просто примером, чтобы показать мою проблему). И абсолютный путь и void main() были просто результатом быстрого и грязного примера. Кстати, это решение легко вписывается в makefile, который является частью моего проекта.

Ответ №2:

В C у вас может быть только одно определение функции во всех файлах, которые вы ссылаете в свой исполняемый файл. Нет хорошего способа сообщить компилятору «Я хочу использовать это main() , а не все остальные». (Есть плохой способ, используя макросы, но это было бы грязно).

Если вы хотите использовать функцию с двумя разными main() функциями, поместите ее в отдельный файл.

Ответ №3:

Я полагаю, что ваш вызов compile / link выглядит следующим образом

 gcc test.c ext.c
  

В этом случае test.c и ext.c (соответственно, если быть точным, файлы .o, созданные на их основе) являются одноранговыми, то есть находятся на одном уровне. Как компоновщику узнать, какую версию символа main использовать, а какую отбросить? Компоновщик не знает об используемых файлах включения.

В случае функции main правильный путь — иметь в вашем проекте ровно одну из них.

Для любой другой функции, где у вас есть это требование, есть несколько способов:

Либо вы могли бы объявить одну из них «слабой». Оно будет отброшено, когда появится «сильное».

Или вы помещаете свою функцию в библиотеку, например libext.a . Если вы свяжете это с -ext , из нее будут удалены только объектные файлы, которые определяют символы, которые не определены. Но опять же, могут возникнуть конфликты имен, если другое имя, определенное этим объектным файлом, уже определено. Поэтому лучше всего определять как можно меньше символов для каждого объектного файла.

Комментарии:

1. Нет, совершенно определенно не следует объявлять main функцию как слабый символ.

2. @SergeyA Итак, путь к созданию библиотеки? Это то, что я могу автоматически выполнить в makefile? «Реальный» проект, над которым я работаю, намного сложнее, и я скорее разработчик оборудования, которому приходится добавлять функциональность во встроенную систему. Не могли бы вы описать процесс создания a .a немного подробнее?

3. @glglgl Помещение ext.o в библиотеку, как есть, не предотвратит ошибку множественного определения в Unix (возможно, это происходит в Windows, я не знаю). Компоновщик Unix извлекает целые объектные файлы из .a библиотеки, поэтому, когда test.o ссылается на func , и эта ссылка удовлетворена из ext.o , это также приведет к ext.o определению main .

4. @SergeyA За исключением случаев, когда вы экспериментируете и тестируете пределы своих инструментов. Но в основном вы правы, поэтому я перефразировал свой ответ.