Forum: Mikrocontroller und Digitale Elektronik verhalten von defines in C


von Chandler B. (chandler)


Lesenswert?

Hallo,
ich habe eine Verständinsfrage zu defines in C.
1
#define TYPE               "ABC"
2
#define VARIATION          "_2"
3
#define MAJOR_VERSION      "00"
4
#define MINOR_VERSION      "900"
5
#define SUB_REVISION       "00"
6
7
#define VERSION_STRING TYPE "." VARIATION "." MAJOR_VERSION "." MINOR_VERSION "." SUB_REVISION
8
9
10
... ... ...
11
... ... ...
12
13
char version = VERSION_STRING;
14
15
... ... ...

Ich habe jetzt zwei Verschieden Möglichkeiten gehört. Und bin mir nicht 
sicher was jetzt richtig ist.
1) Wird das define beim Compilieren direkt ersetzen?
Also steht dann dort quasi
1
char version = "ABC._2.00.900.00"
oder
2) steht das Define irgendwo im Speicher? Und version zeigt darauf?

von Dergute W. (derguteweka)


Lesenswert?

Moin,

Ich tendiere stark zu deiner Moeglichkeit 1 und erhoehe um Probleme, die 
du kriegst, weil in einen char nur ein Zeichen passt und kein ganzer 
String.

Gruss
WK

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Chandler B. schrieb:
> 1) Wird das define beim Compilieren direkt ersetzen?

Der Präprozessor löst die Defines und Includes auf.
Danach kommt der Compiler dran.

Chandler B. schrieb:
> 2) steht das Define irgendwo im Speicher?
Nicht das Define, aber der String wird sich in irgendeinem Speicher 
befinden.

von Chandler B. (chandler)


Lesenswert?

Dergute W. schrieb:
> und erhoehe um Probleme, die
> du kriegst, weil in einen char nur ein Zeichen passt und kein ganzer
> String.

Das ist richtig.
Sollte
1
char version[20] = VERSION_STRING;

Arduino F. schrieb:
> Nicht das Define, aber der String wird sich in irgendeinem Speicher
> befinden.
Der String an für sich? Oder der String in der Variablen version?

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Chandler B. schrieb:
> Der String an für sich?
Natürlich!

Chandler B. schrieb:
> Oder der String in der Variablen version?
Verstehe ich nicht.

von Helmut -. (dc3yc)


Lesenswert?

Aber ein #define ersetzt nur die eine Zeichenkette durch die andere. 
Deinen String wirst du dir schon noch selber zusammenbauen müssen!

von Udo S. (urschmitt)


Lesenswert?

schaue dir an was vom Preprozessor raus kommt.
Je nach Compiler suche nach "Preprocessor output to file"
Siehe z.B.
https://stackoverflow.com/questions/8978997/how-can-i-see-the-output-of-the-visual-c-preprocessor

von Nick (b620ys)


Lesenswert?

Chandler B. schrieb:
> Der String an für sich? Oder der String in der Variablen version?

Der String steht irgendwo im Speicher. Die Variable verweist darauf.
#defines die nicht verwendet werden, werden einfach ignoriert, weil sie 
... nicht verwendet werden.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Chandler B. schrieb:
> Ich habe jetzt zwei Verschieden Möglichkeiten gehört.
Nicht glauben!
Lernen!

https://en.cppreference.com/w/c/preprocessor.html

von Obelix X. (obelix)


Lesenswert?

Chandler B. schrieb:
> Wird das define beim Compilieren direkt ersetzen?
> Also steht dann dort quasi

Es Wird ersetzt durch

char version = "ABC" "." "_2" "." "00" "." "900" "." "00";

von Nikolaus S. (Firma: Golden Delicious Computers) (hns)


Lesenswert?

Der C-Präprozessor ersetzt in einer ersten Runde:
1
char version[20] = VERSION_STRING;
durch
1
char version[20] = TYPE "." VARIATION "." MAJOR_VERSION "." MINOR_VERSION "." SUB_REVISION;
und in einer zweiten durch
1
char version[20] = "ABC" "." "_2" "." "00" "." "900" "." "00";
Das wird dann an den eigentlichen C-Compiler weitergegeben.

Der weiss dass zwei hintereinander stehende Strings zusammengesetzt 
werden sollen. Also ist das identisch zu:
1
char version[20] = "ABC._2.00.900.00";
Das versteht der Compiler nun so, dass er im Speicher 20 Bytes 
reserviert und mit den Zeichen ABC._2.00.900.00 vorbelegt.

Also gehört der String zum Programmspeicher und steht dann bei der 
Ausführung im Speicher. Und "version" verweist auf die Speicheradresse, 
so dass z.B. version[5] eine '2' ergibt.

: Bearbeitet durch User
von Daniel A. (daniel-a)


Lesenswert?

Die defines werden einfach ersetzt durch das, als was sie definiert 
wurden.
Jag es einfach mal durch den Präprozessor, dann siehst du, daraus wird: 
https://godbolt.org/z/GYErcPzGP
1
char version[] = "ABC" "." "_2" "." "00" "." "900" "." "00";

Mehrere String literale nacheinander werden dann vom Compiler wie ein 
einzelnes behandelt. Ist recht praktisch, haben diverse andere Sprachen 
auch so übernommen, und dort gibt es oft mehrere Arten String-literale 
zu schreiben.

C++:  "A" R"X(B)X"  entspricht  "AB"
python: "test" 'abc' f"{123}"  entspricht  "testabc123"
bash: "A"'B'C  entspricht  "ABC"

: Bearbeitet durch User
von Rbx (rcx)


Lesenswert?

Chandler B. schrieb:
> Wird das define beim Compilieren direkt ersetzen?

Nein, der Code wird zuerst beispielsweise wie mit der Suchfunktion im 
VI-Editor angepasst. Auf diese Weise kann man Makro-Szenarien erstellen, 
welche die Programmierung erleichtern, (oder ihn besser Dokumentieren, 
LOL) oder Konstanten wie Mathematische Fix-Zahlen (wie PI) einfliegen.
Zu Anfangszeiten von C meinten auch einige, mit Hilfe dieser 
Vorkau-Steuerung C wieder zurück nach Assembler zu frisieren. In 
Assembler sind ja auch Makros nicht standardisiert, wo man sich dann 
über Dialekte wundern kann.

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


Lesenswert?

Nick schrieb:
> Die Variable verweist darauf.

Nicht ganz. So, wie es da steht, ist es ja echt eine Variable, also 
etwas, was zur Laufzeit geändert werden kann.

Damit steht der String irgendwo (read-only) im Speicher. Beim Start des 
Programms wird er von da in die Variable kopiert, steht also alles in 
allem dann zweimal im Speicher.

Aber das hat natürlich jetzt nichts (mehr) mit dem preprocessing zu tun.

Der Präprozessor war früher oft ein eigenes Programm, heute ist er es 
meist nicht mehr.  Spielt aber für seine Funktionsweise jetzt keine 
Rolle.

: Bearbeitet durch Moderator
von Nick (b620ys)


Lesenswert?

Jörg W. schrieb:
> Damit steht der String irgendwo (read-only) im Speicher. Beim Start des
> Programms wird er von da in die Variable kopiert, steht also alles in
> allem dann zweimal im Speicher.

Ja, gut. OK!
Hätte er halt const char[] schreiben sollen. ;-)

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Nick schrieb:
> Hätte er halt const char[] schreiben sollen. ;-)
Es ist implementation Defined, wo der String dann landet.
z.B. bei den alten AVR wird er trotz const ins RAM kopiert.
Da aufgeteile Adressierung RAM vs Flash vs EEPROM

: Bearbeitet durch User
von Nick (b620ys)


Lesenswert?

Arduino F. schrieb:
> s ist implementation Defined, wo der String dann landet.

OK, mag so sein.
Ich könnte aber auch argumentieren, dass der Compiler merkt, dass der 
Versionsstring nie verändert wird und daher im ROM steht. Das ist 
sicherlich auch Compilerabhängig.

Und nein, ich widerspreche nicht Jörg W.

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


Lesenswert?

Nick schrieb:
> Das ist sicherlich auch Compilerabhängig.

Insbesondere ist es eben architekturabhängig.

Wenn du (wie beim klassischen AVR) für den Zugriff auf den ROM andere 
Befehle als für den Zugriff auf den RAM brauchst, musst du u.U. Dinge im 
RAM ablegen, damit hernach sowas wie strcpy() funktioniert.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Nick schrieb:
> Ich könnte aber auch argumentieren,
Könntest du!

Aber dann musst du es auf dem AVR auch richtig machen.
1
const char __flash version[20] = "ABC" "." "_2" "." "00" "." "900" "." "00";
1
const char PROGMEM version[20] = "ABC" "." "_2" "." "00" "." "900" "." "00";

: Bearbeitet durch User
von Nick (b620ys)


Lesenswert?

Arduino F. schrieb:
> Aber dann musst du es auf dem AVR auch richtig machen.

Die Frage war aber nicht spezifisch für den AVR. Also interessiert hier 
nur der Standard.
Die Sonderlocke __flash mag zwar für den AVR richtig sein, aber hier 
argumentativ nicht relevant.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Nick schrieb:
> Also interessiert hier
> nur der Standard.
Und du bist hier der Bestimmer.

Oder ist das schon der aktive Rückzug, weil du erwischt wurdest?

von Harald K. (kirnbichler)


Lesenswert?

Arduino F. schrieb:
> Aber dann musst du es auf dem AVR auch richtig machen.const char __flash
> version[20] = "ABC" "." "_2" "." "00" "." "900" "." "00";

Die 20 in den eckigen Klammern kann und sollte man weglassen; der 
Compiler kann selbst zählen (und berücksichtigt die implizite \0 am 
Stringende).

von Nick (b620ys)


Lesenswert?

Arduino F. schrieb:
> Oder ist das schon der aktive Rückzug, weil du erwischt wurdest?

Wobei erwischt? Wie du den AVR als Erster in die Arena geworfen hast?

Achso, Arduino F. Heißt das "F." "Fanboy"? Das würde Deine beschränkte 
Sicht erklären.

Arduino F. schrieb:
> Und du bist hier der Bestimmer.

Der TO ist es. Die Frage war komplett plattforumunabhängig gestellt.

: Bearbeitet durch User
von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Harald K. schrieb:
> Die 20 in den eckigen Klammern kann und sollte man weglassen;

Wenn es eine Konstante(read only) ist, dann hast du voll wahr.
Ein paar Beiträge vorher war es noch ein Variable.
Ist mir von da mit durchgerutscht

: Bearbeitet durch User
von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Nick schrieb:
> Achso, Arduino F. Heißt das "F." "Fanboy"?
Jawoll!

Nick schrieb:
> Das würde Deine beschränkte
> Sicht erklären.
Wenn du meinst...
Wenn du dich damit besser, größer und stärker fühlst.

Ansonsten:
Sach ich ja: Aktiver Rückzug.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Chandler B. schrieb:
> 1) Wird das define beim Compilieren direkt ersetzen?

In Translation Phase 4: Preprocessing directives are executed macro 
invocations are expanded, and _Pragma unary operator expressions are 
executed.

> Also steht dann dort quasi
1
> char version = "ABC._2.00.900.00"
> oder
> 2) steht das Define irgendwo im Speicher? Und version zeigt darauf?

Weder noch.  Der Präprozessor (Translation Phase 4) macht daraus:
1
char version = "ABC" "." "_2" "." "00" "." "900" "." "00";

sieht man zum Beispiel in der präprozessierten Quelle (bei GCC 
beispielsweise mit -save-temps in den *.i Files).

Translation Phase 6 macht dann: Adjacent string literal tokens are 
concatenated.  Entspricht dann:
1
char version = "ABC._2.00.900.00";

Die Diagnostic (Warning / Error), dass einem char ein const char* 
zugewiesen wird, wird in Translation Phase 7 ausgegeben: The resulting 
tokens are syntactically and semantically analyzed and translated as a 
translation unit.

von Thorsten S. (whitejack)


Lesenswert?

char *version == char version[20]  //fast

: Bearbeitet durch User
von Harald K. (kirnbichler)


Lesenswert?

Thorsten S. schrieb:
> char *version == char version[20]

Mitnichten.

Lass' Dir mal sizof version für beide Fälle ausgeben.

von Thorsten S. (whitejack)


Lesenswert?

char *version == char version[0]

von Harald K. (kirnbichler)


Lesenswert?

Thorsten S. schrieb:
> char *version == char version[0]

Das wird jetzt nicht besser.

von Thorsten S. (whitejack)


Lesenswert?

lange her

char version[20];

version == &version[0]

char *info = "Hallo"; == char *info[5]="Hallo";

: Bearbeitet durch User
von Bruno V. (bruno_v)


Lesenswert?

Chandler B. schrieb:
> Sollte
> char version[20] = VERSION_STRING;

besser ist nur ein ptr:

> const char * const version = VERSION_SRING;

Hier ist "version" einfach nur ein prt auf einen (const) text.

version selbst ist auch konstant, kann also nicht einfach auf einen 
anderen Text umgelegt werden.

Und egal ob im Ram oder Rom, normalerweise ist das die Darstellung mit 
der geringsten Wahrscheinlichkeit von doppeltem Speicher oder späterer 
Änderung.

(Das hat aber alles nichts mehr mit VERSION_STRING oder Präprozessor zu 
tun, das wurde zu Anfang gut erläutert)

: Bearbeitet durch User
von Walter S. (avatar)


Lesenswert?

Thorsten S. schrieb:
> char *info = "Hallo"; == char *info[5]="Hallo";

das wird ja immer abstruser, das ist definitiv nicht das gleiche

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


Lesenswert?

Nick schrieb:
> Also interessiert hier nur der Standard.

Der darüber natürlich nichts aussagt.

Mit "const" könnte man dem Compiler natürlich zumindest auf den 
Plattformen, wo es problemlos möglich ist, einen Hinweis geben, dass er 
den String nur einmal ablegen muss.

von Nick (b620ys)


Lesenswert?

Jörg W. schrieb:
> Der darüber natürlich nichts aussagt.

Da sind wir uns also einig. Irgendwelche Ausnahmen oder 
Plattformspezifika wurden vom TO nicht gefragt.

Ich find die Frage des TO auch in keiner Weise dumm. Er hat halt 
Wissenslücken (hatte jeder mal) und hat in einer klaren Art & Weise 
gefragt. Er hat auch gute Antworten dafür bekommen. Nur gibts halt paar 
Leute die entweder nicht ordentlich lesen können, oder sich mit ihrem 
Nischenwissen hervortun müssen.

Mit dir kann man ja auch auf einer vernünftigen Ebene argumentieren.

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


Lesenswert?

Nick schrieb:
> Ich find die Frage des TO auch in keiner Weise dumm.

Ist sie auch nicht, auf keinen Fall.

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.