bleicher: C , Zeigerzugriff

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

  1. 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

    --
    Wenn der Kommentar nicht zum Code passt, kann auch der Code falsch sein.
  2. 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

    --
    Mein "Weblog" [RSS]
    Using XSLT to create JSON output (Saxon-B 9.0 for Java)
    »I don't believe you can call yourself a web developer until you've built an app that uses hyperlinks for deletion and have all your data deleted by a search bot.«
                -- Kommentar bei TDWTF
  3. 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

    1. #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

    2. 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

      1. 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

        1. 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

          1. 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.

          2. 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

            --
            Ist die Katze gesund,
            freut sich der Hund.
            1. »» »» 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

  4. 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

    1. 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

      --
      Alleine sind wir stark ...
      gemeinsam sind wir unausstehlich!