Сначала слово о ваших 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 $@ $<
Пока все хорошо.Если это ясно, то мы можем перейти к заголовочным файлам ...