Выяснение составления нескольких файлов с помощью make & C - PullRequest
1 голос
/ 05 октября 2011

Для проекта я работал с партнером, пишущим реализацию дерева решений.Поскольку мы оба являемся относительными новичками в C и должны были работать быстро, мы в основном свалили все функции в один файл, который в итоге составил более 1600 строк.Это был быстрый и грязный проект, чтобы начать работу, но теперь в следующем задании мы отвечаем за расширение и повторную реализацию кода.В текущем состоянии этого не произойдет.

Сейчас я разбиваю исходный источник на основе ответственности функций.Дело в том, что многие функции переплетены, и я получаю серьезные ошибки с моим файлом make.Более конкретно, другие исходные файлы сообщают о неявном объявлении функций, которые объявлены в отдельном файле.

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

cc= gcc
CFLAGS= -g -Wall -lm

proj2: main.o split.o tree.o id3.o output.o 
  $(CC) $(CFLAGS) -o proj2 main.o split.o tree.o id3.o output.o

Я также пробовал предыдущую версию, где каждый объектный файл былскомпилирован отдельно, как

main.o: main.c split.c tree.c id3.c output.c
  $(CC) $(CFLAGS) -o main.c split.c tree.c id3.c output.c

, и это повторилось, чтобы создать файл .o для каждого источника, который затем был скомпилирован в исполняемый файл.

Однако это не сработало, и я получилоколо 500 строк жалоб и предупреждений компилятора, в основном о неявных объявлениях функций.

Итак, по сути, у меня есть два связанных вопроса:

  • возможно ли переплетение вызовов функций между различными исходными файлами?
  • если да, то как я могу сделать это здесь возможным?

Ответы [ 2 ]

5 голосов
/ 05 октября 2011

Сначала слово о ваших make-файлах.

proj2: main.o split.o tree.o id3.o output.o 
  $(CC) $(CFLAGS) -o proj2 main.o split.o tree.o id3.o output.o

Это должно работать (если код верен), но если вы используете GNUMake (что вам следует), вы можете привести его в порядок:

proj2: main.o split.o tree.o id3.o output.o 
  $(CC) $(CFLAGS) -o $@ $^

Теперь у вас есть только одна копия списка объектов для обслуживания.

Другая версия неверна:

main.o: main.c split.c tree.c id3.c output.c
  $(CC) $(CFLAGS) -o main.c split.c tree.c id3.c output.c

Сначала вы пытаетесь скомпилировать все исходных файлов в один объектный файл, что отрицательно сказывается на назначении объектных файлов.Во-вторых, вы называете свой один объектный файл main.o, когда это имя действительно должно принадлежать объектному файлу, сделанному из main.cc.В-третьих, команда указывает компилятору скомпилировать все другие исходные файлы (split.c, tree.c, ...) в объектный файл с именем "main.c" - не незаконно, новы непременно должны подняться.

Кроме того, вы должны попытаться использовать C ++, а не C, но это на другой день.

Теперь для того, чтобы разбить Большой шарГрязь .Я предполагаю, что вы знаете, как разбить большие функции на более мелкие, поэтому проблема заключается в разделении функций на разные исходные файлы (а затем их правильной компиляции и компоновке).Предположим, что main() вызывает функцию foo():

/* main.c */

void foo()
{
  // do foo things
}

int main()
{
  // do main things
  foo();
  return(0);
}

Как вы знаете, foo должен идти первым, в противном случае компилятор будет блокироваться, когда main попытается вызвать необъявленную функцию.Но мы можем объявить foo заранее:

/* main.c */

void foo();

int main()
{
  // do main things
  foo();
  return(0);
}

void foo()
{
  // do foo things
}

Когда компилятор достигает вызова foo(), он уже знает, что такая функция существует, и доверяет нам определить ее позже.Теперь вот хитрость: если мы дадим указание компилятору компилировать, а не ссылаться (то есть создавать объектный файл, такой как main.o, а не исполняемый файл, такой как proj2), он будет доверять нам еще дальше:

/* main.c */

void foo();

int main()
{
  // do main things
  foo();
  return(0);
}

Это очень хорошо скомпилируется в main.o.Компилятор доверяет нам предоставить определение void foo() в каком-то другом объектном файле, когда мы связываем вещи вместе в исполняемый файл.Определение будет в другом файле, например:

/* foo.c */

void foo()
{
  // do foo things
}

Мы могли бы построить это вручную:

gcc -g -Wall -lm -c foo.c -o foo.o
gcc -g -Wall -lm -c main.c -o main.o
gcc -g -Wall -lm foo.o main.o -o proj2

Но это быстро становится утомительным, поэтому мы напишем make-файл:

cc= gcc
CFLAGS= -g -Wall -lm

proj2: main.o foo.o 
  $(CC) $(CFLAGS) -o $@ $^

%.o: %.c
  $(CC) $(CFLAGS) -c -o $@ $<

Пока все хорошо.Если это ясно, то мы можем перейти к заголовочным файлам ...

1 голос
/ 05 октября 2011

Вам нужно создать заголовочные файлы для каждого исходного кода, чтобы в них были объявления. Затем вы #include соответствующие заголовочные файлы в верхней части исходного кода.

...