Go Down

Topic: Libreria TWI accorgimenti (Read 6928 times) previous topic - next topic

RobertoBochet

Ottima cosa, sempre generare documentazione; se tutti la pensassero cosi...
Scusa ma ti vorrei rubare qualche secondo per chiarire un dubbio che farà trasparire una mia ignoranza di cui mi vergogno in modo osceno.
La mia domanda è, se io ho una libreria con una dichiarazione di un array nell'header file di X elementi, quando compilo questi X elementi vengono "Allocati" (Uso allocati impropriamente perché credo sia il termine che rende di piu), se io quasta libreria poi vado ad includerla in un altro progetto e ovviamente includo l'header file dentro il file c principale a questo punto se compilo il mio progetto finale, lo spazio per l'array viene allocato per una seconda volta? Nel senso ho un array in piu di cui non me ne faccio niente che crea solo garbage??

Scusa ma è un dubbio che mi attanaglia che mi hai fatto venire in mente tu :P

Maurotec

Quote
Scusa ma ti vorrei rubare qualche secondo per chiarire un dubbio che farà trasparire una mia ignoranza di cui mi vergogno in modo osceno.

Io non mi vergogno mai della mia ignoranza, chi più chi meno lo siamo tutti.

Quote
Scusa ma è un dubbio che mi attanaglia che mi hai fatto venire in mente tu smiley-razz

Non so se ho capito bene, comunque fai una prova. Se ho capito bene in fase di link riceverai un errore, qualcosa tipo: doppia dichiarazione, la prima è stata dichiarata ... e la seconda ...

Nell'header non si dovrebbero mai dichiarare variabili, e quindi si dichiarano sempre nel file c. Se devi fare riferimento ad una variabile la dichiari nell'header extern, in questo modo il file c che include questo header vi avrà accesso. C'è la possibilità di dire al compilatore di considerare solo la prima dichiarazione e scartare la seconda senza emettere errore. Ti consiglio di fare degli esperimenti in merito, perché scritto su un libro è una cosa, scritto su un forum è un altra cosa. provato per esperienza è la migliore cosa.

Ti consiglio anche di acquistare questo libro: http://www.amazon.it/Design-Patterns-Embedded-Systems-Engineering/dp/1856177076/ref=cm_cr_pr_product_top
PS: lo so è caruccio.

Ciao.

leo72

#32
Oct 16, 2014, 06:55 am Last Edit: Oct 16, 2014, 06:57 am by leo72 Reason: 1
@Roberto:
Forse ho capito, negli header si usa un "giochino" per evitare di includere il codice più di una volta.

Se tu hai ad esempio una libreria chiamata CommonFunctions che puoi includere da tante parti, nell'header metti una cosa del genere:

Code: [Select]
#ifndef COMMON_FUNCTIONS_h
#define COMMON_FUNCTIONS_h
....tutto il codice
#endif

in questo modo, il compilatore definisce, la prima volta che includi la lib, la define COMMON_FUNCTIONS_h (il nome è arbitrario) e poi "isoli" tutto il codice della lib in quell'#if. A questo punto quando vai ad includere nuovamente la lib, il compilatore trova la define e salta tutta l'inclusione.

Maurotec

@leo non credo si riferisse a questo.

La prevenzione da doppia inclusione non funziona tra una libreria compilata e un codice ancora da compilare, per cui se nell'header c'è una dichiarazione in fase di link si solleva quell'errore, perché un simbolo è già presente nella libreria e una altro identico viene creato a causa della inclusione.

Negli header non ci vanno:
Dichiarazioni di variabili o qualunque cosa si trasformi in codice eseguibile.

Non si trasforma in codice eseguibile:
macro, typedef, struct, enum, dichiarazioni di funzione, funzioni inline, perchè queste sono informazioni fornite al compilatore, mentre extern è una informazione per il linker (e per il compilatore quando compila una lib). Diciamo che il programmatore dice al compilatore: non ti preoccupare di risolvere questo simbolo, mentre al linker extern si traduce in: cerca questo simbolo e risolvilo.

Ciao.

leo72

Ah, allora mi son perso una parte dei vostri discorsi  :smiley-sweat:

RobertoBochet

La risposta che piu mi fa aprire gli occhi è "Nell'header non si dovrebbero mai dichiarare variabili"
Oggi chiedendo ad un collega questo mi fa notare che probabilmente anche senza dichiarare la matrice come extern questa venga gestita dal linker per evitare la doppia allocazione di spazio. Sinceramente non so, quello che so è che non mi viene notificato l'errore di doppia dichiarazione.
Mi sto occupando ora di sistemare alcuni header per adattarli alla giusta tecnica di evitare variabili globali e usare extern nel caso le voglia adoperare che non mi sembra faccia male anche se il compilatore non notifica alcun errore.

Leo mi ha dato uno spunto, saltiamo il caso della precompilazione delle librerie, andiamo al caso che la libreria sia composta da due file c e h dentro un progetto eseguibile con un bel file main.c
io cosa faccio
-Creo il mio lib.h con dentro il suo
Code: [Select]
#ifndef LIB_H
#define LIB_H
...code...
#endif

e ci metto una variabile di tipo uint8_t chiamata x
-Quindi creo anche lib.c e includo il file lib.h, ci definisco le funzioni
-Creo main.c e includo lib.h per poterla usare.
-A questo punto compilo i file main.c e lib.c ma salta su un altro dubbio, nel complesso in lib.c è stata dichiarata x ma questo vale anche in main.c in quanto tutti e due includono lib.h, questo lo dico sul punto di vista che all'inizio di main.c e lib.c la definizione LIB_H non esiste ancora oppure mi sbaglio? Capite il mio dubbio? Nel momento che scrivo #define LIB_H questo vale per tutti i file compilati o solo nei file in cui è incluso?

Maurotec

#36
Oct 16, 2014, 06:38 pm Last Edit: Oct 16, 2014, 11:03 pm by MauroTec Reason: 1
#define viene processata dal preprocessore C e non dal compilatore in quanto da compilare non c'è nulla.
lib.h la includi sia nel main che in lib.c e poi compili e tutto funziona senza che la prevenzione della doppia inclusione intervenga, in quanto una volta viene inclusa nella lib.c e una volta nel main.c e non c'è doppia inclusione in una compile unit. [edit] Per testare se c'è ho meno doppia inclusione puoi aggiungere le due righe seguenti nell'header:
Code: [Select]

//#else
//#error TWI_H alredy defined
#endif // TWI_H



Se nel lib.h dichiari una variabile, il simbolo sarà incontrato due volte e il compilatore si può comportare come ho detto prima e cioè dipende dai flags. Magari hai il flag abilitato che dice al compilatore di risolvere il primo simbolo e scartare gli altri. Di default gcc avverte circa la doppia dichiarazione e si rifiuta di andare avanti.

Dopo cena vedo di fare qualche esempio di codice e lo posto.

Ho testato che questa dichiarazione (non c'è definizione e quindi non si alloca un bel niente):
Code: [Select]
uint8_t _TWIRxBufferArray[TWI_RX_BUFFER_SIZE];//Array per il buffer di ricezione
Può essere inclusa tante volte senza che il compilatore si lamenti. Il compilatore si lamenta se oltre a dichiararla la definisci inizializzandola. Una volta il compilatore si comportava diversamente, ora non ricordo se emettesse un warning o un errore fatale. In ogni caso se ritieni che la dichiarazione porti vantaggio li nell'header allora lasciala li, diversamente sarebbe più indicato dichiararla nel file .c come static, si dice in questo modo che la variabile è di modulo e nessuno può accedervi anche usando extern e se si vuole che qualcuno possa accedervi la si deve passare come argomento. Facendo che sia una variabile di modulo rispetti il data hiding e potresi preferirlo o meno, cioè nascondi i dati e li esponi solo ad alcune funzioni esterne alla compile unit.

Se nello stesso header dichiari e definisci la variabile uint8_t help = 0; riceverai l'errore:
Code: [Select]
twi.o:(.bss.help+0x0): multiple definition of `help'
main.o:(.bss.help+0x0): first defined here

Questo perché c'è di mezzo la definizione e la conseguente assegnazione di memoria.

Ciao.


Ciao.

PaoloP


RobertoBochet

Chiedo scusa a tutti per non aver piu risposto ma ero momentaneamente impossibilitato a rispondere, chiedo scusa sopratutto a Mauro che con l'ultimo post credo abbia tolto tutti i dubbi che potessero venire quindi vi ringrazzio tutti tantissimo :)
A presto per un nuovo topic.

Maurotec

Sono uno che dimentica facilmente i dettagli e li ricostruisce con l'aiuto della logica ma alle volte anche se la logica non fa una piega la realtà è leggermente differente.
Sono mesi che non programmo e mi rendo conto di quanto male mi ha fatto.

Volevo aggiungere qualcosa in merito alla allocazione dinamica della memoria tramite malloc.
Dagli esperimento che ho fatto viene fuori che l'uso di malloc su microcontroller con risorse limitate
non è il male assoluto, ma relativo a come si usa. L'allocazione dinamica effettuata all'inizio e mai più deallocata non può portare a deframmentazione, cosa che accade nel caso in cui si alloca e dealloca decine/centinaia di volte durante l'esecuzione del programma. Per questo motivo considero privo di conseguenze deleterie l'uso di malloc in particolare in questo contesto.
L'uso consigliato sarebbe:
Fornire una funzione parametrizzata per inizializzare la TWI e uno dei parametri è la dimensione del buffer. La funzione se chiamata più di una volta modifica la configurazione TWI ma non modifica la dimensione del buffer.
Bisogna ricordare che malloc consuma cicli CPU a differenza della allocazione statica.

Ciao.

Go Up