C , Zeigerzugriff
bleicher
- programmiertechnik
Grüße,
ich stehe leider etwas auf dem schlauch und bin auf fremde hilfe angewiesen ;/
wen ich ein array erkläre
char bla[100];
und den mit werten fülle, kann man auf einzelne werte mit
*(bla+i)
(i vom typ int)
zugreifen
wie schreibe ich aber rein?
bla[i]=wert;
würde helfen, aber diese verwendung ist ausdrücklich VERBOTEN
und
bla+i=wert;
ist ungültig.
wie geht das richtig? gegoogelt habe ich - aber die sache mit den zeigern liefert viele treffer (hauptsächlich zu call by reference) - bloß nicht das gewünschte ;/
MFG
bleicher
hi,
ich stehe leider etwas auf dem schlauch und bin auf fremde hilfe angewiesen ;/
wen ich ein array erkläre
char bla[100];
und den mit werten fülle, kann man auf einzelne werte mit
*(bla+i)
(i vom typ int)zugreifen
wie schreibe ich aber rein?
die Tage hatten wir das schonmal (Danke Martin), die Funktionen
strcpy()
strncpy()
memcpy()
sind dazu da.
Hotte
Hallo,
und den mit werten fülle, kann man auf einzelne werte mit
*(bla+i)
(i vom typ int)zugreifen
wie schreibe ich aber rein?
Genauso:
*(bla + i) = 5;
Viele Grüße,
Christian
bla[i]=wert;
würde helfen, aber diese verwendung ist ausdrücklich VERBOTEN
Das sollte IMHO aber gehen, auch wenn es vielleicht
eine eher ungewöhnliche Methode ist, um eine
Zeichenkette zu befüllen.
Folgender Code funktioniert hier (soll nur
ein Beispiel sein, nicht unbedingt zur Nachahmung
empfohlen...):
ap@zaphodb:~$ cat bla.c
#include<stdio.h>
int main(void) {
char bla[100];
int i=0, code[] = {65, 72, 65, 33, 10, 0};
do { bla[i]=code[i]; } while (code[i++]);
puts(bla);
return 0;
}
ap@zaphodb:~$ gcc -Wall -pedantic bla.c
ap@zaphodb:~$ ./a.out
AHA!
ap@zaphodb:~$
Dagegen funktioniert folgendes nicht:
ap@zaphodb:~$ cat bla.c
#include<stdio.h>
int main(void) {
char *bla="Diese Zeichenkette ist schreibgeschuetzt!\n";
int i=0, code[] = {65, 72, 65, 33, 10, 0};
do { bla[i]=code[i]; } while (code[i++]);
puts(bla);
return 0;
}
ap@zaphodb:~$ gcc -Wall -pedantic bla.c
ap@zaphodb:~$ ./a.out
Speicherzugriffsfehler
ap@zaphodb:~$
Mit
char bla[] = "Lange, sinnlose Zeichenkette";
geht es dann doch wieder, weil somit auf dem Stack ein
Array dynamisch und beschreibbar angelegt und die Zeichenkette
dorthin kopiert wird.
MfG
Andreas
#include<stdio.h>
int main(void) {
char bla[100];
int i=0, code[] = {65, 72, 65, 33, 10, 0};
do { bla[i]=code[i]; } while (code[i++]);
Als Nachtrag sollte man noch erwähnen, dass man hier
genauso gut auch folgendes schreiben kann:
do { i[bla]=i[code]; } while (i++[code]);
Es scheint so, als ob der Compiler einen
Ausdruck bla[i] intern in *(bla+i) umwandelt, und
damit ist die Reihenfolge dann egal... ;)
puts(bla);
return 0;
}
MfG
Andreas
Dagegen funktioniert folgendes nicht:
char *bla="Diese Zeichenkette ist schreibgeschuetzt!\n";
Natürlich nicht. Denn mit der Deklaration char* wird nur ein Pointer auf ein char angelegt und kein Array wie mit
char bla[100];
(wie Du ja schon selbst bemerkst). Die Zeichenkette oben ist nicht etwas "schreibgeschützt", sondern gar nicht existent, denn beim Versuch bla damit zu initialisieren tritt der Speicherfehler auf. Ein String muss in C in einem Array, sprich in einem Buffer abgelegt sein. Bei der Initialisierung direkt bei der Deklaration bestimmt C praktischerweise auch ohne Längenangabe selber die Größe des String-Arrays. Aber eine Zeichenkette kannst Du natürlich nicht in einen Pointer, also in einen Adresswert schreiben, der nur wenig Byte groß ist.
Grüsse
Stefanie
Natürlich nicht. Denn mit der Deklaration char* wird nur ein Pointer auf ein char angelegt und kein Array wie mit
»» char bla[100];
(wie Du ja schon selbst bemerkst). Die Zeichenkette oben ist nicht etwas "schreibgeschützt", sondern gar nicht existent, [...]
Nicht existent??? Wenn ich folgendes schreibe
char *bla = "Dies ist eine schreibgeschuetzte Zeichenkette.\n";
puts(bla);
wird die Zeichenkette ja immerhin ausgegeben ;-)
Schreibt man in C eine Zeichenkette in doppelten
Anführungszeichen, wird diese zunächst in der compilierten
Programmdatei irgendwohin hard-codiert, was man sich mit u. a. dem
Unix-Befehl 'strings program_file' ansehen kann.
Beim Ausführen des Programms gelangt dieser String
zusammen mit dem Maschinencode des Executables
in einen schreibgeschützten Bereich, von dem mir
gerade der Fachterminus entfallen ist. Die Variable 'bla'
im obigen Beispiel enthält dann die Anfangsadresse
dieses hardcodierten Strings, jedoch kann der String-Inhalt nicht
geändert werden, weil veränderliche Speicherbereiche
in C vorher entweder auf dem Stack (-> automatische Variablen)
oder auf dem Heap (--> malloc & Co.) reserviert werden müssen,
was für den hard-codierten String nicht zutrifft.
Mit
char bla[] = "Hallo\n";
wird dagegen auf dem Stack ein Array angelegt und anschließend
der hard-codierte String dort hinkopiert. Die Kopie kann
dann anschließend auch geändert werden.
MfG
Andreas
Hi,
Schreibt man in C eine Zeichenkette in doppelten
Anführungszeichen, wird diese zunächst in der compilierten
Programmdatei irgendwohin hard-codiert
Beim Ausführen des Programms gelangt dieser String
zusammen mit dem Maschinencode des Executables
in einen schreibgeschützten Bereich, von dem mir
gerade der Fachterminus entfallen ist.
Ich denke, Du meinst den Code-Bereich? Die Daten des Strings stehen so gesehen mit im Programmcode drin, das stimmt. Und der Code kann nie geändert werden. Es wäre jedoch denkbar, dass es Compiler-abhängig ist, wie diese Daten im Code abgelegt sind. Der Compiler hat die Anweisung, eine Folge von Bytes an einen bestimmten Speicher zu kopieren. Letztlich tut er das ja mit einzelnen Schreibbefehlen für jedes einzelne Byte oder Wort, und das könnte er auch ohne, dass er den gesamten String vorher zusammenhängend ablegt.
Die Variable 'bla'
im obigen Beispiel enthält dann die Anfangsadresse
dieses hardcodierten Strings,
...der tatsächlich im Code-Bereich liegt? Und dahinter liegen die kompletten Bytes des Strings? Finde ich interessant. Dann stimmt so gesehen Deine Aussage, der String wäre schreibgeschützt. Denn lesend müssten man ja drauf zugreifen können. Trotzdem sollte man es wohl besser unterlassen, seine Daten im Code abzulegen, um damit zu arbeiten...
Bei Deiner Aufzählung von veränderlichen Speicherbereichen hast Du übrigens den Datenbereich vergessen.
Grüsse
Stefanie
Und der Code kann nie geändert werden.
Korrektur, das stimmt so gar nicht. Man kann sich durchaus Code kaputt schreiben, das ist Betriebssystem-abängig. Und dann wäre auch Dein String nicht grundsätzlich schreibgeschützt.
Hallo Stefanie,
»» Schreibt man in C eine Zeichenkette in doppelten Anführungszeichen, wird diese zunächst in der compilierten Programmdatei irgendwohin hard-codiert
das ist soweit erstmal richtig.
»» Beim Ausführen des Programms gelangt dieser String zusammen mit dem Maschinencode des Executables in einen schreibgeschützten Bereich
Üblicherweise liegt er da von Anfang an.
Ich denke, Du meinst den Code-Bereich? Die Daten des Strings stehen so gesehen mit im Programmcode drin, das stimmt. Und der Code kann nie geändert werden.
Doch, kann er - und das ist nicht nur betriebssystemabhängig. Denk mal an Debugger, die ja auch den zu untersuchenden Code im Codesegment manipulieren, z.B. Breaks hineinschreiben oder gar CPU-Instruktionen auf Wunsch des Anwenders (Programmierers) verändern.
Und selbstmodifizierender Code ist auch immer noch ein heißes Thema bei den hirnkranken Programmierern von Viren. Virenscanner arbeiten oft so, dass sie den Code nach bestimmten, typischen Bytefolgen (Signaturen) absuchen. Um der Erkennung zu entgehen, wird der fertige Maschinencode oft nochmal verschlüsselt und erst zur Laufzeit wieder entschlüsselt.
Es wäre jedoch denkbar, dass es Compiler-abhängig ist, wie diese Daten im Code abgelegt sind.
Ja. Es ist aber durchaus gängige Praxis, dass Stringkonstanten direkt im Codesegment abgelegt werden, anstatt im Datensegment für initialisierte Variablen. Bei manchen Compilern kann man das per Compiler-Direktive auch steuern, wenn man eine bestimmte Anordnung unbedingt haben will.
Der Compiler hat die Anweisung, eine Folge von Bytes an einen bestimmten Speicher zu kopieren. Letztlich tut er das ja mit einzelnen Schreibbefehlen für jedes einzelne Byte oder Wort, und das könnte er auch ohne, dass er den gesamten String vorher zusammenhängend ablegt.
Theoretisch. Das wäre aber höchst ineffizient.
»» Die Variable 'bla' im obigen Beispiel enthält dann die Anfangsadresse dieses hardcodierten Strings,
...der tatsächlich im Code-Bereich liegt? Und dahinter liegen die kompletten Bytes des Strings?
Üblicherweise ja.
Finde ich interessant. Dann stimmt so gesehen Deine Aussage, der String wäre schreibgeschützt. Denn lesend müssten man ja drauf zugreifen können.
Richtig.
Trotzdem sollte man es wohl besser unterlassen, seine Daten im Code abzulegen, um damit zu arbeiten...
Selbstverständlich. Man muss da auch etwas genauer unterscheiden.
1. Deklaration mit char[]:
Beispiel: char[] str = "String für Beispiel 1";
Hier kommt es noch darauf an, ob diese Definition innerhalb einer Funktion erfolgt (lokal), oder ob sie im globalen Scope steht.
Ist sie lokal, dann wird das gesamte Array beim Eintritt in die Funktion (typischerweise auf dem Stack) angelegt und initialisiert. Der Initialisierungsstring liegt in der Regel als Stringkonstante im Codesegment und wird beim Eintritt in die Funktion umkopiert. Bei jedem Eintritt in die Funktion hat man wieder einen frisch initialisierten String, auch wenn er beim vorherigen Funktionsaufruf modifiziert wurde, denn man arbeitet ja immer nur mit einer neuen Kopie.
Ist sie global, dann wird das Array bereits vom Compiler im Datensegment für initialisierte Daten angelegt und initialisiert. Veränderungen am String-Inhalt sind in diesem Fall permanent.
Unabhängig davon, ob lokal oder global, wird kein Zeiger namens str angelegt! Es ist also möglich, auf str[x] zu schreiben, aber nicht auf str direkt, d.h. ein str = NULL; wird fehlschlagen.
2. Deklaration mit char*:
Beispiel: char* str = "String für Beispiel 2";
In diesem Fall wird tatsächlich eine Zeigervariable namens str angelegt. Ob der Initialiserungsstring im Daten- oder im Codesegment abgelegt wird, ist compilerabhängig bzw. kann durch Einstellungen vorgegeben werden. Liegt der String im Codesegment, kann auf die einzelnen Zeichen nicht schreibend zugegriffen werden. Ein Schreibzugriff auf den Zeiger str ist aber sehr wohl möglich! Ein nachfolgendes str = NULL; wäre also vermutlich nicht sinnvoll, aber zulässig.
3. Stringkonstanten, die nicht mit Variablen referenziert werden
Beispiel: printf("Hallo Welt!");
Reine Stringkonstanten, die mit keiner Variablen in Verbindung gebracht werden, legt der Compiler in der Regel im Codesegment ab, da sowieso keine Chance besteht, auf diese Daten schreibend zuzugreifen.
Bei Deiner Aufzählung von veränderlichen Speicherbereichen hast Du übrigens den Datenbereich vergessen.
Jein - er hat den Heap erwähnt, den man mit gutem Willen als solchen interpretieren darf. ;-)
Ciao,
Martin
»» »» Beim Ausführen des Programms gelangt dieser String zusammen mit dem Maschinencode des Executables in einen schreibgeschützten Bereich
Üblicherweise liegt er da von Anfang an.
Ich meinte das eigentlich so, dass der Compiler zunächst
mal nur eine Datei erstellt, die irgendwo auf einem Speichermedium
lagert und erst beim Aufruf in den Speicher kopiert (und
dort ggf. noch reloziert und sonstwie umorganisiert) wird.
Ist jetzt aber eher als Haarespalterei zu verstehen... ;-)
MfG
Andreas
Hallo bleicher,
bla[i]=wert;
würde helfen, aber diese verwendung ist ausdrücklich VERBOTEN
das ist eine ganz normale Methode um auf einzelne String-Elemente zuzugreifen. Ein String ist ja auch nichts anderes als ein Array. Wer verbietet Dir das? Wie lautet die genaue Fehlermeldung des Compilers? Kann es sein, dass Du versuchst einen falschen Typ rein zu schreiben, müsstest Du ihn casten?
bla+i=wert;
ist ungültig.
Ja sicher. bla+1 ist eine Adresse, und zeigt genau auf ein Byte weiter als wo bla hin zeigt. Um den Wert an dieser Adresse zu ändern, musst Du natürlich auf den Inhalt zugreifen:
*(bla + 1) = wert;
Ich sehe gerade, dass Du das oben schon selber schreibst - hast Du es denn ausprobiert?
Man muss einmal verinnerlicht haben, was Zeiger sind und was Buffer sind. Zeichne Dir das eventuell einfach mal auf. Kästchen für die Bytes im Speicher, Zeiger für die Adresswerte, die auf diese Kästchen zeigen.
Grüsse
Stefanie
Hallo,
»» bla[i]=wert;
»» würde helfen, aber diese verwendung ist ausdrücklich VERBOTEN
das ist eine ganz normale Methode um auf einzelne String-Elemente zuzugreifen. Ein String ist ja auch nichts anderes als ein Array. Wer verbietet Dir das?
vermutlich die Aufgabenstellung - man möchte nicht die Triviallösung sehen, sondern andere Varianten.
*(bla + 1) = wert;
Genau, und das ist ja eine äquivalente Schreibweise zu bla[i] = wert. Ich vermute, darauf zielt die Aufgabenstellung ab.
Man muss einmal verinnerlicht haben, was Zeiger sind und was Buffer sind. Zeichne Dir das eventuell einfach mal auf. Kästchen für die Bytes im Speicher, Zeiger für die Adresswerte, die auf diese Kästchen zeigen.
Ja. So habe ich das auch schon oft Neueinsteigern verdeutlicht, die das Konzept von Zeigern noch nicht so recht verinnerlicht hatten.
So long,
Martin