Forum: Compiler & IDEs Problem mit sizeof


von Günter R. (galileo14)


Lesenswert?

Hallo,

bei folgendem Code-Fragment

#if sizeof(char)>1
#warning "Size zu gross!"
#endif

bringt der GCC 3.4.6 die Fehlermeldung

../main.c:240:11: missing binary operator before token "("

(Zeile 240 ist die "#if sizeof(char)>1")

Der Code ist in Realität größer, aber ich habe den Fehler mal auf diesen 
Miminal-Code reduziert.

Kann mir jemand einen Hinweis geben, warum dem Compiler das nicht 
gefällt? Ansonsten funktioniert sizeof in den Funktionen einwandfrei.

Danke schon mal.

Günter

von Minetti (Gast)


Lesenswert?

Frage 6.5: Funktioniert der sizeof-Operator in Präprozessor-Direktiven?

Antwort: Nein. Präprozessing findet in einer Phase der Übersetzung 
statt, zu der noch keine Typinformation verfügbar ist. Statt 'sizeof' 
können die in <limits.h> vordefinierten Konstanten verwendet werden. 
Noch besser ist es natürlich, das Programm so zu schreiben, dass es 
unabhängig von der Grösse bestimmter Typen ist.

References: ANSI Sec. 2.1.1.2, Sec. 3.8.1 footnote 83; ISO Sec. 5.1.1.2, 
Sec. 6.8.1; H&S Sec. 7.11.1 p. 225.

von Günter R. (galileo14)


Lesenswert?

Hallo, Minetti,

danke für diesen Hinweis. Damit ist die Sache klar. Das Programm ist 
durchaus unabhängig von der Größe bestimmter Typen, mit dem 
sizeof-Konstrukt prüfe ich eine komplexere Sache ab, wollte hier aber 
den kleinstmöglichen Nenner bringen, um nicht zu viel erklären zu 
müssen.

Aber wenn sizeof zum Präprocessing seine Antwort noch nicht bringt - was 
mir jetzt nach besserem nachdenken klar ist - dann muß ich die Sache 
anders lösen, was leicht möglich ist.

Jedenfalls hast Du mir weitergeholfen. Danke.

Günter

von Uhu U. (uhu)


Lesenswert?

Wenn du C++ verwendest, kannst du aus der BOOST-Library das 
static_assert verwenden - damit kann man solche Dinge so ähnlich 
abprüfen, wie du das mit dem Preprozessor versucht hast.

static_assert benutzt nicht den Prepozessor, sondern Templates.

von Günter R. (galileo14)


Lesenswert?

Uhu Uhuhu wrote:
> Wenn du C++ verwendest, kannst du aus der BOOST-Library das


Danke, Uhu, aber ich benutze "nur" C, nicht C++, somit muß ich wohl 
darauf verzichten.

Das o.g. Konstrukt dient ja nur zum Abfangen von eventuellen 
Programmierfehlern, ich habe es jetzt als C-Code eingebaut, so braucht 
es zwar etwas ROM-Bereich, funktioniert aber einwandfrei.


von Andreas K. (a-k)


Lesenswert?

Bei besseren Compilern darfst du davon ausgehen, dass "if"-Anweisungen 
mit dem Compiler bereits bekanntem Ausgang entsprechend optimiert 
werden.

von Bobby (Gast)


Lesenswert?

Was schon immer im C-Standard verfügbar ist: assert.

man assert liefert:
1
NAME
2
       assert - abort the program if assertion is false
3
4
SYNOPSIS
5
       #include <assert.h>
6
7
       void assert(scalar expression);
8
...

Darin kannst Du problemlos sizeof verwenden.

Aufruf:
assert (sizeof(char)==1);

==1 deshalb, weil assert bei false abbricht.

von Patrick D. (oldbug) Benutzerseite


Lesenswert?

Andreas Kaiser wrote:
> Bei besseren Compilern darfst du davon ausgehen, dass "if"-Anweisungen
> mit dem Compiler bereits bekanntem Ausgang entsprechend optimiert
> werden.

[ ] Du hast die Frage des OP verstanden.
[ ] Du hast die Antwort aus der C-Bibel gelesen.

von Andreas K. (a-k)


Lesenswert?

> [ ] Du hast die Frage des OP verstanden.

Gebe ich dir recht.

> [ ] Du hast die Antwort aus der C-Bibel gelesen.

Bin ich aber seltsamerweise nicht der einzige. Nachgeholt: sizeof(char) 
ist per Definition 1.

von Gast (Gast)


Lesenswert?

Hallo,

was mich mal interessieren würde ist wenn ich z.B. char b definiere und 
das auf einen ARM (32Bit) braucht dann b 4 Byte oder nur 1 Byte?

Gruß

Florian

von Rolf Magnus (Gast)


Lesenswert?

> was mich mal interessieren würde ist wenn ich z.B. char b definiere und
> das auf einen ARM (32Bit) braucht dann b 4 Byte oder nur 1 Byte?

Kommt darauf an, was du mit "Byte" meinst. In C braucht char per 
Definition immer genau ein Byte, denn so ist das Byte dort definiert. 
Wenn du eigentlich ein Oktett meinst, dann kommt es in der Theorie auf 
den Compiler an. Er hat da freie Hand, da C nur definiert, daß ein Byte 
mindestens 8 Bit breit sein muß. Es darf aber auch 32 Bit breit sein.
In der Praxis ist ein Byte aber fast immer 8 Bit groß. Auf einem ARM 
kannst du davon ausgehen.

von Oops (Gast)


Lesenswert?

Welche Länge ein char nun tatsächlich hat müsste aus der erwähnten 
limits.h des Compilers hervorgehen.

Gruss
Oops

von Gast (Gast)


Lesenswert?

@all danke für die superschnelle antwort

also ich war eigentlich auch der Meinung das ein Byte immer 8 Bit sind 
und ein char eigentlich auch...

es geht nur darum kann ich den Speicher (3 Byte) die noch als overhead 
da sind benutzen oder kann man die nicht mehr allozieren?

Geht nicht um ein spezielles Problem.. aber kann interessant werden bei 
größeren Byte Arrays in einem 32Bit system...

man kann den compiler ja auch zwingen (mehr oder weniger) mit #pragma 
pack
aber ist das nötig?

hoffe ich liege hier richtig was ich schreib...

Gruß

Florian

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Gast wrote:

> es geht nur darum kann ich den Speicher (3 Byte) die noch als overhead
> da sind benutzen oder kann man die nicht mehr allozieren?

Du hast erstmal per se keine 3 Bytes Overhead.  Es steht dem Compiler
(oder Linker) frei, Variablen mit gleichen Alignment-Anforderungen
im Speicher zu packen.  D. h. wenn du sowas deklarierst:
1
uint8_t c;
2
uint32_t i;
3
uint8_t x[3];

dann wäre eine mögliche und sinnvolle Speicheraufteilung dafür:
1
Byte     Inhalt
2
...
3
192      c
4
193      x[0]
5
194      x[1]
6
195      x[2]
7
196      i
8
197      i
9
198      i
10
199      i

Anders ist es bei Strukturen, hier besitzt der Compiler nicht die
Freiheit, die Reihenfolge zu ändern, sondern er darf lediglich
dazwischen ungenutzten Platz einfügen.  Das wird er in der Regel
tun, wenn die Alignment-Anforderungen der Strukturelemente es
erfordert.  Daher ist es eine sinnvolle Idee, dass man Strukturen
vom größten zum kleinsten Mitglied hin auffüllt, zuerst also alle
32-bit-Mitglieder, dann die 16-bit-Mitglieder, dann die 8-bitter.
Falls ggf. dazwischen padding notwendig ist, bleibt es auf diese
Weise minimal.

von Gast (Gast)


Lesenswert?

@Jörg
Das es so optimal ausgenutztwäre, da bin ich einverstanden aber es kommt 
wieder drauf an was für ein system es ist... bei einem 32 bit System 
würde er nach dem letzten x ein Byte frei lassen (vorausgesetzt x[0] 
steht in den ersten 8Bit der 32) denn es ist doch für die ALU ein 
größerer Aufwand sich einen uint32_t aus mehreren Register "zusammen zu 
pfrimeln". Meinst du nicht das das der linker (macht er doch oder?) das 
berücksichtigt...

Gruß

Florian

von Rolf Magnus (Gast)


Lesenswert?

@Gast:

> also ich war eigentlich auch der Meinung das ein Byte immer 8 Bit sind
> und ein char eigentlich auch...

Ist ja auch sehr häufig der Fall. Außer bei DSPs und einigen ziemlich 
veralteten Maschinen habe ich noch nicht gehört, daß es mal anders war. 
Ich kenne auch kein reales Programm, das auch andere Größen 
berücksichtigt.

> Geht nicht um ein spezielles Problem.. aber kann interessant werden bei
> größeren Byte Arrays in einem 32Bit system...

In einem Array stehen die Elemente immer direkt hintereinander.

> man kann den compiler ja auch zwingen (mehr oder weniger) mit #pragma
> pack aber ist das nötig?

Das brauchst du eher für Strukturen, weil dort Padding-Bytes enthalten 
sein können, um korrektes Alignment für die Elemente der Struktur zu 
gewährleisten. Man sollte sich aber sehr gut überlegen, ob man das tut. 
Auf manchen Systemen sind Zugriffe mit falschem Alignment langsamer 
(z.B. x86), auf anderen führen sie zu einer CPU-Exception. Ein portables 
Datenaustauschformat ist damit auch nur bedingt machbar, da man auf eine 
Architektur treffen kann, die andere Alignment-Anforderungen hat. 
Außerdem können die Größen der Datentypen sich auch unterscheiden.

von Gast (Gast)


Lesenswert?

@Rolf
Ah das ist doch schon mal gut das das in Arrays so ist...
Mit DSP´s mach ich im Moment noch nix aber gut zu wissen...
Dann werd ich #pragma pack wohl nur verwenden wenn es klar ist das das 
Programm nie portiert wird...

vielen dank...

hast du eine Idee zu der Aussage die ich an Jörg gerichtet hatte?

Gruß

Florian

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

#pragma pack gehört sowieso nicht in dieses Forum. ;-)  Der GCC
benutzt keine Pragmas, er macht sowas mit Attributen.
__attribute__((packed)) wäre dafür also das Zutreffende.

von Rolf Magnus (Gast)


Lesenswert?

> bei einem 32 bit System würde er nach dem letzten x ein Byte frei
> lassen (vorausgesetzt x[0] steht in den ersten 8Bit der 32)

In Jörgs Beispiel steht im ersten Byte c. Ansonsten hättest du recht.

> denn es ist doch für die ALU ein größerer Aufwand sich einen uint32_t
> aus mehreren Register "zusammen zu pfrimeln".

Wie ich schon schrieb, kann es je nach CPU irgendwas von "egal" über 
"größerer Aufwand" bis "bricht mit CPU-Exception ab" sein. Es gibt CPUs 
(z.B. ARM), die auf 32-Bit-Integers nur dann zugreifen können, wenn 
deren Startadresse ein Vielfaches von 4 ist.

> Meinst du nicht das das der linker (macht er doch oder?) das
> berücksichtigt...

Der Linker kümmert sich um globale Variablen und um Code. Lokale 
Variablen werden für gewöhnlich auf dem Stack angelegt oder in Registern 
gehalten. Da ist es dann Sache des Compilers. Bei dynamischem Speicher 
ist es Sache des Compilers und der C-Bibliothek.

von Gast (Gast)


Lesenswert?

@Rolf, Jörg

also vielen dank... habt mir echt weitergeholfen.. aber ich werd mir 
glaub ich mal ein Buch über embedded c/c++ programmierung zulegen um da 
noch ein paar tiefere einblicke zu bekommen...


Gruß
Florian

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.