Sida 3 av 4

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

Postat: 23 april 2018, 16:21:26
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

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

Postat: 23 april 2018, 17:52:19
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?

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

Postat: 23 april 2018, 18:05:39
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.

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

Postat: 23 april 2018, 18:09:57
av BJ
Aha. Då hänger jag med.

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

Postat: 23 april 2018, 18:10:50
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

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

Postat: 23 april 2018, 18:32:12
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.

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

Postat: 23 april 2018, 18:46:16
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.

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

Postat: 23 april 2018, 18:51:14
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

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

Postat: 23 april 2018, 19:19:49
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:

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

Postat: 23 april 2018, 19:24:29
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

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

Postat: 23 april 2018, 19:35:56
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.

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

Postat: 23 april 2018, 19:37:09
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.

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

Postat: 23 april 2018, 19:40:35
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!

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

Postat: 23 april 2018, 19:44:52
av lillahuset
Å f-n. Säger standarden det eller är det möjligen en egenhet hos en specifik kompilator? :humm:

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

Postat: 23 april 2018, 19:53:45
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