Hur ska man strukturera ett projekt? (arduino, C)

C, C++, Pascal, Assembly, Raspberry, Java, Matlab, Python, BASIC, SQL, PHP, etc.
Användarvisningsbild
adent
Inlägg: 4094
Blev medlem: 27 november 2008, 22:56:23
Ort: Utanför Jönköping
Kontakt:

Re: Hur ska man strukturera ett projekt? (arduino, C)

Inlägg av adent »

Att man gör en C-fil och en H-fil är bara ett mentalt koncept, ett bra koncept dock.
Här kommer ungefär samma förklaring som alla andra sagt innan, men kanske en något annan vinkel :)

.h-filen deklarerar, talar om vad som finns i C-filen. Eller snarare: vad som ska vara externt åtkomligt från battery.c.
Alla filer som vill använda funktioner från battery.c måste inkludera battery.h.
Interna funktioner i battery.c deklareras som static i battery.c. De ska ingen annan c-fil se!

När du inkluderar .h-filen, t.ex. battery.h i t.ex. main.c så kommer all text från .h-filen att klistras in där du inkluderade den, väldigt enkelt egentligen.

När kompilatorn kompilerar din main.c och upptäcker att din main.c försöker anropa en funktion som inte finns implementerad i main.c men den är deklarerad längst upp i main.c (tack vara att allt innehåll från battery.h har klistrats in i main.c där #include var!), så köper kompilatorn det. Den "tänker": Ok här anropar du funktionen "int measurebattery(void)" den har du (genom include av battery.h) sagt att den funktionen finns, så jag köper det för tillfället, även om jag inte ser funktionen deklarerad här i main.c. Kompilatorn skapar nu en main.o (objekt-fil, ett mellansteg mellan källkod och körbar fil/laddfil).

gcc -c main.c

Sedan kan du kompilera battery.c till battery.o

gcc -c battery.c

Slutligen länkar du filerna med t.ex. gcc battery.o main.o -o mittfinaprogram

Skulle det nu vara så att du LJÖG för kompilatorn i battery.h, att funktionen "int measurebattery(void)" faktiskt inte alls finns i battery.c (och därmed inte heller i battery.o (eller nån annan .o-fil du har med)) så blir kompilatorn/länkaren arg på dig i detta steg och säger bara att hörredu! i main.o vill jag anropa "int measurebattery(void)" men den finns ingenstans att finna!

I små projekt kan man dock hoppa över objektfils-steget om man vill och köra allt i ett svep med:

gcc main.c battery.c -o mittfinaprogram

Därför kan det vara bra att veta om detta med objektfiler och att det givetvis görs så ändå även i ovanstående fall.
Hoppas jag bidrog och inte förvirrade mer.

MVH: Mikael
BJ
Inlägg: 8185
Blev medlem: 11 april 2007, 08:14:53
Ort: En_stad

Re: Hur ska man strukturera ett projekt? (arduino, C)

Inlägg av BJ »

Jag funderar också på det här med h-filer,
och har läst förklaringarna här.

Om nu funktionerna kan hittas i o-filerna,
varför kan man inte ha deklarationerna i h-filerna
i c-filerna i stället? Kompilatorn eller länkaren
kan ju ändå se dom?

Vad är vitsen med att flytta ut det i h-filer?
Att det blir mer lättläst?

Borde man inte kunna inkludera c-filerna i stället?
Eller har jag missat något?
Användarvisningsbild
TomasL
EF Sponsor
Inlägg: 45168
Blev medlem: 23 september 2006, 23:54:55
Ort: Borås
Kontakt:

Re: Hur ska man strukturera ett projekt? (arduino, C)

Inlägg av TomasL »

Orsaken är att kompilatorn läser in c-filerna i in annan ordning än du kanske tänkt.
Om det då finns ett funktionsanrop i den första filen, till en funktion i den sista filen som kompilatorn skall läsa in, så kan inte kompilatorn allokera plats för anropet..
Därför inkluderar du alla h-filer som behövs för modulen, så kompilatorn kan allokera plats för funktioner och variabler (samt även konstanter).

Kompilatorn läser in den första c-filen, och konstaterar att det finns ett funktionsanrop.
Kompilatorn måste då i sin länklista tala om var denna fil finns.
Hittar den inte funktionen inom modulen så generar kompilatorn ett fel.
Men, om det finns en h-fil inkluderad som innehåller deklarationen för funktionen, då vet kompilatorn att den skall sätta in en platshållare i länklistan, tills dess att filen med funktionen dyker upp, då kan kompilatorn byta ut platshållaren med en "pekare" på var funktionen finns i verkligheten.

denna beskrivning är synnerligen övergripande och förenklat dock.
BJ
Inlägg: 8185
Blev medlem: 11 april 2007, 08:14:53
Ort: En_stad

Re: Hur ska man strukturera ett projekt? (arduino, C)

Inlägg av BJ »

Aha. Då hänger jag med.
johano
Inlägg: 1943
Blev medlem: 22 januari 2008, 10:07:45
Ort: Stockholm

Re: Hur ska man strukturera ett projekt? (arduino, C)

Inlägg av johano »

Ett annat problem med att börja inkludera .c filerna direkt är att du (rätt snabbt) riskerar att få dubblerade funktionsdefinitioner.

Ponera att main.c och dialog.c båda vill använda en funktion från battery.c.
Om du då inkluderar battery.c i både main.c och dialog.c så kommer funktionerna från battery.c att bli definierade
i både main.o och dialog.o = inget bra.

Det *kan* funka i väldigt små och enkla projekt men är absolut inte att rekommendera och är oftast en enkelbiljett
till massa strul och problem.

/j
Användarvisningsbild
TomasL
EF Sponsor
Inlägg: 45168
Blev medlem: 23 september 2006, 23:54:55
Ort: Borås
Kontakt:

Re: Hur ska man strukturera ett projekt? (arduino, C)

Inlägg av TomasL »

Det är därför du alltid i h-filen har med

Kod: Markera allt

#indef NÅGONTING_H
#define NÅGONTING_H
/*dina prototyper och variabler*/





#endif
På detta sätt kan du garantera att allt bara blir inkluderat en gång.
Användarvisningsbild
arvidb
Inlägg: 4537
Blev medlem: 8 maj 2004, 12:56:24
Ort: Stockholm

Re: Hur ska man strukturera ett projekt? (arduino, C)

Inlägg av arvidb »

Nä, include guards skyddar inte mot multipla definitioner av funktioner av den typen som johano nämner. De skyddar mot multipla definitioner av datatyper inom samma kompileringsenhet, men deklarerar du t.ex. en variabel i en headerfil, eller en icke-static funktion, och sedan inkluderar den headerfilen i två eller fler c-filer så får du ändå länk-fel i form av multiple definition - även om du använder include guards.
Användarvisningsbild
TomasL
EF Sponsor
Inlägg: 45168
Blev medlem: 23 september 2006, 23:54:55
Ort: Borås
Kontakt:

Re: Hur ska man strukturera ett projekt? (arduino, C)

Inlägg av TomasL »

Ja om du gör misstaget att skapa två variabler med samma namn i två olika filer.
Men det är ju rimligt att kompilatorn varnar om det finns multipla variabler och funktioner med samma namn
Användarvisningsbild
arvidb
Inlägg: 4537
Blev medlem: 8 maj 2004, 12:56:24
Ort: Stockholm

Re: Hur ska man strukturera ett projekt? (arduino, C)

Inlägg av arvidb »

Nä, skapar du en funktion i en headerfil med header guards och sedan inkluderar den i två olika c-filer som sedan länkas ihop till ett program så får du länkfel "multiple definition":

Kod: Markera allt

#ifndef MDTEST_H
#define MDTEST_H

int multiple;

int do_something()
{
	return 1;
}

#endif /* MDTEST_H */
Inkludera filen från två c-filer (main.c och mdtest.c) och länka:

Kod: Markera allt

$ gcc -Wall mdtest.c main.c -o mdtest
/tmp/cc6nefpD.o: In function `do_something':
main.c:(.text+0x0): multiple definition of `do_something'
/tmp/cclUFG3w.o:mdtest.c:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status
Detta eftersom include guards bara funkar inom samma kompileringsenhet. Funktionsdefinitionen kommer alltså att byggas in i både mdtest.o och main.o och då blir inte länkaren glad!

Det förvånar mig däremot att int multiple; slinker igenom. Variabeln är dessutom åtkomlig i båda c-filerna - och det är samma variabel, så den är global i hela programmet. :humm:
Användarvisningsbild
TomasL
EF Sponsor
Inlägg: 45168
Blev medlem: 23 september 2006, 23:54:55
Ort: Borås
Kontakt:

Re: Hur ska man strukturera ett projekt? (arduino, C)

Inlägg av TomasL »

Du skapar väl aldrig funktioner i h-filer, enbart prototyperna?
Filen i fråga heter i2str.h

Kod: Markera allt

#ifndef i2str_h
	#define i2str_h
	
	#define I2STR_NODOT		  0
	#define I2STR_1DEC		  5
	#define I2STR_2DEC		  4
	#define I2STR_3DEC		  3
	#define I2STR_4DEC		  2
	
	#define I2STR_PLUSSIGN	  8
	#define I2STR_ADDSPACE	 16
	
	#define I2STR_L_ADJ		  0
	#define I2STR_R1ADJ		192
	#define I2STR_R2ADJ		160
	#define I2STR_R3ADJ		128
	#define I2STR_R4ADJ		 96
	#define I2STR_R5ADJ		 64
	#define I2STR_R6ADJ		 32
	
	#define I2STR_ADJMASK	224
	
	
	// str at least 8 chars
	void i2str( char *str,          short n, unsigned char opt);
	void u2str( char *str, unsigned short n, unsigned char opt);
	void i2str_set_dot(char dot); // '.'  ','  punkt eller komma
	void i2str_set_fill(char fill); // ' ' '0' mellanslag eller nolla
	void l2str(unsigned char *str, long n, unsigned char opt);
	void ul2str(unsigned char *str, unsigned long n, unsigned char opt);
	
#endif
Fungerar alldeles utmärkt att inkludera i mängder av filer
Användarvisningsbild
arvidb
Inlägg: 4537
Blev medlem: 8 maj 2004, 12:56:24
Ort: Stockholm

Re: Hur ska man strukturera ett projekt? (arduino, C)

Inlägg av arvidb »

johano skrev: "Ett annat problem med att börja inkludera .c filerna direkt är att du (rätt snabbt) riskerar att få dubblerade funktionsdefinitioner."

Du (TomasL) skrev: "Det är därför du alltid i h-filen har med" /inclusion guards/.

Det stämmer alltså inte, eftersom inclusion guards inte skyddar mot dubblerade funktionsdefinitioner. Normalt skapar man inte funktioner (med extern linkage) i h-filer, nej.
Användarvisningsbild
lillahuset
Gått bort
Inlägg: 13969
Blev medlem: 3 juli 2008, 08:13:14
Ort: Norrköping

Re: Hur ska man strukturera ett projekt? (arduino, C)

Inlägg av lillahuset »

Inget konstigt att "int multiple" blir multipla variabler om h-filen inkluderas i flera c-filer. Den "måste" deklareras "extern int multiple" i h-filen.
Användarvisningsbild
arvidb
Inlägg: 4537
Blev medlem: 8 maj 2004, 12:56:24
Ort: Stockholm

Re: Hur ska man strukturera ett projekt? (arduino, C)

Inlägg av arvidb »

lillahuset: Det trodde jag också, men den deklarationen slinker igenom utan klagomål (trots -Wall) och variabeln beter sig som om den vore extern. Vilket den tydligen också är ser jag nu när jag har läst på lite: default linkage på globala variabler i C är tydligen extern. Jag trodde att man var tvungen att deklarera den (utan extern) i en av c-filerna i programmet också, men det behövs tydligen inte!
Användarvisningsbild
lillahuset
Gått bort
Inlägg: 13969
Blev medlem: 3 juli 2008, 08:13:14
Ort: Norrköping

Re: Hur ska man strukturera ett projekt? (arduino, C)

Inlägg av lillahuset »

Å f-n. Säger standarden det eller är det möjligen en egenhet hos en specifik kompilator? :humm:
Användarvisningsbild
arvidb
Inlägg: 4537
Blev medlem: 8 maj 2004, 12:56:24
Ort: Stockholm

Re: Hur ska man strukturera ett projekt? (arduino, C)

Inlägg av arvidb »

Fan vet. :lol:

Testa själv:

Kod: Markera allt

#ifndef MDTEST_H
#define MDTEST_H

int multiple;

void do_something(void);

#endif /* MDTEST_H */

Kod: Markera allt

#include "mdtest.h"

void do_something(void)
{
	multiple = 2;
}

Kod: Markera allt

#include <stdio.h>
#include "mdtest.h"

int main(void)
{
	multiple = 1;
	do_something();
	printf("multiple: %d\n", multiple);

	return 0;
}

Kod: Markera allt

$ gcc --version
gcc (Gentoo 4.9.4 p1.0, pie-0.6.4) 4.9.4
...
$ gcc -Wall mdtest.c main.c -o mdtest
$ ./mdtest 
multiple: 2
Skriv svar