C: Verständnisfrage Funktionsaufruf

Hab mal probeweiser den Wert des Puffers auf 512 erhöht.
Nun ist die Ausgabe bis HALLO123456789ABCD immer zuerst der Parameter, dann 5 7, wert = 1.
Das soll ja so sein, also alles in Ordnung.

Zur Rechnerleistung: Hat damit weniger zu tun.
Hängt davon ab, wie Compiler/Betriebssystem die Speicherverwendung einteilen.

Überschreiben:
Vereinfacht angenommen, es gibt den String mit 5 Zeichen und zwei int a und b.
char braucht 1 Byte, int 4
Im RAM könnten die dann zB. so eingeteilt sein:
Code:
01234567890123456
__xxxxx__________  string
________xxxx_____  int a
____________xxxx_  int b
Wie gesagt, die Einteilung, wo was ist, übernimmt Compiler etc.
Wenn man dann zB. 3 Zeichen in den String schreibt:
Code:
01234567890123456
__xxxxx__________  string
________xxxx_____  int a
____________xxxx_  int b
00asd000000000000 Inhalt


Man schreibt a=1001 und b=4
Code:
01234567890123456
__xxxxx__________   string
________xxxx_____  int a
____________xxxx_  int b
00asd000100100040 Inhalt
(Sowas in der Art jedenfalls)

Jetzt komt ein überlanger String HALLO12345678:
Code:
01234567890123456
__xxxxx__________   string
________xxxx_____  int a
____________xxxx_  int b
00HALLO1234567840 Inhalt

a ist jetzt 2345 und b 6784
Alles durcheinander

Im Speicher liegts zwar Binär, also 1001 im Code ist nicht direkt 1001 im Speicher,
aber das ist hier im Prinzip egal.

Und die Speicherbytes beinhalten ja nicht nur Variablen, sondern stellenweise
auch Prozessoranweisungen, die der Compiler aus deinem C-Code gemacht hat.
10011110 (158) könnte bedeuten, die folgende Adresse als Funktion zu starten
(keine Lust, die echte Nummern nachzuschauen).
Wenn das überschreiben wird, kommen irgendwann lustige Programmfehler.
Oder es gibt keine Fehler, macht aber irgendwas komplett unerwartetes...auch nicht gut.
Wer will schon, dass statt einem Funktionaufruf die Adresse zB. durch 2 dividiert wird?
Geht problemlos, macht aber nicht unbedingt das Gewünschte.
 
Ich habe dir einmal eine modifizierte Version deines ersten Codes hochgeladen, damit dir die Erklärung von sheel leichter fällt. (Falls du nicht so in C/C++ drin bist, dann versuch jetzt nicht meine Macros zu verstehen, die verwirren dich vll nur und werfen neue Fragen auf^^)

Eine Beispielausgabe ist z.B. (unter Linux!)
Code:
./t1 hall0000000000111
var info
----------------------------
a > Addr: 5e055334 =>  b: 4      puffer[0]: 20
b > Addr: 5e055330 =>  a: -4     puffer[0]: 16
puffer[0] > Addr: 5e055320 =>  a: -20    b: -16

before strcpy
-----------------------------
dump size: 4
        05 00 00 00
dump size: 4
        07 00 00 00
dump size: 5
        00 00 00 00 00

after strcpy
-----------------------------
dump size: 4
        05 00 00 00
dump size: 4
        31 00 00 00
dump size: 5
        68 61 6c 6c 30
hall0000000000111 5 49
wert = 1

Zur Ausgabe:
  • Unter "var info" stehen ein paar Informationen zu den Variablen a, b, und puffer. 5e055*** sind die Adressen im Speicher, wo deine Variable liegt. Dahinter stehen dann der Abstand in Byte zu den angegebenen Variablen. Variable a grenzt also direkt an Variable b, weil 5e05530+4 (sizeof int) = 5e055334. puffer hingegen beginnt 20 Byte vor a im Speicher (es sind also 20-5 (Größe des Arrays) = 15 Bytes platz, bevor a überschrieben wird. Bei b sind es nur 11 Byte)
  • Unter "before" sind die Werte der jeweiligen Variablen ausgegeben, wie sie im Speicher aussehen. Man sieht hier schön, dass a mit 5 und b mit 7 initialisiert sind. puffer ist leer (aufgrund von memset)
  • Unter "after" stehen die Werte, nachdem der puffer überschrieben wurde.

Wenn du mal den ersten Parameter (hall000****) nachzählst, dann würde "hall" problemlos in den puffer reinpassen, alles weitere ist ein Buffer Overflow und überschreibt Speicher, der nicht zu puffer gehört. Auf das "hall" folgen 10 0er und 3 1er.
Wir erinnern uns: zwischen puffer und b sind 11 Byte platz. Es passiert also nichts, wenn du nur 1 1er nehmen würdest. Der 12. 1er überschreibt dir das erste Byte von b mit einer "0" (String-Termiantor char '\0'). Der 3. 1er packt in das 1. Byte eine "31" (31 == ASCII-Wert von char '1', dezimal also 49). Der String-Terminator steckt nun im 2. Byte (was nicht auffällt, weil 0 mit 0 überschrieben nunmal 0 ist ^^).
Das Spiel kannst du nun beliebig weiter spielen. Irgendwann überschreibst du dein a. Und wenn es dumm läuft, dann überschreibst du irgendwann Programmcode bzw verlässt den dem Programm zugewiesenen Speicherbereich.

Wie sheel schon gesagt hat: Es hängt davon ab, wie der Compiler die Variablen in den Speicher packt.
 

Anhänge

Zuletzt bearbeitet:
Zurück