C Array: Implizite Null-Initialisierung abfangen

Gast #5889720
Lesenswert?

Hallo zusammen,

Beispiel:
1
#define ANZAHLELEMENTE     2
2
#define INITIALISIERUNG    0x8000, 0x8100
3

4
uint32_t* adressen[ANZAHLELEMENTE] = { INITIALISIERUNG };
5

6
void zugriff(int element)
7
{
8
  if (element < ANZAHLELEMENTE) return adressen[element];      
9
}

Wenn nun ANZAHLELEMENTE von 2 auf 3 erhöht wird, ohne, dass bei 
INITIALISIERUNG ein dritter Wert eingetragen wird, wird das dritte 
Element im Array automatisch auf 0 gesetzt.
Heute hatten wir so einen Fall, was dann zu einem Crash geführt hat, 
weil auf eine illegale Adresse (0) zugegriffen wurde.

Frage:
Kann man diese implizite Null-Initialisierung ausschalten oder 
wenigstens eine Warnung ausgeben lassen?
Gast #5889758
Lesenswert?

deine Funktion hat sowieso zwei fehler. Das "void & return" und das 
nicht festgelegt ist was passiert wenn "elemente" ungültig ist.
1
uint32_t* zugriff(int element)
2
{
3
  if ( (element < ANZAHLELEMENTE) && (adressen[element] != 0) )
4
    // Ok
5
    return adressen[element];
6
  else
7
    // Fehler      
8
    return 0;
9
}

Beim aufruf von zugriff muss dann natürlich geprüft werden ob der 
Rückgabewert überhaupt gültig ist bevor er verwendet werden darf:
1
uint32_t* x = zugriff(2);
2
if (x != 0)
3
{
4
  x kann verwendet werden 
5
  ...
6
}
7
else
8
{
9
  Fehler aufgetreten
10
}
Persönliche Seite #5889765
Lesenswert?

1
#define ARRAY_SIZE(X) (sizeof(X) / sizeof(*(X)))
2

3
_Static_assert (ANZAHLELEMENTE == ARRAY_SIZE (adressen), "Bug");

oder
1
_Static_assert (ANZAHLELEMENTE == ARRAY_SIZE ((uint32_t*[]) { INITIALISIERUNG }), "Bug");

Falls _Static_assert noch nicht unterstützt wird, dann teilweise bereits 
als Spracherweiterung.  In dem Fall (GCC):
1
__extension__
2
_Static_assert (ANZAHLELEMENTE == ARRAY_SIZE (adressen), "Bug");

Falls _Static_assert überhaupt nicht verfügbar ist, dann gehen 
Konstrukte wie
1
char array[ANZAHLELEMENTE == ARRAY_SIZE (adressen) ? 1 : -1];

und im praktischen Einsatz mit static __attribute__((unused)).  Das gibt 
natürlich keine so schöne Fehlermeldung wie bei _Static_assert.
#5889775
Lesenswert?

Frank Roggel schrieb:
> Heute hatten wir so einen Fall, was dann zu einem Crash geführt hat,
> weil auf eine illegale Adresse (0) zugegriffen wurde.

NULL beseutet ungültige Adresse.
Das Element im Array wurde nicht initialisiert, also ist es ungültig.

Um den Fehler abzufangen muss darauf im Code reagiert werden!
Ob das Programm abstürzt, weil du auf NULL zugreifst oder eine andere 
ungültige Adresse ist egal.
Bei NULL hast du die Chance es zu erkennen.
Gast #5889815
Lesenswert?

devzero schrieb:
> Lass ANZAHLELEMENTE doch einfach weg. Dann ist das Array so gross, wie
> Du Elemente drin hast.

Exakt. Dazu hilft noch ein Makro
1
#define COUNT(n)   (sizeof(n)/sizeof(n[0]))

und schließlich:
1
uint32_t* adressen[] = { INITIALISIERUNG };
2

3
uint32_t zugriff(int element)
4
{
5
   uint32_t result = 0;
6
   if ((element >= 0) && (element < COUNT(adressen)))
7
      result = adressen[element];
8
   return result;
9
}

Sofern negative Indizes hier ohnehin nicht definiertes Verhalten sind, 
bietet sich uint32_t für element an.
#5889852
Lesenswert?

x^y schrieb:
> uint32_t* adressen[] = { INITIALISIERUNG };
>
> uint32_t zugriff(int element)
> {
>    uint32_t result = 0;
>    if ((element >= 0) && (element < COUNT(adressen)))
>       result = adressen[element];
>    return result;
> }
>
> Sofern negative Indizes hier ohnehin nicht definiertes Verhalten sind,
> bietet sich uint32_t für element an.

Das haut nicht hin.
In dem Array werden Adressen gespeichert.
Darum muss result auch vom Typ dieser Adressen sein. Und der 
Rückgabewert der Funktion auch.
#5890173
Lesenswert?

Frank Roggel schrieb:
> Wenn nun ANZAHLELEMENTE von 2 auf 3 erhöht wird, ohne, dass bei
> INITIALISIERUNG ein dritter Wert eingetragen wird, wird das dritte
> Element im Array automatisch auf 0 gesetzt.
> Heute hatten wir so einen Fall, was dann zu einem Crash geführt hat,
> weil auf eine illegale Adresse (0) zugegriffen wurde.
>
> Frage:
> Kann man diese implizite Null-Initialisierung ausschalten

Dann würde das dritte Element eben einen zufälligen Wert haben. 
Inwiefern ist das besser als eine definierte 0?

> oder wenigstens eine Warnung ausgeben lassen?

Ich könnte schwören, so eine Warnung mal gesehen zu haben, aber finde 
sie nicht mehr. ich glaube, es gibt sie nur für Elemente von Strukturen, 
nicht von Arrays.
Gast #5890342
Lesenswert?

Frank Roggel schrieb:
> Wenn nun ANZAHLELEMENTE von 2 auf 3 erhöht wird, ohne, dass bei
> INITIALISIERUNG ein dritter Wert eingetragen wird, wird das dritte
> Element im Array automatisch auf 0 gesetzt.
> Heute hatten wir so einen Fall, was dann zu einem Crash geführt hat,
> weil auf eine illegale Adresse (0) zugegriffen wurde.
Falsch. Das Programm stuerzt nicht ab, weil auf eine (illegale) Adresse 
zugegriffen wird. Es muss nicht mal abstuerzen, denn ihr habt da 
undefined behavior gebaut.
Der Pointer wird zwar (impliziet) mit 0 initialisiert, aber die 
dereferenzierung eines NULL Pointers ist UB.

Gegenfrage: Mit was soll die Adresse denn sonst initialisiert werden, 
wenn nichts angegeben wird? Dann ist es ein nicht initialisierter 
Pointer. Damit haette das Programm auch abstuerzen koennen, oder auch 
nicht. Denn auch das ist UB.

Frank Roggel schrieb:
> Frage:
> Kann man diese implizite Null-Initialisierung ausschalten oder
> wenigstens eine Warnung ausgeben lassen?
Ihr koennt einfach euren Code reparieren, wie waer's damit?
#5890438
Lesenswert?

Kaj schrieb:
>> Heute hatten wir so einen Fall, was dann zu einem Crash geführt hat,
>> weil auf eine illegale Adresse (0) zugegriffen wurde.
> Falsch. Das Programm stuerzt nicht ab, weil auf eine (illegale) Adresse
> zugegriffen wird.

Wie kommst du darauf? Woher weißt du, warum das Programm abgestürzt ist?

> Es muss nicht mal abstuerzen, denn ihr habt da undefined behavior gebaut.
> Der Pointer wird zwar (impliziet) mit 0 initialisiert, aber die
> dereferenzierung eines NULL Pointers ist UB.

… und hat in diesem Fall offenbar zum Absturz geführt.
Gast #5892024
Lesenswert?

Johann L. schrieb:
> #define ARRAY_SIZE(X) (sizeof(X) / sizeof(*(X)))
> _Static_assert (ANZAHLELEMENTE == ARRAY_SIZE (adressen), "Bug");
Perfekt, das tut's! Danke!

Irgendwer schrieb:
> deine Funktion hat sowieso zwei fehler. Das "void & return" und das
Ja stimmt, sorry. Hab das nur als Beispiel runtergetippt, da tatsächlich 
Situation, ist etwas komplexer.

Kaj schrieb:
> Falsch. Das Programm stuerzt nicht ab, weil auf eine (illegale) Adresse
> zugegriffen wird.
Konkret gab es einen Trap/Exception.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren