Hallo zusammen,
ich habe eine eher allgemeine Frage zu Arrays in C.
Ich schreibe gerade ein Programm, dass ein Struktur-Array mittels define
anlegt:
1
#define FELDER 10
2
3
typedefstruct
4
{
5
uint8_tu8_Option1;
6
uint8_tu8_Option2;
7
8
}meine_struktur_t;
9
10
meine_strukur_ts_struktur[FELDER];
Jetzt ist es im Programm realistisch möglich, dass FELDER mit 0
definiert wird (im Beispiel wäre es blöd, aber ist halt nur ein
Beispiel). Das bemängelt C beim Compilieren auch nicht. Ich frage mich
wie das im Speicher aussieht ;-) Wird da überhaupt Speicherplatz
reserviert? Ein Array mit 0 Feldern kann ja gar nicht verwendet
werden...
Wie würdet Ihr den Fall abfangen? Ich tendiere jetzt dazu, im Falle von
FELDER == 0 mindestens eine Struktur anzulegen:
1
#define FELDER 10
2
3
typedefstruct
4
{
5
uint8_tu8_Option1;
6
uint8_tu8_Option2;
7
8
}meine_struktur_t;
9
10
#if (FELDER > 0)
11
meine_strukur_ts_struktur[FELDER];
12
#else
13
meine_strukur_ts_struktur[1];
14
#endif
Im Hauptprogramm wird vor Benutzung des Arrays natürlich auch geprüft,
ob FELDER > 0 ist. Geschrieben/Gelesen wird nur, wenn es überhaupt
FELDER gibt. Aber nichtsdestotrotz macht mich ein Null-Felder-Array
nervös ;-)
Gruß,
André
Das "Nullte" Element eines Arrays ist ein Pointer auf den Begin des
Arrays. Das bedeutet, die Variable die du für das Arrays anlegst ist
letztlich ein Pointer eines bestimmten Typen, in deinem fall eine
Struct.
Das heißt ein Array besteht aus einem Zeiger auf das Nullte Argument und
den tatsächlich reservierten Speicherstellen.
Gibst du also Null Speicherstellen an, hast du nur noch einen Pointer
vom angegebenen Typ.
Das kann man z.B. nutzten, wenn man zur Compilezeit nur weiß, DASS es
ein Array geben soll, aber noch nicht mit wie viel Speichertiefe. Dann
macht man einen Pointer vom entsprechenden Typ, dem dann über malloc,
new, was_auch_immer speicherstellen zugewiesen werden können. Danach
lässt sich der zeiger mit einem Arrayindex verwenden.
Ein Array ist kein Pointer. (Auch ein Array der Größe 0 nicht)
Die Adresse von einem Array kann man nach der Definition nicht mehr
ändern.
ISO-C verbietet Arrays der Größe 0.
gcc meckert das auch an bei -pedantic
sizeof() liefert dafür auch 0
Da offensichtlich ein Fehler vorliegt:
Im Beispiel ist es ein klarer Fehler, im realen Programm aber eine
zulässige Definition. Wäre schön, wenn ich es so leicht abfangen könnte
;-)
Klingt so, als wäre mein Lösungsansatz (Array dann sicherheitshalber mit
1 anlegen) schon der vielversprechendste.
André Wippich schrieb:> im realen Programm aber eine zulässige Definition.
Wie das?
Dann musst du per #if #endif alles ausklammern, denn das Array benutzen
kannst du nicht wenn es die Größe 0 hat.
Insofern würde ich statt "zulässige Definition" eher "versteckter
Fehler" sagen.
War dein Code nur ein Fallbeispiel und du willst letztlich dynamisch
allokieren?
Wenn es statisch mittels #define bleiben soll, kannst du auch das
"define´te" FELDER nur einmal am Anfang mit Präprozessordirektiven
abfragen und behandeln. Das hält den Code besser lesbar.
von zu vielen #ifdef´s im laufenden Code kriegt man Augenkrebs...
Chefkoch schrieb:> War dein Code nur ein Fallbeispiel und du willst letztlich dynamisch> allokieren?>> Wenn es statisch mittels #define bleiben soll, kannst du auch das> "define´te" FELDER nur einmal am Anfang mit Präprozessordirektiven> abfragen und behandeln. Das hält den Code besser lesbar.>> von zu vielen #ifdef´s im laufenden Code kriegt man Augenkrebs...
Der Code ist wirklich nur ein Minimal-Fallbeispiel. Ich will nichts
dynamisch allokieren, sondern habe ein recht großes bestehendes
Programm, dass durch Erweiterungen auch mit dem neuen Sonderfall 0
FELDER klar kommen muss. In dem Fall wird das Array vom Programm nicht
verwendet, da überall vorher gepürft (werden) wird, ob FELDER > 0 ist.
Die Frage war nur, wie lege ich das ungenutzte Array an, so dass es
keinen Unsinn im Speicher anstellt ;-)
Auf haufenweise #ifdefs möchte ich wirklich verzichten. Wenn ich das
Array einfach mit mindestens einem Feld anlegen lasse und dann nicht
verwende bin ich ja an allen Fronten auf der sicheren Seite...
André Wippich schrieb:> FELDER klar kommen muss. In dem Fall wird das Array vom Programm nicht> verwendet
d.h. du schaltest effektiv Funktionalität ab.
Genau so wird das üblicherweise gehandhabt, dass man das ganze so
ansieht, ob man eine bestimmte Funktionalität auch haben will und die
entsprechenden Abfragen (ob mit #if oder anders) in genau diese Richtung
formuliert: Funktionalität ist eingeschaltet.
> da überall vorher gepürft (werden) wird, ob FELDER > 0 ist.
denn dabei wird es nicht bleiben. Oft gibt es dann auch noch andere
Variablen die überflüssig geworden sind, wenn nicht sogar ganze
Funktionen wegfallen, wenn besagte Funktionalität abgeschaltet wird.
um in einer UART Lib den Ringbuffer loszuwerden, setzt man nicht die
Größe des Ringbuffers auf 0, sondern man hat ein
1
#define USE_RINGBUFFER
entweder hängt man dann die Inklusion des Ringbuffers an die Existenz
des Makros (in dem Fall wird dann das Makro auskommentiert um die
Funktionalität loszuwerden) oder an bestimmte Werte (meist 0 oder 1) mit
entsprechenden #if
Wenn es aber einen Ringbuffer gibt, angezeigt durch das Makro
USE_RINGBUFFER, dann hat seine Größe auch nicht 0 zu sein.
Karl Heinz schrieb:> um in einer UART Lib den Ringbuffer loszuwerden, setzt man nicht die> Größe des Ringbuffers auf 0, sondern man hat ein>
1
>#defineUSE_RINGBUFFER
2
>
> ...> Wenn es aber einen Ringbuffer gibt, angezeigt durch das Makro> USE_RINGBUFFER, dann hat seine Größe auch nicht 0 zu sein.
Uff, Du hast natürlich Recht. Manchmal sieht man den Wald vor lauter
Bäumen nicht ;-)
Wenn du das dann übersetzt (mit FELDER==0) bekommst du überall
Compilerfehler, wo du auf die Struktur zugreifst. Damit weisst du
automatisch wo das #if noch hin muss.
Es darf ja dann keinen Code mehr geben, der deine Struktur verwendet.
>> Wenn du das dann übersetzt (mit FELDER==0) bekommst du überall> Compilerfehler, wo du auf die Struktur zugreifst. Damit weisst du> automatisch wo das #if noch hin muss.> Es darf ja dann keinen Code mehr geben, der deine Struktur verwendet.
Wäre machbar, aber ich möchte "#if #endif" im Programmcode lieber
vermeiden/minimal halten. Ich finde das ab einer bestimmten Komplexität
zu schlecht lesbar.
André Wippich schrieb:> Wäre machbar, aber ich möchte "#if #endif" im Programmcode lieber> vermeiden/minimal halten. Ich finde das ab einer bestimmten Komplexität> zu schlecht lesbar.
Der Preis den du dafür bezahlst (nichts ist umsonst) ist, dass du oder
jemand anderes eventuell irgendwo ein Test
1
if(FELDER>0){
2
...
3
}
vergisst und es dann zur Laufzeit einen Fehler gibt.
Daher sehe ich nicht, warum statt dessen an den selben Stellen ein
1
#if(FELDER > 0)
2
...
3
#endif
schlechter lesbar sein soll. Wie du selber schreibst, testen musst du
sowieso.
Was du in beiden Fällen machen kannst ist den Code umzustrukturieren, so
dass du nur wenige Stellen hast an denen auf das Array zugegriffen wird.
Am besten ein paar wenige Funktionen, isoliert in einer einzigen Datei.
Hat man erst mal alle möglichen Operationen auf das Array in ein paar
Funktionen gegossen, kann man das Ganze noch steigern. Man implementiert
zwei Versionen jeder Operation, für FELDR == 0 und für FELDER > 0. Dann
wählt man mit einem einzigen #if den jeweils richtigen Satz von
Funktionen aus. Das lohnt sich nur, wenn der Aufwand zwei Versionen zu
schreiben vertret bar ist. Zum Beispiel, wenn die Implementierungen sehr
unterschiedlich sind, man im anderen Fall also sehr viele #if in eine
Funktion einstreuen müsste.