Forum: Compiler & IDEs GCC Preprozessor Berechnungen und Forced Errors


von Thorsten M. (cortex_user)


Lesenswert?

Hallo,
sicher kam das schonmal hier aber habe es nicht gefunden.

Müssen #define Berechnungen gecasted werden, wenn Zwischenergebnisse zb 
größer sind als 16 Bit? Auch für float die gleiche Frage: Müssen da dann 
kommastellen an die Zahlen? Also 500.0

Beispiel
1
#define WERT1 1000
2
#define WERT2 500
3
#define WERT3 300
4
5
#define ERGEBNIS = WERT1*WERT2 / WERT3

Die andere Frage ist: Kann man mit

#if sizeof (struct....) > GRENZWERT
#pragma error Errortext
#endif

oder ähnlich einen Fehler erzeugen zur Kompilierzeit, wenn mein Struct 
in diesem Fall die Größe des EEPROMS übersteigt? So wie geschrieben 
klappt es jedenfalls nicht. Der Fehler wird immer ausgelöst. Derzeit 
behelfe ich mir diese Angaben zur Laufzeit zu prüfen, was aber 
umständlich ist, da sie ja nicht dynamisch sind.

Gruss,
Thorsten

: Bearbeitet durch User
von PittyJ (Gast)


Lesenswert?

Meine Meinung zu #define: lass sie weg. Da passieren nur Fehler.

Man kann auch mit

const int xxx=6;

ganz normal Konstanten deklarieren und dann damit rechnen. Und dann 
werden die normalen C++ Vorschriften benutzt.

Alle Rechnereien mit #define machen nur Probleme, weil dort nichts 
gerechnet wird, sondern eine Textersetzung stattfindet.

von Oliver S. (oliverso)


Lesenswert?

1. Der Preprozessor macht nur Textersetzungen. Immer. Ohne Ausnahmen.
2. Ausnahmen bestätigen die Regel.

https://microchipdeveloper.com/c:preprocessor-arithmetic

Die andere Antwort:
In C++ kannst du sizeof in static_assert verwenden.
In C brauchts wohl etwas schwarze Magie, aber irgendwie geht das dann 
auch:
https://scaryreasoner.wordpress.com/2009/02/28/checking-sizeof-at-compile-time/

Oliver

von A. S. (Gast)


Lesenswert?

PittyJ schrieb:
> Meine Meinung zu #define: lass sie weg.

In C++ geht das fast vollständig, in C sind defines oft sinnvoll.

Zu den asserts Google nach "Compile time asserts"

von Thorsten M. (cortex_user)


Lesenswert?

PittyJ schrieb:
> Meine Meinung zu #define: lass sie weg. Da passieren nur Fehler.

Ich frage mich wie ich ein Programm mit derzeit 13.000 Zeilen und 5 
Modulen ohne sowas schreiben soll, wenn es wartbar bleiben soll? 
Natürlich ersetzt das nur aber ausgerechnet wird es trotzdem!

Nur nach welchen Regeln?

Habe kein assert etc. Läuft auf einem Cortex M0 Bare Metal Code 
geschrieben...

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Thorsten M. schrieb:
> oder ähnlich einen Fehler erzeugen zur Kompilierzeit, wenn mein Struct
> in diesem Fall die Größe des EEPROMS übersteigt?

Nein. Der Präprozessor heisst so, weil er im Prinzip als selbstständiges 
Programm dem Compiler voran läuft und keine intime Kenntnis vom dem hat, 
was erst der Compiler weiss. Und sizeof kennt erst der Compiler.

von Vincent H. (vinci)


Lesenswert?

Oliver S. schrieb:
> 1. Der Preprozessor macht nur Textersetzungen. Immer. Ohne Ausnahmen.
> 2. Ausnahmen bestätigen die Regel.
>
> https://microchipdeveloper.com/c:preprocessor-arithmetic
>
> Die andere Antwort:
> In C++ kannst du sizeof in static_assert verwenden.
> In C brauchts wohl etwas schwarze Magie, aber irgendwie geht das dann
> auch:
> https://scaryreasoner.wordpress.com/2009/02/28/checking-sizeof-at-compile-time/
>
> Oliver

C kennt mittlerweile auch static_assert
1
#include <assert.h>
2
3
typedef struct {
4
    int x;
5
    int y;
6
} S;
7
8
static_assert(sizeof(S) <= 8);

https://godbolt.org/z/5Pbo3jeEW

: Bearbeitet durch User
von Thorsten M. (cortex_user)


Lesenswert?

Vincent H. schrieb:
> https://godbolt.org/z/5Pbo3jeEW

Der ist ja mal geil! Dasss es sowas gibt wusste ich gar nicht... nur 
ausführen kann der wohl nicht.

von Vincent H. (vinci)


Lesenswert?

Thorsten M. schrieb:
> Vincent H. schrieb:
>> https://godbolt.org/z/5Pbo3jeEW
>
> Der ist ja mal geil! Dasss es sowas gibt wusste ich gar nicht... nur
> ausführen kann der wohl nicht.

Doch doch (rechtes Fenster)
https://godbolt.org/z/rW38EEMsr

von Udo K. (udok)


Lesenswert?

Thorsten M. schrieb:
> Müssen #define Berechnungen gecasted werden, wenn Zwischenergebnisse zb
> größer sind als 16 Bit? Auch für float die gleiche Frage: Müssen da dann
> kommastellen an die Zahlen? Also 500.0

Berechnungen finden im grössten Integer Typ statt.  Es geht nur Integer 
und sonst nix.  Casten gibt es im Präprozessor nicht.
Wenn das Ergebnis unsigned sein soll, kannst du U dranhängen.

Siehe Beispiel:
1
#define WERT1 1024
2
#define WERT2 64
3
#define WERT3 1
4
#define ERGEBNIS (WERT1*WERT2 / WERT3)
5
6
#if ERGEBNIS >= (1 << 16)
7
  #error ERGEBNIS zu gross
8
#endif
9
10
#define X (1 << 63)
11
12
#if X < 0
13
  #error X negative
14
#endif
15
16
#if X < 0U
17
  #error Tritt nie auf
18
#endif

von Oliver S. (oliverso)


Lesenswert?

Vincent H. schrieb:
> Doch doch (rechtes Fenster)
> https://godbolt.org/z/rW38EEMsr

Aber nur X86, keine andere Architektur.

Oliver

von Thorsten M. (cortex_user)


Lesenswert?

Oliver S. schrieb:
> Aber nur X86, keine andere Architektur.

Doch, ESP32, ARM, MIPS, 6502 u.v.m.  Ist aber nicht so ganz einfach zu 
durchschauen, das Teil.

von Rolf M. (rmagnus)


Lesenswert?

Thorsten M. schrieb:
> Oliver S. schrieb:
>> Aber nur X86, keine andere Architektur.
>
> Doch, ESP32, ARM, MIPS, 6502 u.v.m.  Ist aber nicht so ganz einfach zu
> durchschauen, das Teil.

Was muss man denn tun, damit diese Versionen ausgeführt werden?

von Oliver S. (oliverso)


Lesenswert?

Nix.

Die binaries aller anderen Architekturen ausser X86 können zwar erzeugt, 
aber nicht ausgeführt werden. Wie auch. Der Server müsste die anderen 
Architekturen dafür ja alle emulieren können.

Oliver

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Vincent H. schrieb:
> C kennt mittlerweile auch static_assert

Ist ein C23-Feature. Dürfte bei den üblichen Compilern noch unter 
-std=c2x rangieren.

von MakroMan (Gast)


Lesenswert?

Der Vollständigkeit halber, mit diesem Makro funktioniert es auch:
1
#define BUILD_BUG_ON(condition)  ((void)sizeof(char[1 - 2*!!(condition)]))
2
3
char some_array[10];
4
5
BUILD_BUG_ON(sizeof(some_array) != 10);

von Rolf M. (rmagnus)


Lesenswert?

Oliver S. schrieb:
> Nix.
>
> Die binaries aller anderen Architekturen ausser X86 können zwar erzeugt,
> aber nicht ausgeführt werden.

Ja, aber es wurde ja behauptet, dass das auch mit anderen ginge:

Thorsten M. schrieb:
> Doch, ESP32, ARM, MIPS, 6502 u.v.m.  Ist aber nicht so ganz einfach zu
> durchschauen, das Teil.


> Der Server müsste die anderen Architekturen dafür ja alle emulieren
> können.

Ja, wobei das ja zumindest für einen Teil der Architekturen nicht ganz 
unmöglich wäre. Aber deshalb die Frage, wie das denn gehen soll.

von Martin B. (ratazong)


Angehängte Dateien:

Lesenswert?

> Nein. Der Präprozessor heisst so, weil er im Prinzip als selbstständiges
> Programm dem Compiler voran läuft und keine intime Kenntnis vom dem hat,
> was erst der Compiler weiss. Und sizeof kennt erst der Compiler.


Zumindest bei dem GCC weiss ich nicht, ob das so stimmt.

Folgendes Beipiel habe ich mal angehängt zum Gegenbeweis:

Übersetzen mit gcc x.c -O0 --save-temps erzeugt die angehängte *.s Datei 
(Assembler output)

in func1() (per define) wird die Division durch 10 durch eine 
Multiplikation und anschliessendes Shift ersetzt.

In func2() (per const Variable) wird der HW Divisionsbefehl benutzt. 
(Den will ich vermeiden, weil auf den meisten Prozessoren arschlangsam.)

Wenn ich mit -O2 übersetze, kommt bei func1() und func2() dasselbe 
heraus.

Das ist zumindest merkwürdig.
Irgendetwas macht der Prepro, was eigentlich Aufgabe des Compilers wäre.

von Rolf M. (rmagnus)


Lesenswert?

Jörg W. schrieb:
> Vincent H. schrieb:
>> C kennt mittlerweile auch static_assert
>
> Ist ein C23-Feature.

Nö, das gibt es seit C11.

Martin B. schrieb:
>> Nein. Der Präprozessor heisst so, weil er im Prinzip als selbstständiges
>> Programm dem Compiler voran läuft und keine intime Kenntnis vom dem hat,
>> was erst der Compiler weiss. Und sizeof kennt erst der Compiler.
>
> Zumindest bei dem GCC weiss ich nicht, ob das so stimmt.

Beim GCC ist der Präprozessor mit im Compiler eingebaut, aber dadurch 
darf sich dessen Verhalten natürlich nicht ändern.

> In func2() (per const Variable) wird der HW Divisionsbefehl benutzt.
> (Den will ich vermeiden, weil auf den meisten Prozessoren arschlangsam.)

Das hat aber nichts mit dem Präprozessor zu tun, sondern damit, dass 
eine const-Variable eben keine Konstante ist, sondern immer noch eine 
Variable, während die direkt hingeschriebene Zahl 10 eine echte 
Konstante ist, und durch die ersetzt ja der Präpozessor jedes Vorkommen 
von "Teilfaktor" (das eher "Divisor" heißen müsste).

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Rolf M. schrieb:
>> Ist ein C23-Feature.
>
> Nö, das gibt es seit C11.

OK, war mir nicht mehr klar. Ist natürlich vorteilhaft, dann sollte es 
schon recht gut ausgerollt sein.

von Martin B. (ratazong)


Lesenswert?

> Variable, während die direkt hingeschriebene Zahl 10 eine echte
> Konstante ist, und durch die ersetzt ja der Präpozessor jedes Vorkommen
> von "Teilfaktor" (das eher "Divisor" heißen müsste).

Eben nicht! Genau das passiert nicht. Schau Dir den Assemblercode noch 
einmal an. Da wird in func1() vorher etwas errechnet, die 10 taucht 
nicht auf.

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

Martin B. schrieb:
> Schau Dir den Assemblercode noch
> einmal an. Da wird in func1() vorher etwas errechnet.

Aber wie kommst du darauf, dass das am Präprozessor liegt?
Kommt was anderes raus, wenn du
1
> for (i = 0; i < N; i++) in[i] = in[i] / Teilfaktor;
ersetzt durch:
1
for (i = 0; i < (100); i++) in[i] = in[i] / 10;
? Denn das würde exakt dem entsprechen, was der Präprozessor hier tun 
sollte.

von Martin B. (ratazong)


Lesenswert?

Rolf M. schrieb:
> Martin B. schrieb:
>> Schau Dir den Assemblercode noch
>> einmal an. Da wird in func1() vorher etwas errechnet.
>
> Aber wie kommst du darauf, dass das am Präprozessor liegt?

Weil ich dem Compiler verboten habe, zu optimieren. Vielleicht tut er es 
trotzdem, kann ja sein.

Aber in func2() tut er es ja nicht. Erst wenn ich Optimierungen 
einschalte.

Test mit /10 entlastet aber den Praeprozessor, selbes Ergebnis.

: Bearbeitet durch User
von Udo K. (udok)


Lesenswert?

Martin B. schrieb:
> Eben nicht! Genau das passiert nicht. Schau Dir den Assemblercode noch
> einmal an. Da wird in func1() vorher etwas errechnet.

Bei mir wird da immer der identische Code erzeugt, mit -O1 und -O2.
Nur wenn das "const" bei tf weggelassen wird, dann wird func2 kompakter, 
weil nur ein idiv verwendet wird.
Die Optimierung sollte eigentlich nur bei -O2 angemacht werden, und 
nicht bei -O1 / -Os, weil sie in 99/100 Fällen nichts bringt und Platz 
braucht.
1
/* Code auf die das Wesentliche reduziert */
2
#define Teilfaktor 10
3
int tf = 10; // kein const
4
int func1(int in) { return in / Teilfaktor; }
5
int func2(int in) { return in / tf; }
6
7
Liefert mit cl -O1:
8
9
func1:
10
  0000000000000000: B8 67 66 66 66     mov         eax,66666667h
11
  0000000000000005: F7 E9              imul        ecx
12
  0000000000000007: C1 FA 02           sar         edx,2
13
  000000000000000A: 8B C2              mov         eax,edx
14
  000000000000000C: C1 E8 1F           shr         eax,1Fh
15
  000000000000000F: 03 C2              add         eax,edx
16
  0000000000000011: C3                 ret
17
18
func2:
19
  0000000000000000: 8B C1              mov         eax,ecx
20
  0000000000000002: 99                 cdq
21
  0000000000000003: F7 3D 00 00 00 00  idiv        eax,dword ptr [tf]
22
  0000000000000009: C3                 ret

Interessanterweise liefert der MS cl den kompaktesten Code.

Das static_assert Macro gibt es in Windows seit ca. NT 4.x, da wird es 
so gemacht:
#define STATIC_ASSERT(expr) typedef char _assert_type[(expr)? 1 : -1]

Der Fehler wird aber nicht vom Präprozessor sondern vom Compiler 
generiert, der ein ungültiges Typedef sieht.

Das BUILD_BUG_ON Macro vom Beitrag weiter vorne funktioniert bei mir 
überhaupt nicht.

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

Martin B. schrieb:
> Test mit /10 entlastet aber den Praeprozessor, selbes Ergebnis.

Das zeigt ja, dass der Präprozessor nichts damit zu tun hat.

Udo K. schrieb:
> Das BUILD_BUG_ON Macro vom Beitrag weiter vorne funktioniert bei mir
> überhaupt nicht.

Das wird daran liegen, dass es im Gegensatz zu den üblichen 
static_asserts nicht außerhalb einer Funktion verwendet werden kann.

von Martin B. (ratazong)


Lesenswert?

Udo K. schrieb:
> dann wird func2
> kompakter, weil nur ein idiv verwendet wird.

Der mag kompakter sein, er ist mit großer Wahrscheinlichkeit sehr viel 
langsamer. (Auf PC Prozessoren habe ich das aber nie gemessen.)

Rolf M. schrieb:
> Das zeigt ja, dass der Präprozessor nichts damit zu tun hat.

Jupp, der ist raus. Die Operation sieht der nicht als Optimierung.

von Oliver S. (oliverso)


Lesenswert?

Martin B. schrieb:
> Irgendetwas macht der Prepro, was eigentlich Aufgabe des Compilers wäre.

Macht er natürlich nicht.

Was er macht, brauchst du dir aber nicht zusammenzurätseln, du kannst es 
dir doch einfach anschauen. -E, und schon weißt du es.

Oliver

von A. S. (Gast)


Lesenswert?

Martin B. schrieb:
> Weil ich dem Compiler verboten habe, zu optimieren. Vielleicht tut er es
> trotzdem, kann ja sein.

Eine Compiler-Option zum Verbot des Optimierens wäre ungewöhnlich. Meist 
gibt es nur verschieden wählbare "zusätzliche" Optimierungsstufen, die 
mit mehr Aufwand Platz/Laufzeit weiter optimieren.

von Markus F. (mfro)


Lesenswert?

Nun, da musst Du dich eben dran gewöhnen.

Die Speicherklassen in C werden ihren Namen selten gerecht: an auto ist 
nix automatisch, an static ist (meist) nichts statisch, volatile ist 
keineswegs unberechenbar und const ist nicht konstant (sondern 
readonly).
Noch nicht mal deine Präprozessor "Konstanten" sind Konstanten (sondern 
Literale).

von Rolf M. (rmagnus)


Lesenswert?

Markus F. schrieb:
> Nun, da musst Du dich eben dran gewöhnen.
>
> Die Speicherklassen in C werden ihren Namen selten gerecht: an auto ist
> nix automatisch,

Doch, natürlich. Der Speicher wird bei Verlassen des Blocks, in dem die 
Variable definiert ist, automnatisch wieder frei.

> an static ist (meist) nichts statisch,

Außerhalb von Funktionen nicht, aber innerhalb schon. Außerhalb von 
Funktionen hat es eine andere Bedeutung zugewiesen bekommen.

> volatile ist keineswegs unberechenbar

volatile heißt nicht unberechenbar, sondern flüchtig oder unbeständig, 
und genau darum geht es da. Der Wert muss jedesmal neu gelesen werden, 
weil nicht unbedingt das mehr drin steht, was man im Kontrollfluss 
vorher reingeschriebne hat.

> und const ist nicht konstant (sondern readonly).

Zumindest da würde ich zustimmen.

> Noch nicht mal deine Präprozessor "Konstanten" sind Konstanten (sondern
> Literale).

Wie schon gesagt: Doch, sind sie. Der Begriff "integer literal" kommt im 
C-Standard im Gegensatz zu "integer constant" überhaupt nicht vor.

: Bearbeitet durch User
von Markus F. (mfro)


Lesenswert?

Rolf M. schrieb:
> Markus F. schrieb:
>> Nun, da musst Du dich eben dran gewöhnen.
>>
>> Die Speicherklassen in C werden ihren Namen selten gerecht: an auto ist
>> nix automatisch,
>
> Doch, natürlich. Der Speicher wird bei Verlassen des Blocks, in dem die
> Variable definiert ist, automnatisch wieder frei.
>
das gilt für alle (auch die globalen) Variablen in C. Ausser denen, 
die statisch sind. auto ist nicht "automatischer" als eine globale 
Variable.

>> an static ist (meist) nichts statisch,
>
> Außerhalb von Funktionen nicht, aber innerhalb schon. Außerhalb von
> Funktionen hat es eine andere Bedeutung zugewiesen bekommen.
>
>> volatile ist keineswegs unberechenbar
>
> volatile heißt nicht unberechenbar, sondern flüchtig oder unbeständig,
> und genau darum geht es da. Der Wert muss jedesmal neu gelesen werden,
> weil nicht unbedingt das mehr drin steht, was man im Kontrollfluss
> vorher reingeschriebne hat.
>
volatile heisst (in meinem Oxford dictionary) durchaus unberechenbar 
(von mir aus auch instabil oder leicht verdunstend. "flüchtig" fällt nur 
dem englischen Chemiker zuerst ein, dem von der Strasse nicht.

>> Noch nicht mal deine Präprozessor "Konstanten" sind Konstanten (sondern
>> Literale).
>
> Wie schon gesagt: Doch, sind sie. Der Begriff "integer literal" kommt im
> C-Standard im Gegensatz zu "integer constant" überhaupt nicht vor.
>
Wenn "3.141" eine Konstante ist, ist es längst nicht dasselbe wie "const 
double pi = 3.141". Da ist in der Begrifflichkeit schon was falsch. Von 
pi z.B. kann ich eine Adresse ermitteln, von 3.141 jedoch nicht - das 
kann also nicht dasselbe sein.

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


Lesenswert?

Markus F. schrieb:
>> Doch, natürlich. Der Speicher wird bei Verlassen des Blocks, in dem die
>> Variable definiert ist, automnatisch wieder frei.
>
> das gilt für alle (auch die globalen) Variablen in C. Ausser denen, die
> statisch sind.

Globale Variablen sind immer statisch.

"static" ist (auf oberstem Niveau) ein im Nachhinein gesehen sehr 
unglücklich gewählter Name. Heute weiß man es besser, dass man hätte die 
Voreinstellung (ohne Schlüsselwort) so machen sollen, dass die Objekte 
(und Funktionen) dabei nur innerhalb der Übersetzungseinheit sichtbar 
sind, während man eine globale Sichtbarkeit mit einem extra 
Schlüsselwort ("extern" oder "global") explizit anfordert. Ist leider 
nicht so, also hat "static" auf oberstem Niveau eine andere Bedeutung 
als innerhalb von Funktionen.

von Irgend W. (Firma: egal) (irgendwer)


Lesenswert?

Markus F. schrieb:
> volatile heisst (in meinem Oxford dictionary) durchaus unberechenbar
> (von mir aus auch instabil oder leicht verdunstend.

Wobei ich diese Übersetzung durchaus auch zutreffend finde. Der Inhalt 
er Variable kann sich "unberechenbar", durch für an dieser Stelle im 
Programmfluss nicht erkennbare Einflüsse, verändern und ist daher als 
"instabil" anzusehen. Gegenmaßnahme, der Wert muss bei jedem Zugriff 
erneut von der Originaladresse gelesen und geschrieben werden (das 
Atomar zu gestalten ist wieder eine andere Baustelle). Der Inhalt darf 
nicht über mehrere Befehle hinweg z.B. in einem Register 
zwischengespeichert werden wie es bei einem "stabilen" Wert gemacht 
werden darf der sich nicht "unvorhersehbar" verändern kann.

von Rolf M. (rmagnus)


Lesenswert?

Markus F. schrieb:
>> Doch, natürlich. Der Speicher wird bei Verlassen des Blocks, in dem die
>> Variable definiert ist, automnatisch wieder frei.
>>
> das gilt für alle (auch die globalen) Variablen in C.

Globale Variablen stehen gar nicht in einem Block. Sie existieren über 
die gesamte Programmlaufzeit hinweg. Aus Sicht des Programms werden sie 
nie freigegeben.

> Ausser denen, die statisch sind.

Bezüglich der Lebensdauer gibt es auf globaler Ebene keinerlei 
Unterschied zwischen static und nicht-static Variablen.

> auto ist nicht "automatischer" als eine globale Variable.

Doch. Ich kann in einem Programm 100 mal eine Funktion aufrufen, und 
dann werden deren lokale Variablen 100 mal erzeugt und 100 mal wieder 
freigegeben. Bei rekursiven Funktionen gibt es für jede Aufrufebene eine 
eigene Instanz der Variablen.
Nach dem Rücksprung aus der Funktion ist die Variable jeweils nicht mehr 
existent und wird beim nächsten Aufruf neu erzeugt.

> Wenn "3.141" eine Konstante ist, ist es längst nicht dasselbe wie "const
> double pi = 3.141". Da ist in der Begrifflichkeit schon was falsch. Von
> pi z.B. kann ich eine Adresse ermitteln, von 3.141 jedoch nicht - das
> kann also nicht dasselbe sein.

Richtig. Deshalb habe ich weiter oben geschrieben, dass

Rolf M. schrieb:
> eine const-Variable eben keine Konstante ist, sondern immer noch eine
> Variable, während die direkt hingeschriebene Zahl 10 eine echte
> Konstante ist

: Bearbeitet durch User
von Markus F. (mfro)


Lesenswert?

Rolf M. schrieb:
> Globale Variablen stehen gar nicht in einem Block. Sie existieren über
> die gesamte Programmlaufzeit hinweg. Aus Sicht des Programms werden sie
> nie freigegeben.

Komisch. Bei mir sind die globalen Variablen nach dem Programmende immer 
weg.
Joke aside - was ich meinte war: Variablen verschwinden, wenn ihr Scope 
verlassen wird. Globale Variablen sind auf dem äussersten Scope 
definiert (völlig wurscht, wie sie intern organisiert sind und dass sie 
nicht auf dem Stack liegen). Dieser Scope wird nur ein einziges Mal 
betreten. Wird dieser Scope verlassen (bei einem µC-Programm passiert 
das nie), sind sie weg (also genauso "automatisch" wie auto-Variablen).

von Bauform B. (bauformb)


Lesenswert?

Markus F. schrieb:
> Globale Variablen sind auf dem äussersten Scope definiert
> (völlig wurscht, wie sie intern organisiert sind und dass
> sie nicht auf dem Stack liegen).

Die könnten doch genauso gut auf dem Stack liegen? Man könnte die 
ganze bss section sparen, das wäre doch für ein Mikro-RTOS durchaus 
vorteilhaft. Aber wie sähe die C-Syntax dafür aus?

von J. S. (jojos)


Lesenswert?

Markus F. schrieb:
> Wird dieser Scope verlassen (bei einem µC-Programm passiert
> das nie), sind sie weg

oh, das passiert sehr wohl. Man kann aus main() zurückkehren und landet 
dann im Startupcode. Wenn in main jetzt threads gestartet wurden und auf 
nicht mehr gültige Variablen oder Objekte aus main zugreifen wollen, 
dann gibt das lustige Effekte und man sucht Fehler erstmal ganz 
woanders.

von Markus F. (mfro)


Lesenswert?

Bauform B. schrieb:
> Markus F. schrieb:
>> Globale Variablen sind auf dem äussersten Scope definiert
>> (völlig wurscht, wie sie intern organisiert sind und dass
>> sie nicht auf dem Stack liegen).
>
> Die könnten doch genauso gut auf dem Stack liegen? Man könnte die
> ganze bss section sparen, das wäre doch für ein Mikro-RTOS durchaus
> vorteilhaft. Aber wie sähe die C-Syntax dafür aus?

Das könnten sie - völlig ohne Änderung der Syntax. Der Compiler müsste 
sich eben eine Tabelle mit Stack-Offsets anlegen.
Es würde allerdings den Austausch von Daten zwischen Startup-Code und 
dem C-Programm erschweren.

von db8fs (Gast)


Lesenswert?

PittyJ schrieb:
> const int xxx=6;
>
> ganz normal Konstanten deklarieren und dann damit rechnen. Und dann
> werden die normalen C++ Vorschriften benutzt.

Für sowas gibts mittlerweile auch constexpr seit C++11, ist explizit für 
solche Vorberechnungen gedacht.

Jörg W. schrieb:
> "static" ist (auf oberstem Niveau) ein im Nachhinein gesehen sehr
> unglücklich gewählter Name. Heute weiß man es besser, dass man hätte die
> Voreinstellung (ohne Schlüsselwort) so machen sollen, dass die Objekte
> (und Funktionen) dabei nur innerhalb der Übersetzungseinheit sichtbar
> sind, während man eine globale Sichtbarkeit mit einem extra
> Schlüsselwort ("extern" oder "global") explizit anfordert.
> Ist leider
> nicht so, also hat "static" auf oberstem Niveau eine andere Bedeutung
> als innerhalb von Funktionen.

Hmm, na ja, ganz anders ja aber nun auch nicht.
1
int foobar  =1;
2
static int foobar2 =2;
3
4
static void funcFoo() {}
5
void        funcFoo2() {}
6
7
int main()
8
{
9
  static int foobar3=3;
10
  foobar = 0;
11
  foobar2= 0;
12
  foobar3= 0;
13
14
  funcFoo();
15
  funcFoo2();
16
17
  return 0;
18
}

Da sagt der nm dann schon für Funktionen und Variablen, dass die groß- 
bzw. kleingeschrieben werden bei den Symbolen: (groß: global, klein: 
local)
1
0000000000601028 D foobar
2
0000000000601030 d foobar2
3
0000000000400520 t funcFoo
4
00000000004004c0 T funcFoo2
5
00000000004004d0 T main
6
000000000060102c d main.foobar3

Ansonsten aber stimm ich dir zu, es wär besser, per Default Variablen 
und Funktionen aus Linker-Sicht lokal zu halten - zumindest bei C/C++.

von Rolf M. (rmagnus)


Lesenswert?

Markus F. schrieb:
> Rolf M. schrieb:
>> Globale Variablen stehen gar nicht in einem Block. Sie existieren über
>> die gesamte Programmlaufzeit hinweg. Aus Sicht des Programms werden sie
>> nie freigegeben.
>
> Komisch. Bei mir sind die globalen Variablen nach dem Programmende immer
> weg.

Ich schrieb ganz bewusst "aus Sicht des Programms". Nach dem 
Programmende gibt's keine Sicht des Programms mehr, weil es weg ist. Was 
dann mit dem Speicher passiert, interessiert das Programm nicht mehr: 
"Not my job". Und erzeugt werden sie bereits vor der Ausführung von 
main(). Also existieren diese Variablen aus Sicht des Programms für 
immer.
Bei automatischen Variablen ist das anders. Die werden innerhalb des 
Programms beliebig oft erzeugt und entsprechend natürlich auch wieder 
freigegeben, und das ganz automatisch.

: Bearbeitet durch User
von Udo K. (udok)


Lesenswert?

db8fs schrieb:
> Ansonsten aber stimm ich dir zu, es wär besser, per Default Variablen
> und Funktionen aus Linker-Sicht lokal zu halten - zumindest bei C/C++.

Dafür gibt es in C++ ja Namespaces.

von Philipp Klaus K. (pkk)


Lesenswert?

Udo K. schrieb:
> Thorsten M. schrieb:
>> Müssen #define Berechnungen gecasted werden, wenn Zwischenergebnisse zb
>> größer sind als 16 Bit? Auch für float die gleiche Frage: Müssen da dann
>> kommastellen an die Zahlen? Also 500.0
>
> Berechnungen finden im grössten Integer Typ statt.  Es geht nur Integer
> und sonst nix.  Casten gibt es im Präprozessor nicht.
> Wenn das Ergebnis unsigned sein soll, kannst du U dranhängen.
>

Fast. Berechnungen im Präprozessor werden in intmax_t 
(vorzeichenbehaftet) bzw. uintmax_t (vorzeichenlos) ausgeführt.
Seit C23 ist aber (u)intmax_t nicht mehr notwendigerweise der größte 
Ganzzahltyp.

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.