🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: nl.comp.programmeren
Article #742

Re: Opbouw grote programma's via opsplitsing source-files

#742
From: Koen Deforche
Date: Mon, 08 Nov 1999 00:00
170 lines
5367 bytes
Bart Vandewoestyne wrote:
> 
> Frans Bouma wrote:
> >
> > > #include "sub.c"
> >
> >     erm... er zijn header files en er zijn codefiles.
> >     geen codefiles includen. maak dan een .h file aan.

Aiai, wat een verwarring in deze nieuwsgroep.

Je wil duidelijk meerdere bestanden, omdat dit handig is. Dit is niet
alleen handig om te editeren maar ook handig om te compileren: een
verandering
aan de implementatie van een functie impliceert niet dat je alles moet
hercompileren: je hoeft enkel de functie (het bestand die de functie
bevat)
te hercompileren, en daarna opnieuw te linken, wat in de praktijk
uiteraard een serieuze tijdswinst oplevert.

Wat is compileren en wat is linken hoor ik je afvragen.

Compileren zet een .c file om in een .o file. Om een file correct
te kunnen compileren is het voldoende dat functies of variabelen
(of kortweg symbolen) die gebruikt worden gedeclareerd zijn.

<terzijde>
(eigenlijk veronderstelt C zelfs dat niet: hij kan overweg met
 'impliciete' declaraties van functies -- maar het is ongezond leven
 om daarop te vertrouwen, ooit zal de compiler een verkeerde
 veronderstelling maken).
</terzijde>

Het declareren gebeurt met dingen zoals:

extern void foo();
extern int i;

struct bla
{
   int koekoek;
};

Dit steek je in de file bla.h:

-- start bla.h --
#ifndef BLA_H_
#define BLA_H_

/* hier nodig omdat we de declaratie van FILE nodig hebben */
#include <stdio.h>

extern void foo(void);
extern int i;
extern FILE *logFile;

struct bla
{
   int koekoek;
};

#endif BLA_H_
-- stop bla.h --

Concreet, je gaat functies (en ook variabelen!) die in de file bla.c
gedefineerd zijn, declareren in een file bla.h. Dus bla.c ziet eruit
als :

-- start bla.c --
/* include van bla.h: eigenlijk onnodig, maar het garandeert
   dat wat je definieert en declareert overeen komt.
   Dit garandeert dus consistentie tussen je verschillende
   files. */
#include "bla.h"

void foo(void)
{
   return;
}

int i;
-- stop bla.c --

en dan is er nog een file alb.c waarin je functies uit bla.h
wil gebruiken:

-- start alb.c --
/* include van bla.h: declaraties van functies die we gaan
   gebruiken */
#include "bla.h"
/* waarschijnlijk gaat alb.c ook dingen exporteren, en die
   staan dan in alb.h */
#include "alb.h"


/* stel dat alb(void) niet in alb.h staat, vermits het een
   private functie is, dan kan je hem zelfs static
   declararen (dat hoeft niet, maar dat kan linktijden
   versnellen en duplicate symbol definities vermijden
    wanneer je twee keer een functie met dezelfde naam
   zou willen definieren maar dan 2maal lokaal in
   verschillende files */

static void alb(void)
{
   if (1)
      bla();
}

int main()
{
   for (;;)
      alb();

   return 0;
}

-- stop alb.c --

Dan nu het compileren:
(1) $ gcc -Wall -ansi -c -o bla.o bla.c
(2) $ gcc -Wall -ansi -c -o alb.o alb.c

En het linken om een executable te krijgen:
(3) $ gcc -o test bla.o alb.o

Stel dat je bla.c wijzigt, dan hoef je enkel (1) en (3) te herhalen,
niettegenstaande dat alb.c de functie bla.c gebruikt.

Stel dat je bla.h wijzigt, bvb, je verandert de declaratie van
de functie bla en je laat die een int doorkijrgen als argument,
dan moeten zowel stappen (1), (2) als (3) herhaalt worden.

Nu komt de kink in de kabel: stel dat je enkel (1) en (2) doet,
maar niet (2), dan heb je problemen: het linken zal werken,
de compiler zal geen fout boodschap geven, maar je programma
bevat een inconsistentie en het zal waarschijnlijk ergens
mank lopen (bus error, segmentation fault, fout gedrag, ...)

Een nachtmerrie voor de programmeur, want hou maar eens bij wat
allemaal van wat afhangt. De oplossing ? make en zijn makefiles.

make gaat aan de hand van veranderde files beslissen welke stappen
opnieuw moeten ondernomen worden om iets te 'maken'.

Een eenvoudige makefile (in de praktijk laat je de makefile
voor een stuk zichzelf maken, met behulp van gcc die je vertelt
welke file afhangt van welke files) voor dit probleem zou zijn:

-- start Makefile --
CFLAGS = -Wall -ansi
CC = gcc
LD = gcc

# om test te maken heb je bla.o en alb.o nodig
# m.a.w. test hangt af van bla.o en alb.o
test: bla.o alb.o
    $(LD) -o $@ $<	   

# om bla.o te maken heb je bla.c en bla.h nodig
# m.a.w. bla.o hangt af van bla.c en bla.h
bla.o: bla.c bla.h

# enzovoorts...
alb.o: alb.c bla.h
-- stop Makefile --

De rest is voor een volgende les :)

koen.
Technical Details
Message-ID: <38270B81.2D1F8D86@easics.be>
Path: arc1-ca.pugleaf.net!unknown!not-for-mail
References: <38249DCC.ABDE8533@skynet.be> <802cvd$i3f$1@news1.xs4all.nl> <38253EA9.1D7A1ADF@skynet.be>

Show Full Headers
Complete Article Headers
X-Google-Language: DUTCH,ASCII-7-bit
X-Google-Thread: 100a19,bea50703bd2c4537
X-Google-Attributes: gid100a19,public
X-Google-Thread: 107324,bea50703bd2c4537
X-Google-Attributes: gid107324,public
X-Google-Thread: fcea4,bea50703bd2c4537
X-Google-Attributes: gidfcea4,public
From: Koen Deforche <koen@easics.be>
Subject: Re: Opbouw grote programma's via opsplitsing source-files
Date: 1999/11/08
Message-ID: <38270B81.2D1F8D86@easics.be>
X-Deja-AN: 545987569
Content-Transfer-Encoding: 7bit
References: <38249DCC.ABDE8533@skynet.be> <802cvd$i3f$1@news1.xs4all.nl> <38253EA9.1D7A1ADF@skynet.be>
X-Accept-Language: en
Content-Type: text/plain; charset=us-ascii
Organization: Easics nv, Leuven, Belgium, http://www.easics.com
Mime-Version: 1.0
Newsgroups: nl.comp.programmeren,be.comp.programming,be.comp.os.linux