[C] Netste methode om constantes te includen

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • Sissors
  • Registratie: Mei 2005
  • Niet online
Volgens mij moet ik hier zijn :)

Ik heb c-files, die hebben een aantal constante arrays nodig. Gezien dat die afhankelijk van het target zijn (verschillende types microcontrollers) is het idee om die arrays in een andere file, target specific, te specificeren. Dan kunnen de c-files onafhankelijk van het target hergebruikt worden. Nu de vraag, wat is de beste methode? Ik heb er met iemand redelijk wat discussie over gehad, en we kwam er niet echt uit, de opties:

Gewoon de handel in a .h file definieren, static zodat hij het gewoon 1x in het geheugen plaatst en geen linker errors maakt. Nadeel is dat de waarde in een header stoppen volgens mij niet heel net is.
C: constantes.h
1
  static const int constante1 = 5;

C: api.c
1
2
3
4
5
#include "constantes.h"

void foo() {
  int a = constante1;
}


Optie twee: hetzelfde, maar nu het in een c file zetten en extern doen in de header. Nu wordt het wel in een code file gedefinieerd, en die 'hoort' bij een header. Gezien dat ze in verschillende folders zitten is het daardoor misschien duidelijker. Nadeel is dat ook als een target de constante niet gebruikt er nog steeds iets gedefinieerd moet zijn (de api.c code in dit voorbeeld is wel hetzelfde voor alle targets, maar niet alle targets hoeven hem per sé te gebruiken). Daarnaast doet de header file in principe niks.
C: constantes.h
1
  extern const int constante1;

C: constantes.c
1
  const int constante1 = 5;

C: api.c
1
2
3
4
5
#include "constantes.h"

void foo() {
  int a = constante1;
}


Oftewel optie 3. Gewoon de hele header file overslaan, en direct hem in de code file binnenhalen. Nadeel wat ik hier weer zie is dat het nou niet 1-2-3 duidelijk is waar constante1 vandaan komt in api.c.
C: constantes.c
1
  const int constante1 = 5;

C: api.c
1
2
3
4
5
extern const int constante1;

void foo() {
  int a = constante1;
}


Oftewel, wat is de beste methode? Mogelijk nog een vierde methode? En voor de duidelijkheid: de integer hier is een voorbeeld, in het echt is het een 2D array, dus je kan het niet zomaar door een enum of define vervangen.

Acties:
  • 0 Henk 'm!

  • Elijan9
  • Registratie: Februari 2004
  • Laatst online: 17-09 23:01
Optie 2 is de enige nette manier van de drie. Dit is in feite hetzelfde als optie 3, alleen verplaats je de declaratie naar een headerfile, die daardoor herbruikbaar is. Als je een constante bijvoorbeeld unsigned zou willen maken hoef je dit maar op 2 plekken aan te passen (de declaratie en de definitie). Bij optie 3 moet je alle sourcefiles weer langs.

Bij optie 1 daarentegen doet heel iets anders! Hier wordt voor elke C file een (static) constante gedefinieerd, dus als je vanuit 4 verschillende sourcefiles de headerfile include, dan worden er 4 int's gedefinieerd met dezelfde waarde. Zou je dit met een niet const waarde doen en deze variabele zou worden aangepast vanuit een bepaalde sourcefile en gelezen worden vanuit een andere, dan ziet de laatste niet de wijzigingen van de eerste, ze beschikken over een totaal andere variabele...
Een static variabele of functie in een C headerfile zou ik zelf nooit goedkeuren! :/

Overigens, als je de contanten alleen maar gaat gebruiken om andere variabelen weer mee te initialiseren en niet rechtstreeks gebruikt, dan kun je waarschijnlijk beter defines gebruiken...

[ Voor 9% gewijzigd door Elijan9 op 30-01-2014 12:42 ]

War is when the young and stupid are tricked by the old and bitter into killing each other. - Niko Bellic


Acties:
  • 0 Henk 'm!

  • Sissors
  • Registratie: Mei 2005
  • Niet online
Elijan9 schreef op donderdag 30 januari 2014 @ 12:32:
Optie 2 is de enige nette manier. Dit is in feite hetzelfde als optie 3, alleen verplaats je de declaratie naar een headerfile, die daardoor herbruikbaar is. Als je een constante bijvoorbeeld unsigned zou willen maken hoef je dit maar op 2 plekken aan te passen (de declaratie en de definitie). Bij optie 3 moet je alle sourcefiles weer langs.
Goed punt bij optie 3. (Niet dat het in dit geval erg waarschijnlijk is, maar natuurlijk wel good practise).
Bij optie 1 daarentegen doet heel iets anders! Hier wordt voor elke C file een (static) constante gedefinieerd, dus als je vanuit 4 verschillende sourcefiles de headerfile include, dan worden er 4 int's gedefinieerd met dezelfde waarde.
Dat wordt niet door de static voorkomen? (Helpt voor mijn begrip niet dat static meerdere dingen kan betekenen).
Een static variabele of functie in een C headerfile zou ik zelf nooit goedkeuren! :F
Die smiley had niet gehoeven hoor, ik stel ook maar een vraag.

Om het nog niet te hebben over dat ik toch genoeg static functies in header files heb gezien, en die waren niet van mij afkomstig.

Acties:
  • 0 Henk 'm!

  • Elijan9
  • Registratie: Februari 2004
  • Laatst online: 17-09 23:01
Sissors schreef op donderdag 30 januari 2014 @ 12:41:
Dat wordt niet door de static voorkomen? (Helpt voor mijn begrip niet dat static meerdere dingen kan betekenen).
In C betekent static dat de variabele/functie niet buiten de source-file benaderbaar is.
Die smiley had niet gehoeven hoor, ik stel ook maar een vraag.

Om het nog niet te hebben over dat ik toch genoeg static functies in header files heb gezien, en die waren niet van mij afkomstig.
Point taken, op een klein schermpje zag de smile er meer uit als of hij zijn hand voor zijn ogen hield om iets niet te willen zien.

Ik ben het ook veel teveel tegengekomen...

War is when the young and stupid are tricked by the old and bitter into killing each other. - Niko Bellic


Acties:
  • 0 Henk 'm!

  • Infant
  • Registratie: Januari 2008
  • Laatst online: 11-08 11:15

Infant

It's a floating Dino!

Ik zou het ook met preprocessor defines doen, zo kun je bijv. de verschillende platformen waar je het over hebt in een header laten:

C: platform.h
1
2
3
4
5
6
7
#define PLAFTORM P1

#if PLATFORM == P1
 #define CONSTANTE1 5
#elif PLATFORM == P2
 #define CONSTANTE1 7
#endif


En in je c file kun je per configuratie dezelfde header includen, en aan CONSTANTE1 refereren. Het enige wat je hoeft te doen is per configuratie de platform define aan te passen.

Acties:
  • 0 Henk 'm!

  • Sissors
  • Registratie: Mei 2005
  • Niet online
Elijan9 schreef op donderdag 30 januari 2014 @ 12:48:
[...]

In C betekent static dat de variabele/functie niet buiten de source-file benaderbaar is.
Het verwarrende is dat je ook een static variabele in een functie hebt, en dat is weer heel wat anders, net als statische member variabelen in C++
[...]

Point taken, op een klein schermpje zag de smile er meer uit als of hij zijn hand voor zijn ogen hield om iets niet te willen zien.
Misverstandje dus :).

Maar iig bedankt voor de heldere hulp, wordt dus optie 2.
Infant schreef op donderdag 30 januari 2014 @ 13:23:
Ik zou het ook met preprocessor defines doen, zo kun je bijv. de verschillende platformen waar je het over hebt in een header laten:

C: platform.h
1
2
3
4
5
6
7
#define PLAFTORM P1

#if PLATFORM == P1
 #define CONSTANTE1 5
#elif PLATFORM == P2
 #define CONSTANTE1 7
#endif


En in je c file kun je per configuratie dezelfde header includen, en aan CONSTANTE1 refereren. Het enige wat je hoeft te doen is per configuratie de platform define aan te passen.
Het probleem is dat de integer een voorbeeld was, het is een 2D array (met één dimensie variabel). Nu kan ik dat ook nog wel in een define stoppen, maar ik heb altijd begrepen dat over het algemeen complexe defines niet aan te raden zijn, en dat ik in zo'n situatie beter een const kan gebruiken.

Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 01:47
Ik ben het helemaal met Elijan9 eens: de tweede optie is de meest redelijke.
Elijan9 schreef op donderdag 30 januari 2014 @ 12:32:
Overigens, als je de contanten alleen maar gaat gebruiken om andere variabelen weer mee te initialiseren en niet rechtstreeks gebruikt, dan kun je waarschijnlijk beter defines gebruiken...
... maar aangezien het in de TS om arrays met data gaat, lijkt me dat geen elegante oplossing.

Ook dit lijkt me persoonlijk minder geslaagd:
Infant schreef op donderdag 30 januari 2014 @ 13:23:
C: platform.h
1
2
3
4
5
6
7
#define PLAFTORM P1

#if PLATFORM == P1
 #define CONSTANTE1 5
#elif PLATFORM == P2
 #define CONSTANTE1 7
#endif
Nu staan alle platforms door elkaar in één bestand (maar zijn definities gegroepeerd op identifier). Aangezien je maar voor één platform tegelijk compileert, is het echter logischer om op platform te groeperen, en alleen de definities van het doelplatform te includen. (Dit is ook de aanpak die bijvoorbeeld in de Linux kernel gehanteerd word.)

(Overigens werkt de code van Infant sowieso niet omdat de preprocessor alleen integers met == kan vergelijken. Het principe werk wel als je #define PLATFORM_P1 en dan #ifdef PLATFORM_P1 enzo gebruikt.)

Acties:
  • 0 Henk 'm!

  • Sissors
  • Registratie: Mei 2005
  • Niet online
Ja dat gebeurd sowieso al in de huidige build scripts (die door andere zijn gemaakt), die zorgen dat alleen bepaalde mappen worden gecompiled. Die defines zijn er in principe ook wel voor verschillende platformen, en een hele enkele keer wil je die weleens gebruiken (als een code file echt identiek is op één klein detail na bij twee targets), maar in principe is dat hier niet nodig, en hoeft er inderdaad geen #ifdef TARGET gebruikt te worden.

De arrays worden zelf rechtstreeks gebruikt, niet voor initialiseren van andere variabelen. Ze zijn constant omdat ze constant zijn (:P), en daarnaast zodat de compiler dan de handel in het flash geheugen van de microcontrollers stopt in plaats van in het SRAM geheugen (wat veel beperkter is).

[ Voor 5% gewijzigd door Sissors op 30-01-2014 14:56 ]


Acties:
  • 0 Henk 'm!

  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

Hou er wel rekening mee dat in geval van optie 2 en 3 je de compiler niet de kans geeft om te optimaliseren. Hij weet namelijk niet welke waarde de variabele heeft tijdens het compileren.

Persoonlijk zou ik voor zoiets gaan:

C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// constantes.h
#if defined(PLATFORM_1)
#include "platform1_constantes.h"
#elif defined(PLATFORM_2)
#include "platform2_constantes.h"
#else
#error "unknown platform"
#endif

// platform1_constantes.h
static const int CONSTANTE1 = 5
// of
#define CONSTANTE2 7

// user.c
#include "constantes.h"

int foo() {
  int a = CONSTANTE1;
  int b = CONSTANTE2;
}


De compiler zal bij de meeste architecturen gewoon die variabele rechstreeks als operand in de instructies gebruiken zodat er geen extra loads nodig zijn. De compiler kan in zo'n geval ook de storage space voor die variabele weglaten.
Beide zullen je snellere en kleinere code opleveren.

ASSUME makes an ASS out of U and ME


Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 16-09 22:43
Dat zou de variant zijn die ik zou kiezen, en die meer wordt gebruikt bij dit soort platform afhaankelijke includes.
Ik vraag me wel af waarom die constante static moet worden? Dat vind ik dan weer niet zo deftig.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

  • Mijzelf
  • Registratie: September 2004
  • Niet online
Om namespace problemen te voorkomen. Voor iedere module waar de header is geinclude bestaat de constante (en wordt waarschijnlijk weggeoptimaliseerd). Als hij niet static was, zouden alle modules dezelfde constante bijdragen aan de global namespace.

Acties:
  • 0 Henk 'm!

  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

De static is in dit geval nodig omdat anders de linker zal klagen over een duplicaat symbool.

ASSUME makes an ASS out of U and ME


Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 16-09 22:43
H!GHGuY schreef op zaterdag 01 februari 2014 @ 13:57:
De static is in dit geval nodig omdat anders de linker zal klagen over een duplicaat symbool.
Pfff, was er met de gedachten niet bij :D Misschien dan toch maar een macro :P

[ Voor 6% gewijzigd door farlane op 01-02-2014 14:07 ]

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

  • jeroen3
  • Registratie: Mei 2010
  • Laatst online: 22:31
Voor het definiëren van constanten kun je beter const gebruiken ipv #define
Een const gebruikt maar één non-volatile geheugenplaats, een define vormt meerdere litererals en het is maar aan je optimizer om te zorgen dat het bij één geheugenplaats blijft.
Een const is type-safe, een define is dat niet als je hem niet typecast. Net als define macro's gevaarlijk zijn omdat er geen typecontrole plaatsvindt.

Het ook prima mogelijk is om een C file te includen, mits je deze dus niet apart compileert.
Zo kun je bijvoorbeeld config_modus_A.c includen bovenin je C file.
Het voordeel van die constructie is dat je per #if een andere C file kan gebruiken, die in tegenstelling tot een header file ook normale variabelen voor je module mag bevatten die een andere beginwaarde hebben.

[ Voor 5% gewijzigd door jeroen3 op 01-02-2014 17:36 ]


Acties:
  • 0 Henk 'm!

  • Daos
  • Registratie: Oktober 2004
  • Niet online
Een #define gebruikt in principe geen geheugen (bij int dan). Het wordt door de preprocessor in de broncode geplakt en wordt door de compiler verwerkt in de instructies.

De static const int CONSTANTE1 = 5 van H!GHGuY vind ik wel een geinige constructie, maar alleen zou ik het bestand hernoemen naar .c. Het is namelijk geen info, maar echte code/data.

Is een apart c-bestand met de constantes als const (ints, arrays en dergelijke) en een h-bestand met "extern" geen optie? Je kan dan voor elk platform een eigen c-bestand maken en het probleem met linken oplossen.

edit:
Nog even reageren op mijn bovenbuurman:
jeroen3 schreef op zaterdag 01 februari 2014 @ 17:33:
Een const is type-safe, een define is dat niet als je hem niet typecast. Net als define macro's gevaarlijk zijn omdat er geen typecontrole plaatsvindt.
Niet waar. Bij defines is er ook een typecontrole. De preprocessor plakt als het ware dingen in de broncode en daarna moet alles kloppen wat types betreft. Dit zal bv niet werken:
C:
1
2
#define MYCONST "test"
double x = MYCONST;
Het ook prima mogelijk is om een C file te includen, mits je deze dus niet apart compileert. Zo kun je bijvoorbeeld config_modus_A.c includen bovenin je C file.
Waar (behalve als we gaan mierenneuken: apart compileren maakt juist niet uit :9. Je moet het object dan alleen niet mee linken. Als je compilen en linken in 1x keer doet met bv "gcc x.c y.c" moet je het ook niet toevoegen als argument. Apart compilen met "gcc -c config_modus_A.c" maakt een mooi object en kan dus wel ;) )
Het voordeel van die constructie is dat je per #if een andere C file kan gebruiken, die in tegenstelling tot een header file ook normale variabelen voor je module mag bevatten die een andere beginwaarde hebben.
Niet waar. Het maakt voor de preprocessor bij #include niet uit of je het bestand .c of .h of .xxx hebt genoemd. Een conventie is dat het c-bestand code (functies) of data (variabelen maar ook "const int x = 5;") bevat en de header alleen info voor de preprocessor (defines (ook macros)) en de compiler (functie-declaraties(dus zonder body), structs, enums, externs). Van een los c-bestand kan je dan een object maken dat je kan linken met andere objecten en libraries. Met een losse header kan je niet veel. Het bevat immers geen code en geen data.

[ Voor 61% gewijzigd door Daos op 01-02-2014 19:26 ]


Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 16-09 22:43
Daos schreef op zaterdag 01 februari 2014 @ 17:54:
Is een apart c-bestand met de constantes als const (ints, arrays en dergelijke) en een h-bestand met "extern" geen optie? Je kan dan voor elk platform een eigen c-bestand maken en het probleem met linken oplossen.
Het punt van H!GHGuY is dat die constructie (waarschijnlijk?) minder goed te optimizen is omdat de eigenlijke definitie in een andere translation unit zit.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

  • PrisonerOfPain
  • Registratie: Januari 2003
  • Laatst online: 26-05 17:08
Daos schreef op zaterdag 01 februari 2014 @ 17:54:
Een #define gebruikt in principe geen geheugen (bij int dan). Het wordt door de preprocessor in de broncode geplakt en wordt door de compiler verwerkt in de instructies.
En die instructies worden weer opgeslagen in je...?

Acties:
  • 0 Henk 'm!

  • Daos
  • Registratie: Oktober 2004
  • Niet online
farlane schreef op zaterdag 01 februari 2014 @ 13:53:
Dat zou de variant zijn die ik zou kiezen, en die meer wordt gebruikt bij dit soort platform afhaankelijke includes.
Ik heb gewoon de startpost niet goed gelezen |:(
Ik vraag me wel af waarom die constante static moet worden? Dat vind ik dan weer niet zo deftig.
static voor globale variabelen en functies wil zeggen dat het alleen voor het huidige object bestaat. Het kan dan weg geoptimaliseerd worden. Zonder static krijg je ook nog een naming-conflict bij linken als meerdere objecten diezelfde constante bevatten. const wil alleen zeggen dat de waarde niet wijzigt.

edit:
PrisonerOfPain schreef op zaterdag 01 februari 2014 @ 19:40:
En die instructies worden weer opgeslagen in je...?
Met const stop je in je data segment een waarde (bv 5) en doe je als instructie bv zoiets
add r1, #addr_van_const

met define krijg je zoiets
add_imm r1, 5

[ Voor 19% gewijzigd door Daos op 01-02-2014 19:49 ]


Acties:
  • 0 Henk 'm!

  • Caelorum
  • Registratie: April 2005
  • Laatst online: 23:21
PrisonerOfPain schreef op zaterdag 01 februari 2014 @ 19:40:
[...]
En die instructies worden weer opgeslagen in je...?
HDD, geheugen en bij uitvoeren in je icache, maar dat wist je zelf vast ook wel :P

Acties:
  • 0 Henk 'm!

  • PrisonerOfPain
  • Registratie: Januari 2003
  • Laatst online: 26-05 17:08
Caelorum schreef op zaterdag 01 februari 2014 @ 19:50:
[...]

HDD, geheugen en bij uitvoeren in je icache, maar dat wist je zelf vast ook wel :P
Kortom, die nemen ook geheugen in ;)

Acties:
  • 0 Henk 'm!

  • Daos
  • Registratie: Oktober 2004
  • Niet online
offtopic:
Ik ben niet goed wakker vandaag. Ik zit ook gewoon de verkeerde post te quoten |:(


Ik had nog een edit gedaan zodat ik ook kon reageren op PrisonerOfPain.

Acties:
  • 0 Henk 'm!

  • PrisonerOfPain
  • Registratie: Januari 2003
  • Laatst online: 26-05 17:08
Daos schreef op zaterdag 01 februari 2014 @ 19:44:
Met const stop je in je data segment een waarde (bv 5) en doe je als instructie bv zoiets
add r1, #addr_van_const

met define krijg je zoiets
add_imm r1, 5
Die 5 neemt ook gewoon geheugen in, die moet in de ISA ge-encode worden (of word apart gezet in dezelfde segment als je const variables).

add al, 5 word 0x0405 waar de 0x04 voor "add al, imm8" staat, en de 0x5 de waarde is van de imm8.

Acties:
  • 0 Henk 'm!

  • Daos
  • Registratie: Oktober 2004
  • Niet online
PrisonerOfPain schreef op zaterdag 01 februari 2014 @ 20:01:
[...]


Die 5 neemt ook gewoon geheugen in, die moet in de ISA ge-encode worden (of word apart gezet in dezelfde segment als je const variables).

add al, 5 word 0x0405 waar de 0x04 voor "add al, imm8" staat, en de 0x5 de waarde is van de imm8.
Wat ik probeerde duidelijk te maken is dat met define en const de code meestal even groot blijft, maar bij const een fysiek plekje wordt gereserveerd waar de waarde in staat zodat ook code uit andere objecten (als in .o) erbij kan komen.

edit:
Voorbeeldje:
C:
1
2
3
4
5
6
7
8
9
10
11
12
13
#define CONST1 5
const int CONST2 = 5;
  
int x;
int y;

void f1() {
    x = y + CONST1;
}

void f2() {
    x = y + CONST2;
}


"gcc -S test.c" geeft:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    .file   "test.c"
.globl _CONST2
    .section .rdata,"dr"
    .align 4
_CONST2:
    .long   5
    .comm   _x, 4, 2
    .comm   _y, 4, 2
    .text
.globl _f1
    .def    _f1;    .scl    2;  .type   32; .endef
_f1:
    pushl   %ebp
    movl    %esp, %ebp
    movl    _y, %eax
    addl    $5, %eax
    movl    %eax, _x
    popl    %ebp
    ret
.globl _f2
    .def    _f2;    .scl    2;  .type   32; .endef
_f2:
    pushl   %ebp
    movl    %esp, %ebp
    movl    _y, %edx
    movl    _CONST2, %eax
    leal    (%edx,%eax), %eax
    movl    %eax, _x
    popl    %ebp
    ret

CONST1 is verdwenen en de waarde daarvan is in de instructies verwerkt. CONST2 bestaat nog en neemt in je data segment naast x en y een plek in ter grootte van een .long.

Zet je optimalisaties aan dan wordt f2 exact hetzelfde als f1, maar CONST2 blijft bestaan en dus ruimte innemen. Met "static const" en optimalisaties aan is er geen verschil meer.

[ Voor 45% gewijzigd door Daos op 01-02-2014 21:05 ]


Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 16-09 22:43
Je zou zeggen dat als niet het adres van de const ergens gebruikt wordt, de code hetzelfde zou moeten opleveren, ook qua geheugengebruik. Ik zie niet in wat er in die situatie anders is tussen een const en een macro.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

Zoals Daos aangeeft.

Bovendien is er bij sommige instruction sets nog een voordeel bij het gebruiken van een define of static const die beschikbaar is in de translation unit (lees: in de .h file is gedefinieerd).
Bij PowerPC is het bijvoorbeeld zo dat een 32bit variabele inladen 2 instructies kost. Als je dan zo'n load + add doet heb je 3 instructies, maar _veel_ belangrijker: je hebt waarschijnlijk een D-cache-miss.
Als je dan 1 instructie met data-operand hebt, heb je je code eigenlijk vele malen sneller gemaakt.
farlane schreef op zaterdag 01 februari 2014 @ 21:14:
Je zou zeggen dat als niet het adres van de const ergens gebruikt wordt, de code hetzelfde zou moeten opleveren, ook qua geheugengebruik. Ik zie niet in wat er in die situatie anders is tussen een const en een macro.
Tussen een static const en een define zal er bij een deftige compiler normaal geen verschil zijn. Ik heb me laten wijsmaken dat back in the days de compilers nog niet zo geavanceerd waren en er wel een voordeel was aan het gebruik van een define.

Het niet typesafe zijn van defines is trouwens ook niet helemaal waar:
C:
1
2
3
4
#define SOME_INT 3
#define SOME_DOUBLE  3.0d
#define SOME_FLOAT 4.5f
#define SOME_ULONG 3ul

[ Voor 42% gewijzigd door H!GHGuY op 01-02-2014 21:20 ]

ASSUME makes an ASS out of U and ME


Acties:
  • 0 Henk 'm!

  • Daos
  • Registratie: Oktober 2004
  • Niet online
Ik vind nog altijd dat een (static) const niet in een header mag (hernoem de bestanden dan naar .c). Het is iets fysieks dat alleen soms in het geval van static const weggeoptimaliseerd kan worden. Heb je bv een array met constante waarden dan neemt het vrij zeker een plekje in in het data deel. (Voor de mensen die het verschil nog niet zien: met "#define X 5" zeg je alleen tegen de preprocessor: als je in de broncode een X ziet vervang die dan door 5. Het is alleen info voor de tools en wordt op zichzelf niet in code/data veranderd. "const int X = 5;" is data. "int X(void) { return 5; }" is code)

In meerdere c-bestanden elke keer de code voor een heel array includen neemt trouwens misschien meer ruimte in dan het op 1 plek te zetten en "extern" te gebruiken.

[ Voor 2% gewijzigd door Daos op 01-02-2014 22:28 . Reden: "code" voorbeeld toegevoegd ]


Acties:
  • 0 Henk 'm!

  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

Ik was wel even uit het oog verloren dat het om arrays gaat... #define van arrays staat meestal niet zo lekker.
Je kan ze ook niet indexeren dus moet je ze sowieso nog eerst ergens in een variabele stoppen.

Over het al dan niet extern declareren: GCC kan ongebruikte data secties verwijderen. (gcc: -fdata-sections, ld: --gc-sections), dus als (?) de compiler van de static const (niet extern) gedefinieerde variabele elke access kan inlinen dan kan het stuk data ook uit je binary verwijderd worden. Van het moment dit niet het geval is kan je met een extern voordeel halen.
Los daarvan vind ik ook dat het plaatje voor arrays een stuk minder naar de ene of andere kant neigt en dat het van je compiler en target afhangt (kan de compiler hier efficient optimizen naar speed en size? Wat is de kost van cache-misses - gezien het een microcontroller betreft: heeft het ding zelfs een cache?)

ASSUME makes an ASS out of U and ME


Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 16-09 22:43
Ik verwacht eigenlijk dat const arrays ( evenals const string literals ) door de compiler/linker hergebruikt kunnen worden, ook als ze in de vorm van een macro langskomen en ook als ze static worden gedeclareerd.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

In macro-vorm denk ik het niet.

C:
1
2
3
4
5
6
7
8
9
10
11
12
13
#define FOO { 1, 2, 3, 4, 5 }

int bar1(int i)
{
  int array[] = FOO;
  return array[i];
}

int bar2()
{
  int array2[] = FOO;
  return array2[3];
}


De pre-processor zal beide FOO's vervangen met de array. De compiler ziet daarna helemaal niet meer dat deze van dezelfde definitie komen.

Volgens GCC is deze 2 arrays naar 1 enkele array duwen zelfs non-confirming behavior:
-fmerge-constants
Attempt to merge identical constants (string constants and floating-point constants) across compilation units.

This option is the default for optimized compilation if the assembler and linker support it. Use -fno-merge-constants to inhibit this behavior.

Enabled at levels -O, -O2, -O3, -Os.
-fmerge-all-constants
Attempt to merge identical constants and identical variables.

This option implies -fmerge-constants. In addition to -fmerge-constants this considers e.g. even constant initialized arrays or initialized constant variables with integral or floating-point types. Languages like C or C++ require each variable, including multiple instances of the same variable in recursive calls, to have distinct locations, so using this option results in non-conforming behavior.
En specifieker over static const heeft GCC ook nog:
-fkeep-static-consts
Emit variables declared static const when optimization isn't turned on, even if the variables aren't referenced.

GCC enables this option by default. If you want to force the compiler to check if a variable is referenced, regardless of whether or not optimization is turned on, use the -fno-keep-static-consts option.

ASSUME makes an ASS out of U and ME

Pagina: 1