Forum: Compiler & IDEs C Array: Implizite Null-Initialisierung abfangen


von Frank Roggel (Gast)


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?

von Random .. (thorstendb) Benutzerseite


Lesenswert?

Besser: Mit NULL initialisieren und im Code initialisieren und als 
Fehlerfall prüfen. Dann fliegt auch einem unbedarften späteren 
Entwickler der Kram nicht um die Ohren.

: Bearbeitet durch User
von devzero (Gast)


Lesenswert?

Lass ANZAHLELEMENTE doch einfach weg. Dann ist das Array so gross, wie 
Du Elemente drin hast. Im if benutzt du dann sizeof.

von mh (Gast)


Lesenswert?

Frank Roggel schrieb:
> Frage:
> Kann man diese implizite Null-Initialisierung ausschalten oder
> wenigstens eine Warnung ausgeben lassen?

Was soll erwartest du als Ergebnis, wenn der dritte Wert nicht 
initialisiert wird?

von Irgendwer (Gast)


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
}

von Johann L. (gjlayde) Benutzerseite


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.

von Dirk B. (dirkb2)


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.

von Andreas S. (Firma: Schweigstill IT) (schweigstill) Benutzerseite


Lesenswert?

Da die Funktion zugriff() einen void-Rückgabetyp besitzt, jedoch in der 
Funktion eine Rückgabe eines int32_t * erfolgt, wird das ganze auch 
nicht kompilieren. Dann ist es auch völlig egal, ob irgendwelche 
Arraygrenzen überschritten oder falsch initialisierte Werte 
zurückgegeben würden.

von Dirk B. (dirkb2)


Lesenswert?

Irgendwer schrieb:
> Beim aufruf von zugriff muss dann natürlich geprüft werden ob der
> Rückgabewert überhaupt gültig ist bevor er verwendet werden darf:

Wenn bei ungültigen Feldern eine 0 drin steht, kann er auch direkt auf 
das Array zugreifen.
Da bringt deine Funktion gar nichts.

von x^y (Gast)


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.

von DPA (Gast)


Lesenswert?

Johann L. schrieb:
> _Static_assert

Wenn man die assert.h einbindet, kann man auch einfach "static_assert" 
schreiben.

von Dirk B. (dirkb2)


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.

von Rolf M. (rmagnus)


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.

von Kaj (Gast)


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?

von Rolf M. (rmagnus)


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.

von Frank Roggel (Gast)


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.

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.