Forum: Mikrocontroller und Digitale Elektronik #define übergreifend


von Hain (Gast)


Lesenswert?

Hallo,

gib es für folgendes eine Möglichkeit?

Ich möchte übergreifend auf #defines zugreifen. Was mir klar ist, es 
handelt sich hierbei mehr oder weniger nur um Textersetzung für den 
Compiler

>>main.c
#define BAUD 9600

>>uart.h
#ifndef BAUD
#warning UART_BAUD not defined. Set UART_BAUD 9600
#define BAUD 9600
#endif

>>uart.c
void func() {if(BAUD >= 9600) ...;}


Wenn in main.c BAUD nicht definiert ist, soll er in der uart.h 
entsprechend BAUD setzen.

von hp-freund (Gast)


Lesenswert?

Im Makefile - falls vorhanden. Sonst Projekteinstellungen der IDE.

von Karl H. (kbuchegg)


Lesenswert?

Hain schrieb:
> Hallo,
>
> gib es für folgendes eine Möglichkeit?

Du kannst es maximal komplett aus allen Source Code Files rausziehen und 
in deinen Projektoptionen dem Compiler beim Aufruf mitgeben.

Aber: Warum lässt du dir nicht einfach in der uart_init Funktion die 
gewünschte Baudrate als Parameter mitgeben? In der uart.c gibt es 
eigentlich keinen wirklichen Grund, warum das als Makro verfügbar sein 
müsste.

>>>uart.h
> #ifndef BAUD
> #warning UART_BAUD not defined. Set UART_BAUD 9600
> #define BAUD 9600
> #endif

wie sich immer wieder rausstellt ist das keine gute Idee. Wenn so ein 
Makro nicht definiert ist, dann sollte das ernsthaft ein einen Fehler 
münden und nicht in Default-Annahmen. Denn die Praxis zeigt, dass 
derartige Warnungen leider immer wieder einfach ignoriert werden. Mit 
allen daraus folgenden Konsequenzen.

von Mark B. (markbrandis)


Lesenswert?

Warum nicht die Baudrate in einer Header-Datei definieren, und diese 
eben dort inkludieren wo sie benötigt wird?

von dotm (Gast)


Lesenswert?

ja zb so:

main.h:
#define BAUD 9600
#include "uart.h"
main.c:
#include main.h

oder auch so:

main.c:
#define BAUD 9600
#include "uart.h"

oder so:

main.h:
#define BAUD 9600

main.c:
#include "main.h"
#include "uart.h"

manchen Compilern ist es sogar Powidl was zuerst inkludiert oder 
definiert wird.

von dotm (Gast)


Lesenswert?

Karl Heinz schrieb:
> Du kannst es maximal komplett aus allen Source Code Files rausziehen und
> in deinen Projektoptionen dem Compiler beim Aufruf mitgeben.

Das ist dann wirklich von Compiler zu Compiler unterschiedlich. Meiner 
Frisst das alles und manchmal sogar zuviel.
(uvision)
letztens hatte ich z:b. das Problem dass ein vollständig aufgelöstes 
Makro nach #undef und #define wieder zum Makro wird.

von Phantomix X. (phantomix)


Lesenswert?

dotm schrieb:
> manchen Compilern ist es sogar Powidl was zuerst inkludiert oder
> definiert wird.

Würde mich sehr wundern, da es das ganze wohldefinierte Konzept 
durcheinanderbrächte.

Hier, wie ich es lösen würde:

config.h
1
#ifndef CONFIG_H
2
#define CONFIG_H
3
#include "chip_specific_headerfile.h"
4
5
#define UART_BAUDRATE 9600    //"BAUD" finde ich doof, da es noch andere peripherals geben koennte
6
7
#endif //CONFIG_H

uart.h
1
#ifndef UART_H
2
#define UART_H
3
#include "config.h"
4
5
//Deklarationen der UART-Funktionen
6
7
#endif //UART_H

uart.c
1
#include "uart.h"
2
3
//Definitionen der UART-Funktionen und -Variablen

main.c
1
#include "config.h"
2
#include "uart.h"
3
4
int main(void)
5
{
6
    //Aufruf der UART-Funktionen
7
8
    while(1);
9
}

von Hain (Gast)


Lesenswert?

#ifndef UART_H
#define UART_H

Was bedeutet das eigentlich, wird dauerhaft mit angelegt. Ich würde es 
so deuten,

dass ich eine h-Datei nicht doppelt laden kann. Soll heißen, wenn 
bereits bekannt, inkludiere keine weiteren Zeilen innerhalb der 
Präprozessordefenition.

von Phantomix X. (phantomix)


Lesenswert?

Exakt. Das wird auch manchmal "Include guards" genannt und hat den 
Vorteil, dass eine Headerdatei nur einmal includiert wird.

Hinweis: Man muss ich vor Augen führen, dass dies immer für jede C-Datei 
einzeln gilt. Im obigen Beipiel wird also sowohl uart.h als auch 
config.h zweimal includiert (da ich zwei c-Dateien habe, die das tun)

von dotm (Gast)


Lesenswert?

Phantomix Ximotnahp schrieb:
> Würde mich sehr wundern, da es das ganze wohldefinierte Konzept
> durcheinanderbrächte.

Ich hab in letzter Zeit die Erfahrung gemacht dass das Verhalten des 
Präprozessors sich stark unterscheidet.
Beispiel:

werte.h:
1
#define WERT_MODULA 100
2
#define WERT_MODULB 200


modula.h
1
#include "werte.h"
2
#undef MAKE(x)
3
#define MAKE(x) x+WERT_MODULA
4
5
#define TEST_MODULA MAKE(1)

modulb.h
1
#include "werte.h"
2
#undef MAKE(x)
3
#define MAKE(x) x+WERT_MODULB
4
5
#define TEST_MODULB MAKE(1)

main.c:
1
#include "modula.h"
2
#include "modulb.h"
3
4
printf("modula: %d\r", TEST_MODULA);
5
printf("modulb: %d\r", TEST_MODULB);

eclipse makroexpansion sagt
101
201
chide sagt
101
201
uvision sagt
101
101

von Hain (Gast)


Lesenswert?

Danke, dann passt es soweit ;)

von Phantomix X. (phantomix)


Lesenswert?

Interessant. Ich werde das morgen auf Arbeit mal testen, da hab ich 
uvision und em:blocks und und und...

von Karl H. (kbuchegg)


Lesenswert?

eclipse, chide, uvision?

Das sind doch alles IDE's und haben als solche keine relevante 
Entscheidungsfunktion.
Entscheidend ist, was der eigentliche Präprozessor, der bei einem 
Compilerlauf zum Einsatz kommt, dazu sagt.

Ich hätte eigentlich 2 mal 201 als Ausgabe erwartet und in der Tat ist 
das auch genau der Output, den man vom laufenden Programm kriegt. 
Unabhängig davon, ob eine IDE mit der richtigen Reihenfolge der 
Makrodefinition bedingt durch die include Reihenfolge klar kommt oder 
nicht.

von Phantomix X. (phantomix)


Lesenswert?

Korrekt
Zumindest der GCC übersetzt mir das auch so

main.i
1
uint8_t aaa = 1 +200;
2
uint8_t bbb = 1 +200;

von dotm (Gast)


Lesenswert?

Karl Heinz schrieb:
> Ich hätte eigentlich 2 mal 201 als Ausgabe erwartet und in der Tat ist
> das auch genau der Output, den man vom laufenden Programm kriegt.
> Unabhängig davon, ob eine IDE mit der richtigen Reihenfolge der
> Makrodefinition bedingt durch die include Reihenfolge klar kommt oder
> nicht.

ich meinte eigentlich 201 201.
Das Problem ist dass die IDE beim Codegenerieren Fehler wirft. Eventuell 
macht es dann der Compiler wieder richtig. Das geht mir bei Eclipse in 
Kombination mit µVision gehörig auf die Nerven. Allein schon wegen einem 
zwingende return. Eclipse regt sich auf und µVision wiederum sagt 
(richtigweise) statement unreacheable.
Warum eigentlich zweimal 201? Ist der Präprozessor nicht single-pass?

von Phantomix X. (phantomix)


Lesenswert?

dotm schrieb:
> Warum eigentlich zweimal 201?
1
#define WERT_MODULA 100
2
#define WERT_MODULB 200
3
4
#undef MAKE(x)
5
#define MAKE(x) x+WERT_MODULA
6
7
#define TEST_MODULA MAKE(1)
8
9
#undef MAKE(x)
10
#define MAKE(x) x+WERT_MODULB
11
12
#define TEST_MODULB MAKE(1)
13
14
uint8_t aaa = TEST_MODULA;
15
uint8_t bbb = TEST_MODULB;

Dein Problem hat eigentlich nichts mit include zu tun, daher habe ich 
das mal in einen Codeschnippsel zusammengefasst.
Das zweite undef + define MAKE(x) ist entscheidend für das was passiert.

von dotm (Gast)


Lesenswert?

Was mich auch wahsinnig macht:

char string[4] = "abcd";

chide:
ERROR: string is longer than array size

uvision nimmt es (meineserachtens korrekterweise) problemlos an

von Karl H. (kbuchegg)


Lesenswert?

Phantomix Ximotnahp schrieb:

> Das zweite undef + define MAKE(x) ist entscheidend für das was passiert.

Ich würde es so formulieren:
ENtscheidend ist, wann genau die Makroexpanion gemacht wird.
Mit einem #define wird nur gespeichert, dass eine gemacht werden soll. 
Aber es wird noch nichts ersetzt. Selbst wenn im Ersetzungstext selbst 
wieder ein Makro verwendet wird.

von dotm (Gast)


Lesenswert?

> Dein Problem hat eigentlich nichts mit include zu tun, daher habe ich
> das mal in einen Codeschnippsel zusammengefasst.
> Das zweite undef + define MAKE(x) ist entscheidend für das was passiert.

Liegt das daran dass ich ein Makro redefiniere? Das sollte ich 
eigentlich garnicht dürften. Diese Konstrukt liefert aber das gleiche 
Ergebnis:
1
#define MAKE(x) x+WERT_MAKE
2
#define WERT_MODULA 100
3
#define WERT_MODULB 200
4
5
#undef WERT_MAKE
6
#define WERT_MAKE WERT_MODULA
7
8
#define TEST_MODULA MAKE(1)
9
10
#undef WERT_MAKE
11
#define WERT_MAKE WERT_MODULB
12
13
#define TEST_MODULB MAKE(1)
14
15
uint8_t aaa = TEST_MODULA;
16
uint8_t bbb = TEST_MODULB;

Würde der Präprozessor singlepass drübergehen wäre schon bei

#define TEST_MODULA MAKE(1)
Das Symbol vollständig aufgelöst. Wozu sollte sonst #undef gut sein wenn 
die Reihenfolge nicht ausschlaggebend ist?

von Phantomix X. (phantomix)


Lesenswert?

Karl Heinz schrieb:
> Ich würde es so formulieren:
> ENtscheidend ist, wann genau die Makroexpanion gemacht wird.

Das unterschreibe ich mal so und ist auch die Antwort auf deine Frage.

Die Makroexpansion wird offensichtlich zum Schluss gemacht

von Phantomix X. (phantomix)


Lesenswert?

dotm schrieb:
> Was mich auch wahsinnig macht:
>
> char string[4] = "abcd";
>
> chide:
> ERROR: string is longer than array size
>
> uvision nimmt es (meineserachtens korrekterweise) problemlos an

"abcd" ist aber 5 characters lang ;-)

Immer die '\0' mit berücksichtigen!

von Karl H. (kbuchegg)


Lesenswert?

Phantomix Ximotnahp schrieb:
> Karl Heinz schrieb:
>> Ich würde es so formulieren:
>> ENtscheidend ist, wann genau die Makroexpanion gemacht wird.
>
> Das unterschreibe ich mal so und ist auch die Antwort auf deine Frage.
>
> Die Makroexpansion wird offensichtlich zum Schluss gemacht

Genau

Die komplette Ersetzung, bis runter zu den Zahlenwerten findet erst hier
1
uint8_t aaa = TEST_MODULA;
statt. Und an dieser Stelle gelten die gerade 'akiven' Makros. So etwas 
wie eine Umgebung, in der ein Makro definiert wurde, eine Bindung zur 
Defintionszeit, gibt es in C nicht.

von Karl H. (kbuchegg)


Lesenswert?

dotm schrieb:
>> Dein Problem hat eigentlich nichts mit include zu tun, daher habe ich
>> das mal in einen Codeschnippsel zusammengefasst.
>> Das zweite undef + define MAKE(x) ist entscheidend für das was passiert.
>
> Liegt das daran dass ich ein Makro redefiniere?

Nein.
Es liegt daran, dass du IDE und Compiler durcheinander würfelst.

Das eine ist ein Editor, der Syntax-Highlighting bzw. Querverbindungen 
im Code auswerten, macht.
Und das andere ist der C-Compiler, der sich an die normierten C-Regeln 
halten muss.

Was immer dir dein Editor anzeigt .... das ist das was der Editor dir 
anzeigt. Das kann identisch zu dem sein, was ein Compiler auch 
ermittelt, muss es aber nicht sein.

von dotm (Gast)


Lesenswert?

Phantomix Ximotnahp schrieb:
> "abcd" ist aber 5 characters lang ;-)
>
> Immer die '\0' mit berücksichtigen!

Richtig. Der Abschluss mit einem Null ist aber bei Definition optional!

von Kai S. (zigzeg)


Lesenswert?

dotm schrieb:
> Was mich auch wahsinnig macht:
>
> char string[4] = "abcd";
>
> chide:
> ERROR: string is longer than array size
>
> uvision nimmt es (meineserachtens korrekterweise) problemlos an

Falsch. Ein C "string" wird automatisch mit 0 abgeschlossen. Also 
braucht er 5 Bytes:
1
char string[5] = "abcd";
oder einfacher
1
char string[] = "abcd";

Soll keine abschliessende 0 angehaengt werden, muss man leider 
schreiben:
1
char string[4] = {'a', 'b', 'c', 'd'};

ZigZeg

von Phantomix X. (phantomix)


Lesenswert?

dotm schrieb:
> Richtig. Der Abschluss mit einem Null ist aber bei Definition optional!

Ähm. Wenn du "abcd" schreibst, ist '\0' mit dabei. Auch wenn du es nicht 
hingeschrieben hast. "abcd" ist 5 characters lang.

von Karl H. (kbuchegg)


Lesenswert?

dotm schrieb:

> Würde der Präprozessor singlepass drübergehen wäre schon bei
>
> #define TEST_MODULA MAKE(1)
> Das Symbol vollständig aufgelöst.

Da ist überhaupt nichts aufgelöst.
Du dichtest dem Präprozessor Fähigkeiten an, die er nicht hat.
Wenn der Präprozessor diese #define Zeile zu Gesicht bekommt, dann trägt 
er sich in seinen internen Tabellen ein, dass der Text 'TEST_MODULA' in 
weiterer Folge im Source Code durch den Text 'MAKE(1)' zu ersetzen ist. 
Mehr passiert da nicht.

> Wozu sollte sonst #undef gut sein

Dann schmeisst der Präprozessor aus seinen internen Tabellen wieder 
raus, dass es einen Ersetzungstext für MAKE gibt. Mehr passiert da 
nicht.

von Karl H. (kbuchegg)


Lesenswert?

Kai S. schrieb:
> dotm schrieb:
>> Was mich auch wahsinnig macht:
>>
>> char string[4] = "abcd";
>>
>> chide:
>> ERROR: string is longer than array size
>>
>> uvision nimmt es (meineserachtens korrekterweise) problemlos an
>
> Falsch. Ein C "string" wird automatisch mit 0 abgeschlossen.

Nicht ganz

1
char c[4] = "abcd";

ist tatsächlich korrekt. In diesem Fall findet keine automatische 
Terminierung mit einem '\0' Byte im Array statt.

http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf
6.7.8 - 14, auf Seite 126
Entscheidend ist der Passus "if there is room"

von dotm (Gast)


Lesenswert?

Phantomix Ximotnahp schrieb:
> Ähm. Wenn du "abcd" schreibst, ist '\0' mit dabei. Auch wenn du es nicht
> hingeschrieben hast. "abcd" ist 5 characters lang.
1
There is one special case where the null character is not automatically appended to the array. This case is when the array size is explicitly specified and the number of initializers completely fills the array size. For example:
2
3
char c[4] = "abcd";
http://h30097.www3.hp.com/docs/base_doc/DOCUMENTATION/V40F_HTML/AQTLTBTE/DOCU_046.HTM

Karl Heinz schrieb:
> Da ist überhaupt nichts aufgelöst.
> Du dichtest dem Präprozessor Fähigkeiten an, die er nicht hat.
> Wenn der Präprozessor diese #define Zeile zu Gesicht bekommt, dann trägt
> er sich in seinen internen Tabellen ein, dass der Text 'TEST_MODULA' in
> weiterer Folge im Source Code durch den Text 'MAKE(1)' zu ersetzen ist.
> Mehr passiert da nicht.

Leider hast du da vollkommen recht. Das Problem ist dass dieses 
Verhalten nirgendwo dokumentiert ist. Dann Programmiere ich freudig vor 
mich dahin weil mir der Eclipse Indexer das Blaue vom Himmel verspricht 
und bekomme dann vom Compiler den Hammer drüber.
Hast du das irgendwo schriftlich?

von dotm (Gast)


Lesenswert?

dotm schrieb:
> Hast du das irgendwo schriftlich?

Ahja...

Seh schon..

Pff..

von Karl H. (kbuchegg)


Lesenswert?

dotm schrieb:

> Hast du das irgendwo schriftlich?

Ich würde mal sagen

http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf

6.10.1 - 7, Seite 147
1
7 The preprocessing tokens within a preprocessing directive are
2
  not subject to macro expansion unless otherwise stated.

Und bei 6.3.10 Macro Replacement ist da auch keine Ausnahme dafür 
angeführt.

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz schrieb:
> dotm schrieb:
>
>> Hast du das irgendwo schriftlich?
>
> Ich würde mal sagen
>
> http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf
>
> 6.10.1 - 7, Seite 147
>
1
> 7 The preprocessing tokens within a preprocessing directive are
2
>   not subject to macro expansion unless otherwise stated.
3
>
>
> Und bei 6.3.10 Macro Replacement ist da auch keine Ausnahme dafür
> angeführt.


6.10.3 - 7 geht in eine ähnliche Richtung
1
Semantics
2
7 The identifier immediately following the define is called the macro
3
  name. There is one name space for macro names.

von dotm (Gast)


Lesenswert?

Karl Heinz schrieb:
> Und bei 6.3.10 Macro Replacement ist da auch keine Ausnahme dafür
> angeführt.

leider. muss ich dir recht geben.

von Kai S. (zigzeg)


Lesenswert?

Karl Heinz schrieb:
> http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf
> 6.7.8 - 14, auf Seite 126
> Entscheidend ist der Passus "if there is room"

Wow. Man lernt nie aus :-)

ZigZeg

von Phantomix X. (phantomix)


Lesenswert?

Kai S. schrieb:
> Karl Heinz schrieb:
>> http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf
>> 6.7.8 - 14, auf Seite 126
>> Entscheidend ist der Passus "if there is room"
>
> Wow. Man lernt nie aus :-)
>
> ZigZeg

Dito.

Ich kann mir auch schon genau vorstellen, wieso diese Ausnahme so 
definiert ist. Weil es sonst jedem zweiten Entwickler mal passiert wäre, 
ein Byte zu wenig definiert zu haben, was dann einem Buffer Overflow 
gleichkommt :D Allerdings ist ein unterminierter C-String auch nicht 
wirklich schön. Nur beim Debuggen merkt man den schneller...

von Karl H. (kbuchegg)


Lesenswert?

Kai S. schrieb:
> Karl Heinz schrieb:
>> http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf
>> 6.7.8 - 14, auf Seite 126
>> Entscheidend ist der Passus "if there is room"
>
> Wow. Man lernt nie aus :-)

Nicht falsch verstehen. Ich bin völlig auf deiner Seite, wenn du
1
  char c[] = "abcd";
propagierst.

Der springende Punkt ist aber, dass in C die Unterscheidung zwischen 
Strings und Bytefeldern sich im Prinzip nur an dieser \0 Terminierung 
stösst.

Mittels
1
  char c[4] = "abcd";
definiert man sich im Grunde ein Bytefeld bekannter Länge, in dem 
bestimmte Werte stehen. c als String aufzufassen, wäre vom Konzept her 
recht fatal. In so einem Fall sind alle str.... Funktionen tabu und 
müssen durch die entsprechenden mem.... Funktionen ersetzt werden.
(Ich bin vielleicht nicht repräsentativ. Aber IMHO braucht so eine 
Initialisierung wirklich niemand. Das ist nur eine Stolperfalle, wie so 
einige andere 'obskure' C Regeln auch. Aber ... damit muss man leben.)

von dotm (Gast)


Lesenswert?

Kai S. schrieb:
> Wow. Man lernt nie aus :-)

Ja hier sind einige äusserst fix in diesen Dingen.
Möge es noch lange so sein.

von dotm (Gast)


Lesenswert?

Karl Heinz schrieb:
> Mittels  char c[4] = "abcd";
> definiert man sich im Grunde ein Bytefeld bekannter Länge, in dem

Ich mache damit dann noch wesentlich schwindlichere Konstruke wie zb 
einen uint32_t pointer drübercasten um damit einen schnellen 
Konsolencommandodecoder zu realisieren. Ist sicher alles nicht ganz 
koscher aber dafür halt sehr flott am MCU. Und wenn da steht ich darf 
das dann will ich auch dass der Compiler die Schnauze hält und pariert 
:)

von Karl H. (kbuchegg)


Lesenswert?

dotm schrieb:

> koscher aber dafür halt sehr flott am MCU. Und wenn da steht ich darf
> das dann will ich auch dass der Compiler die Schnauze hält und pariert
> :)

Oooch. Der Compiler hält da normalerweise schon die Schnauze.
Aber die Parsing-Fähigkeiten diverser Editoren zum Syntax-Highlighting 
bzw. für andere Komfort-Funktionen, lassen dann eben des öfteren zu 
wünschen übrig.

Bei einem Compiler ist das recht einfach zu gewährleisten. Jeder 
Hersteller hat da seine umfangreichen Sammlung an "Testprogrammen", die 
durch den Compiler gehen müssen. Ehe ein Compiler raus geht, muss er 
sich da normalerweise erst mal stundenlang mit diesen Tests 
beschäftigen, die sich auch gut auswerten lassen. Das stellt zum einen 
sicher, dass sich nicht unbemerkt Fehler in den Compiler eingeschlichen 
haben die vorher nicht da waren und gewährleistet andererseits dass im 
Laufe der Zeit auch noch jedes noch so obskure Stück syntaktischer 
Zucker in den Compiler kommt und der Compiler zeigen muss, dass er damit 
umgehen kann. Der Tei list eigentlich recht einfach automatisch zu 
testen. Schwieriger ist die Qualitätskontrolle, wenn am Optimizer 
Veränderungen vorgenommen werden.

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.