mikrocontroller.net

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


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
Autor: Frank Roggel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,

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

uint32_t* adressen[ANZAHLELEMENTE] = { INITIALISIERUNG };

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

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?

Autor: Random .. (thorstendb) Benutzerseite
Datum:

Bewertung
1 lesenswert
nicht 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
Autor: devzero (Gast)
Datum:

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

Autor: mh (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Irgendwer (Gast)
Datum:

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

Beim aufruf von zugriff muss dann natürlich geprüft werden ob der 
Rückgabewert überhaupt gültig ist bevor er verwendet werden darf:
uint32_t* x = zugriff(2);
if (x != 0)
{
  x kann verwendet werden 
  ...
}
else
{
  Fehler aufgetreten
}

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
1 lesenswert
nicht lesenswert
#define ARRAY_SIZE(X) (sizeof(X) / sizeof(*(X)))

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

oder
_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):
__extension__
_Static_assert (ANZAHLELEMENTE == ARRAY_SIZE (adressen), "Bug");

Falls _Static_assert überhaupt nicht verfügbar ist, dann gehen 
Konstrukte wie
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.

Autor: Dirk B. (dirkb2)
Datum:

Bewertung
1 lesenswert
nicht 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.

Autor: Andreas S. (Firma: Schweigstill IT) (schweigstill) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Dirk B. (dirkb2)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: x^y (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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
#define COUNT(n)   (sizeof(n)/sizeof(n[0]))

und schließlich:
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.

Autor: DPA (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Johann L. schrieb:
> _Static_assert

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

Autor: Dirk B. (dirkb2)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Rolf M. (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Kaj (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Rolf M. (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Frank Roggel (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.