Kompilacja pliku bez funkcji main lub linkowanie dwóch plików z funkcją main

0

Mam problem. Miałem zrobić 7 funkcji dotyczących obróbki tekstu i zrobiłem. Oto fragment polecenia na studiach co mam zrobić dalej:
"
Deklaracje funkcji umieścić w pliku nagłówkowym o nazwie np. funTabTekst.h.

Definicje (implementacje) funkcji umieścić w pliku źródłowym o nazwie np. funTabTekst.c.

Funkcję main() zaimplementować w oddzielnym pliku, np. tekst.c, a powyżej jej definicji włączyć powyższy plik nagłówkowy deklaracją: #include"funTabTekst.h" Tutaj nazwę pliku nagłówkowego umieszczamy pomiędzy znakami " "a nie znakami < >, co oznacza, że plik nagłówkowy będzie szukany w bieżącym katalogu, a nie katalogu ustawionym domyślnie w danym systemie.
Skompilować każdy plik źródłowy(z rozszerzeniem .c) oddzielnie, a potem zlinkować je razem, np. $gcc -o tekst.x tekst.o funTabTekst.o
"

  1. Każą nam zrobić funkcję main w pliku tekst.c.
  2. W pliku funTabTekst.c mam tylko definicje funkcji (bez funkcji main). Jednak mówią, żeby każdy plik źródłowy skompilować oddzielnie! Jak mam to zrobić skoro nie mam funkcji main w funTabTekst.c. Dobra nawet jak ją dodam do pliku i skompiluję to będę miał kolejny problem, bo gdy zlinkuję
    gcc -o tekst.x tekst.o funTabTekst.o to będę miał dwie funkcje main i błąd
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o: In function `_start':
(.text+0x20): undefined reference to `main'
collect2: error: ld returned 1 exit status

Dlaczego oni się upierają z tym oddzielnym kompilowaniem każdego pliku źródłowego, a nie po prostu
gcc -o tekst.x funTabTekst.c tekst.c

2

gcc -c funTabTekst.c -o funTabTekst.o
gcc tekst.c funTabTekst.o -o tekst

Zrób sobie makefile to będzie przyjemniej i się czegoś nauczysz.

Co do powodu: podział istnieje aby nie wykonywać zbędnej ponownej kompilacji jeśli w pliku nie dokonano żadnych zmian. W większych (a nawet średnich) projektach to ma znaczenie.

0
kq napisał(a):

gcc -c funTabTekst.c -o funTabTekst.o
gcc tekst.c funTabTekst.o -o tekst

Zrób sobie makefile to będzie przyjemniej i się czegoś nauczysz.

Co do powodu: podział istnieje aby nie wykonywać zbędnej ponownej kompilacji jeśli w pliku nie dokonano żadnych zmian. W większych (a nawet średnich) projektach to ma znaczenie.
Tylko czy jest to możliwe do zrobienia w tym przypadku, gdy linkuje dwa pliki z funkcją main lub ewentualnie staram się skompilować plik bez funkcji main.

Zrobiłem, odpaliłem, nie działa (dałem tekstA, zamiast tekst, bo robię na razie do podpunktu A)

funTabTekst.o: funTabTekst.c
	gcc -c funTabTekst.c -o funTabTekst.o

tekstA.x: tekstA.c funTabTekst.o
	gcc -c tekstA.c funTabTekst.o -o tekstA.x

jak już zrobiłem plik o nazwie makefile, to go odpaliłem wpisując po prostu make. Pliku tekstA.x nie ma

1

Co to znaczy "nie działa"? Jak nie umiesz opisać to wklej log z konsoli.

W drugim przypadku -c jest zbyteczne, bo chcesz także linkować.

0

Ok już wiem

2
%.o: %.c
    gcc -c $< -o $@

tekstA.x: tekstA.o funTabTekst.o
    gcc $^ -o $@
1

Oddzielna kompilacja pozwala np. kompilować plik z różnymi parametrami. W Makefile warto stosować zmienne, np:

CC = gcc
CFLAGS = -Wall -Wextra -Wpedantic
LDFLAGS = -s -L.
LDLIBS = -ltools -lm

main: main.o libtools.a
        $(CC) -o main main.o $(LDFLAGS) $(LDLIBS)

main.o: main.cpp tools.h
        $(CC) $(CFLAGS) -c main.cpp

libtools.a: lib1.o
        ar cr libtools.a lib1.o

lib1.o: lib1.cpp
        $(CC) $(CFLAGS) -c lib1.cpp
clean:
        rm -f main.exe libtools.a *.o *.s *.i
4

Jak jest dużo plików .c, zamiast pisać regułę dla każdego można w osobnej zmiennej trzymać ich listę, a potem operować tylko na wildcardach:

CFILES = foo.c bar.c
CFLAGS =
LFLAGS =
EXE = foobar.exe

%.o: %.c
	$(CC) -c $^ $(CFLAGS)

COBJS = $(CFILES:%.c=%.o)

$(EXE): $(COBJS)
	gcc $^ -o $@ $(LFLAGS)

default: all

all: $(EXE)

clean:
	rm $(EXE) $(COBJS)

.PHONY: default all clean

w ten sposób interesuje nas właściwie tylko kilka pierwszych linijek, cała reszta to już automatyka.

ALE: taki makefile (oraz wszystkie inne pokazane w tym wątku) w ogóle nie bierze pod uwagę plików nagłówkowych należacych do projektu – czyli zmiana w pliku .h może pozostać niezauważona (nie będą przekompilowane używające go pliki .c).
Da się to jednak naprawić w sposób automatyczny:

CFILES = foo.c bar.c
CFLAGS =
LFLAGS =
EXE = foobar.exe

%.o: %.c
	$(CC) -c $^ $(CFLAGS) -MMD -MP

OBJS = $(CFILES:%.c=%.o)

$(EXE): $(OBJS)
	gcc $^ -o $@ $(LFLAGS)

default: all

all: $(EXE)

DEPS = $(CFILES:%.c=%.d)

clean:
	rm $(EXE) $(OBJS) $(DEPS)

.PHONY: default all clean

-include $(DEPS)

Do parametrów gcc dochodzi -MMD -MP (nie chcę zaśmiecać tym CFLAGS, bo to parametry bardziej związane z działaniem makefile'a niż efektem kompilacji). To spowoduje generowanie dodatkowego mini-makefile'a z rozszerzeniem .d (dependency) dla każdego kompilowanego .c. pliki te są dołączane na końcu makefile'a. Teraz zmiana w jakimś pliku .h (lokalnym w projekcie, nie mówimy o systemowych) spowoduje przekompilowanie wszystkich .c które go używają.

1 użytkowników online, w tym zalogowanych: 0, gości: 1