[C]unitialisierte variablen

dettus

Bicycle User
herakles hatte in http://www.bsdforen.de/showthread.php?t=28112 den wunsch geaeussert einen neuen thread dazu aufzumachen. dort ist eine diskussion leicht off topic geworden.

ausgangspunkt war folgender code
Code:
int main(void){
    int i;

   ++i %= 5;
   return 0;
}
dieser fuehrte zu compiler-problemen, richtig(er) waere hier folgendes gewesen:
Code:
int main(void){
    int i;

   ++i;
   i %= 5;
   return 0;
}

bei der seitendiskussion ging es jetzt um die frage nach dem
Code:
int i;

was eine variable anlegt, diese aber nicht initalisiert. nun, wie schon im original erwaehnt, wird das ganze IMPLIZIT bei modernen compilern mit 0 inialisiert.

bei aeltern, und auch bei einigen selbstgestrickten im embedded bereich kann es allerdings vorkommen dass diese variable muell enthaelt.


wo kommt dieser muell her? antwort: vom stack. lokale variablen werden dort angelegt. nehmen wir mal an dass der stack im system bei 0x40001000 beginnt und in richtung kleiner werdende adresen waechst.

Code:
void func1()
{
   float x;    // @0x40000FFC
  int h;        // @0x40000FF8
  char y;    // @0x40000FF7
  short s;   // @0x40000FF5

  x=3.141519;
}

void func2()
{
    int i;   // @0x40000FFC
   int j;   // @0x40000FF8

  printf("%i\n",i);
}

int main(void)
{
   func1();
   func2();
}

wie man leicht sieht(tm) teilen sich nun float x und int i die gleiche speicheradresse. damit kann es vorkommen dass das printf() etwas muell ausgibt.
 
8051 C-Compiler verwenden nur für Interrupts und reentrant Funktionen (Funktionen, die mehrmals aufgerufen werden können) einen Stack, push und pop sind dort sehr teuer.

Stattdessen wird ein Call-Tree angelegt und allen Variablen werden feste Speicheradressen zugewiesen. Durch den Call-Tree wird ermittelt welche Funktionen nie gleichzeitig aufgerufen werden, deren Variablen können den gleichen Speicher verwenden (so genanntes Overlaying).

Nicht initialsierte Variablen werden aber immer mit fetten Warnings von den Compilern quittiert.
 
Kamikaze: Wer freiwillig einen 8051 programmiert schiebt sich auch freiwillig glühende Eisen in den Arsch.
 
@kamikaze: ah! wusste ich noch nicht. aber gut, das ist im prinzip ein "stack zur compilezeit". deckt sich mit meinem beispiel.

@crest: du, manchmal kann man sich seine umgebung nicht aussuchen. ein 8051 hat bei mir auf der arbeit schon ein problem geloest fuer das wir sonst 15000 euro fuer eine "professionellere" variante gezahlt haetten. :D
 
Kamikaze: Wer freiwillig einen 8051 programmiert schiebt sich auch freiwillig glühende Eisen in den Arsch.
Ich tue das Eine aber nicht das Andere. Damit bist du widerlegt.:ugly:

@kamikaze: ah! wusste ich noch nicht. aber gut, das ist im prinzip ein "stack zur compilezeit". deckt sich mit meinem beispiel.
Wie gesagt funktioniert es nicht mit Interrupts. Und beim Inline ASM muss man auch pushen und poppen, damit man nicht mit dem Compiler ins Gehege kommt.

Bei einem Interrupt sichern die Compiler alle Rn-Register (8 Stück), macht beim XC878 (den ich benutze) 64 Takte (~2.7µs) bloß zum pushen und poppen. Dafür gibt es Register-Bänke, je Interrupt Priorität braucht man eine weitere, damit sich die Interrupts nicht gegenseitig ins Gehege kommen können.
Natürlich verliert man mit jeder Register Bank 8 Byte Speicher - ein hoher Preis wenn man nur 256 Byte internen Speicher hat. Und die Register liegen auch noch in den 128 Byte direkt addressierbaren (und damit schnelleren) Speicher.
 
Code:
int i;

was eine variable anlegt, diese aber nicht initalisiert. nun, wie schon im original erwaehnt, wird das ganze IMPLIZIT bei modernen compilern mit 0 inialisiert.
Dies ist schlicht falsch. Nicht initialisierte automatische (lokale) Variablen sind schlicht ... uninitialisiert. Kein Übersetzer treibt den Zusatzaufwand, diese mit 0 zu initialisieren. Falls das doch irgendeiner tut, dann darf man da sich schlicht nicht darauf verlassen.
Nur statische (globale) Variablen werden mit 0 (des entsprechenden Typs) initialisiert.
Manche Übersetzer nutzen uninitialisierte Werte auch (völlig von der Norm abgesegnet) zum Optimieren: undef + 1 -> undef, undef * 0 -> undef (sic), undef & 0 -> undef (sic).

wo kommt dieser muell her? antwort: vom stack. lokale variablen werden dort angelegt.
C kennt den Begriff "stack" nicht -- das Wort kommt in der gesamten Spezifikation nicht vor. Wie die Konzepte von C auf eine Maschine abgebildet werden, ist der Norm schlicht egal. Für diese zählt nur, dass die Verwendung uninitialisierter Variablen zu undefiniertem Verhalten führt. Durch welche Umstände der Maschinenabbildung da effektiv irgendein Wert reingelangt ist auch Sicht von C völlig egal: undefiniert ist undefiniert.

Code:
void func1()
{
   float x;    // @0x40000FFC
  int h;        // @0x40000FF8
  char y;    // @0x40000FF7
  short s;   // @0x40000FF5

  x=3.141519;
}
Diese Sicht ist bestenfalls naiv. Lokale Variablen brauchen sich keineswegs im Keller befinden. Oder es können sich auch mehrere diesselbe Stelle teilen, falls sie nicht gleichzeitig verwendet werden. Selbst wenn die Werte von Variablen tatsächlich in den Keller ausgelagert werden (hier werden alle Variablen eh direkt verworfen), dann wird sich "s" höchstwahrscheinlich nicht an einer ungeraden Adresse befinden, sondern auf eine natürliche Adresse (sprich Vielfaches seiner Größe) ausgerichtet werden.
 
C kennt den Begriff "stack" nicht -- das Wort kommt in der gesamten Spezifikation nicht vor.
ist ja auch nicht c, sondern eine datenstruktur. :belehren:

diese Sicht ist bestenfalls naiv. Lokale Variablen brauchen sich keineswegs im Keller befinden.
naiv mit sicherheit nicht. nur leichter um einen punkt anhand eines beispieles zu illustrieren. ob sie nun im speicher liegen, oder in registern... voellig wurst. darum gings nicht!

Oder es können sich auch mehrere diesselbe Stelle teilen, falls sie nicht gleichzeitig verwendet werden.
speichst du von compileroptimierungen? mit oder ohne "stack"?

dann wird sich "s" höchstwahrscheinlich nicht an einer ungeraden Adresse befinden.
du moechtest realistische werte? okay.
Code:
x@00007fffffffe81c
h@00007fffffffe818
y@00007fffffffe817
s@00007fffffffe814
i@00007fffffffe81c
j@00007fffffffe818
du sprichst hier von alignment. aber das tat wie gesagt bei meinem beispiel nichts zur sache.
 
Zuletzt bearbeitet:
Oh, wir kriegen unseren eigenen BSDForen.de Bikeshed! Ob's jetzt wohl einen green IT Preis gewinnen?
 
ist ja auch nicht c, sondern eine datenstruktur. :belehren:
Nur den einzelnen Satz aus dem Kontext gerissen, hast du irgendwie recht. Mit Kontext ist diese Entgegnung sinnfrei.

naiv mit sicherheit nicht. nur leichter um einen punkt anhand eines beispieles zu illustrieren. ob sie nun im speicher liegen, oder in registern... voellig wurst. darum gings nicht!
Du versuchst etwas zu illustrieren, wo es nichts zu illustrieren gibt und vermittelst damit die gefährlich falsche Vorstellung, dass das Konzept "uninitialisierte Variable" inhärent an die Maschinenabbildung gebunden ist. Du hast geflissentlich den zentralen Aspekt meiner Aussage nicht zitiert: Die C-Spezifikation sagt, dass die Verwendung von uninitialisierten Variablen zu undefiniertem Verhalten führt. Werte für die Variable kommen nirgendwo her. Dort endet die Argumentationskette schlicht -- wie du auch richtig erkannt hast: "ob sie nun im speicher liegen, oder in registern... voellig wurst.". Was eine zufällige konkrete Implementierung bei der Maschinenabbildung als tatsächliches Verhalten zeigt ist aus der Sicht von C völlig egal. Das braucht nicht betrachtet werden, denn das Verhalten ist undefiniert! Es als gedankliches Bild zu verwenden ist sogar schädlich, denn das führt -- auch an anderen Stellen -- zu dem falschen Vorgehen "Was tut meine Maschine/mein Übersetzer hier?" statt die korrekte Frage "Was schreibt die Sprachspezifikation vor?" zu stellen.
Unterm Strich bleibt schlicht: Die Sprachspezifikation sagt, das Verhalten ist undefiniert, was der wichtige Aspekt ist. Was eine zufällige (und auch nicht genannte) Implementierung tut ist einfach egal.

speichst du von compileroptimierungen? mit oder ohne "stack"?
Deswegen sagte ich "naiv": Du gehst davon aus, dass es eine kanonische Maschinenabbildung mit Variablen im Keller gibt. Davon ist aber in der C-Spezifikation an keiner Stelle die Rede.

du moechtest realistische werte? okay.
Code:
x@00007fffffffe81c
h@00007fffffffe818
y@00007fffffffe817
s@00007fffffffe814
i@00007fffffffe81c
j@00007fffffffe818
du sprichst hier von alignment. aber das tat wie gesagt bei meinem beispiel nichts zur sache.
Das bestätigst meine Randbemerkung.
 
also, in meinem beispiel kams vom stack. :D
hast du ein besseres?
Liest du eigentlich, was ich schreibe?

Nochmals mit Betonung:
Tron schrieb:
Du versuchst etwas zu illustrieren, wo es nichts zu illustrieren gibt und vermittelst damit die gefährlich falsche Vorstellung, dass das Konzept "uninitialisierte Variable" inhärent an die Maschinenabbildung gebunden ist. Du hast geflissentlich den zentralen Aspekt meiner Aussage nicht zitiert: Die C-Spezifikation sagt, dass die Verwendung von uninitialisierten Variablen zu undefiniertem Verhalten führt. Werte für die Variable kommen nirgendwo her. Dort endet die Argumentationskette schlicht -- wie du auch richtig erkannt hast: "ob sie nun im speicher liegen, oder in registern... voellig wurst.". Was eine zufällige konkrete Implementierung bei der Maschinenabbildung als tatsächliches Verhalten zeigt ist aus der Sicht von C völlig egal. Das braucht nicht betrachtet werden, denn das Verhalten ist undefiniert! Es als gedankliches Bild zu verwenden ist sogar schädlich, denn das führt -- auch an anderen Stellen -- zu dem falschen Vorgehen "Was tut meine Maschine/mein Übersetzer hier?" statt die korrekte Frage "Was schreibt die Sprachspezifikation vor?" zu stellen.
 
Zurück
Oben