Inhaltszuweisung bei Pointervariable ohne Dereferenzierung



  • Bei weiterem Interesse zu String literals siehe C Standard: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf Seite 74



  • Hm..find ich erstmal seltsam.

    Ich dachte da wird some_strings[0] die Adresse "foo" zugewiesen und nciht die Adresse VON foo, weil "foo" doch ein Wert ist, nämlich ein string?

    ich dachte das müsste so viellicht aussehen:

    char a,b,*str[2];
    str[0] = &a;
    str[1] = &b;
    *str[0] = "schnitzel";
    *str[1] = "pommes";
    

    und würde dann ausgeben a sowie b und schnitzel und pommes bekommen.

    Ich störe mich hier hauptsächlich daran, dass , wenn ich diese pointervariable str nehme und ich einfach etwas im Stil
    str[0] = "beispiel";
    mache, da ja steht (Adresse, auf die pointer zeigen soll) = (ein string).
    Ich würde erwarten, dass der Compiler dann wegen der rechten Seite meckert, weil er eine Zahl erwarten würde und keinen string.



  • Willkommen in C!
    C und C++ sind die soweit ich weiß einzigen Sprachen, bei denen Zeichenketten keine Werte sind wie Zahlen.*
    Ein String-Literal wie "foo" ist in wirklich ein Array von 4 Zeichen 'f', 'o', 'o', '\0'. Diese vier Zeichen werden praktischerweise vom Compiler irgendwo beim Programmstart hingepackt, so dass du dich nicht um deren Speicherung kümmern musst. Du darfst diese Zeichen jedoch nicht verändern.
    Schreibst du nun ptr = "foo", erhält ptr nicht den Wert "foo", denn das ist kein Wert. Es erhält die Addresse des ersten Zeichens vom Array "foo". Und dank Pointerarithmetik kannst du dann auf die anderen zugreifen. Würdest du *ptr = "foo" schreiben, würdest du das, worauf ptr zeigt - einem char - versuchen die Addresse des ersten Elementes von "foo" zuweisen, was nicht geht.

    *bzw. wo das direkt bei Pointerartihmetik auffällt.



  • Du solltest dich mit der Frage beschäftigen, was genau ein string im C Umfeld ist (deswegen auch der Link auf den C Standard). Du scheinst ein Konzept von einer anderen Sprache zu kennen und das auf C zu übertragen. Das geht hier allerdings nicht gut.

    Edit: zu langsam



  • Sprich das wird hier eine etwas spezielle Situation, weil wir mit strings arbeiten?

    Stimmt dann also die allgemeine Vorstellung, dass i.d.R.

    &Variable -> Adresse der Variable

    Bei Pointernvariablen:
    *Variable -> Inhalt der Adresse, auf die Variable zeigt
    Variable -> Adresse wo Variable hinzeigt

    bei "normalen" Variablen:
    Variable -> Inhalt an der Adresse &Variable



  • Anknüpfend an Nathan.
    Außer den String Literalen und der Array initialisierung mit "" bietet die Sprache C keine weitere Unterstützung für Strings, es gibt nicht einmal einen String Datentypen.
    Es gibt nur eine Konvention, wie Strings im Speicher abgelegt werden: Ein String besteht aus hintereinanderliegenden chars, '\0' markiert das Ende eines Strings.

    D.h. der Programmierer muss diegesamte Speicherverwaltung für Strings selber machen, und muss selbst dafür sorgen, dass ein char* der zB an puts übergeben wird auf chars zeigt die der String Konvention folgen.



  • Ach du meine Güte...

    heißt also, dass in meinem Eingangsbeispiel mit der Ausgabe von some_strings[0] ich automatisch some_strings[0][0] bis some_strings[0][2] sequentiell ausgegeben bekomme. Wie käme ich denn dann an den zweiten Buchstaben meines Strings (der ja kein string im Datentyp-Sinne ist) ran, wenn die Definition wie in meinem Eingangsbeispiel erfolgt ?

    Würde dann der Unterschied zu einer Deklaration wie folgt:

    char some_strings[2];

    darin bestehen, dass bei dieser Deklaration lediglich zwei zeichen gespeichert werden könnten, dieses array also sogar zu klein für "foo" wäre?



  • Wolfone schrieb:

    Wie käme ich denn dann an den zweiten Buchstaben meines Strings?

    some_strings[0][1]
    oder some_strings[1][1] beim zweiten String.

    Wolfone schrieb:

    Würde dann der Unterschied zu einer Deklaration wie folgt:

    char some_strings[2];

    darin bestehen, dass bei dieser Deklaration lediglich zwei zeichen gespeichert werden könnten, dieses array also sogar zu klein für "foo" wäre?

    Ja.



  • Verstehe...wäre es generell so, dass bei der Ausgabe eines mehrdimensionalen arrays unabhängig vom Datentyp (bleiben wir mal im zweidimensionalen) in der Art:

    ...print(modifikatoren, beispielarray[blubb]);

    dann die Ausgabe aus der Konkatenation von beispielarray[blubb][0] bis beispielarray[blubb][n] bestehen würde?


  • Mod

    Wolfone schrieb:

    Verstehe...wäre es generell so, dass bei der Ausgabe eines mehrdimensionalen arrays unabhängig vom Datentyp (bleiben wir mal im zweidimensionalen) in der Art:

    ...print(modifikatoren, beispielarray[blubb]);

    dann die Ausgabe aus der Konkatenation von beispielarray[blubb][0] bis beispielarray[blubb][n] bestehen würde?

    Nein, wieso sollte das so sein? printf kann nicht zaubern. Alles passiert streng nach Vorschrift. Die hier relevanten Regeln sind, dass ein Array als ein Zeiger auf sein erstes Element interpretiert wird (außer in ein paar genau definierten Ausnahmefällen) und die Regeln zur Konvertierung von Pointertypen. Wenn beispielarray ein 2D-Array

    datentyp beispielarray[10][20];
    

    wäre, dann wird beispielarray[blubb] in diesem Beispiel als ein Zeiger auf ein datentyp[20] aufgefasst (und eben nicht als ein Zeiger auf datentyp! Das erste Element ist nämlich vom Typ datentyp[20]). Der printf-modifikator muss also geeignet sein, um ein (*datentyp)[20] auszugeben. Solch einen Modifikator wird es in aller Regel nicht geben. Es gibt in Standard-C allgemein nur drei Modifikatoren, die überhaupt einen Zeigertypen erwarten, %p (für void*), %s (für char*) und das praktisch unbekannte %n (ist kompliziert). Wenn du einen dieser Modifikatoren benutzt, dann wird dein Zeiger auf das datentyp[20]-Feld kurzerhand als ein void* beziehungsweise char* interpretiert*. Egal, ob das passt oder nicht, denn printf nimmt keine Typprüfung vor (eine große Gefahr bei printf und einer der Gründe, warum C kein Ponyhof ist). Glücklicherweise sind alle Zeiger auf irgendwelche Daten garantiert in void* konvertierbar, man kann mit %p also alle Zeigertypen korrekt ausgeben (aber keine Funktionszeiger!). Sollte es sich beim datentyp um char handeln und in dem Array eine nullterminierte Zeichenkette stehen, dann geht das ebenfalls gut, denn es ist garantiert, dass die Adresse eines Arrays identisch ist mit der Adresse seines ersten Elements. Das heißt, wenn man den Zeiger auf das Array einfach so als einen Zeiger auf einen char auffasst, dann geht das in diesem Fall(!) gut.

    *: %n lasse ich bei dieser Betrachtung mal weg, da es nichts ausgibt und für ganz was anderes da ist.



  • SeppJ schrieb:

    datentyp beispielarray[10][20];
    

    wäre, dann wird beispielarray[blubb] in diesem Beispiel als ein Zeiger auf ein datentyp[20] aufgefasst (und eben nicht als ein Zeiger auf datentyp!

    Nein. Du meinst nicht beispielarray[blubb] sondern beispielarray.



  • SeppJ schrieb:

    Wenn du einen dieser Modifikatoren benutzt, dann wird dein Zeiger auf das datentyp[20]-Feld kurzerhand als ein void* beziehungsweise char* interpretiert*. Egal, ob das passt oder nicht, denn printf nimmt keine Typprüfung vor (eine große Gefahr bei printf und einer der Gründe, warum C kein Ponyhof ist). Glücklicherweise sind alle Zeiger auf irgendwelche Daten garantiert in void* konvertierbar, man kann mit %p also alle Zeigertypen korrekt ausgeben (aber keine Funktionszeiger!). Sollte es sich beim datentyp um char handeln und in dem Array eine nullterminierte Zeichenkette stehen, dann geht das ebenfalls gut, denn es ist garantiert, dass die Adresse eines Arrays identisch ist mit der Adresse seines ersten Elements. Das heißt, wenn man den Zeiger auf das Array einfach so als einen Zeiger auf einen char auffasst, dann geht das in diesem Fall(!) gut.

    Nein. Das kann gut gehen, muss es aber nicht, weil es in diesem Fall (forciertes) UB ist.
    printf erwartet bei "%s" einen char* bzw. kompatiblen Typ.
    Ein Typ char (*)[20], der übergeben wird bei z.B.

    char array[10][20]={{"bla"}};
    printf("%s",array);
    

    ist inkompatibel zu char*, und die Dereferenzierung nichtkompatibler Zeiger ist UB (wegen den Alignments, auch wenn der Zeigerwert gleich ist).
    Der printf-Code wird (nach Auswertung der va_list) irgendwie einen 'char*'-Typ verarbeiten wollen:

    {
      const char *s = ...;
      while( *s!='\0' ) /* und hier liegt der Haken: die Dereferenzierung setzt 'char*'-Typ oder kompatibel voraus */
      {
        ...
      }
    }
    

    Interessant wäre mal, ob UBSan von clang oder gcc sowas finden. Dürfte aber schwierig werden, wegen der va_list bei printf.



  • @SeppJ

    ich dachte, dass das so sein könnte, weil dei Ausgabe von some_strings[0]
    ja tatsächlcih foo zurückgibt, von dem vorhin gesagt wurde, dass es quasi ein array von 3 chars ist und es dort auch nicht nötig war jeden Eintrag einzeln anzusprechen.

    Insgesamt überfordern mich die letzten drei Posts mit meinem Kenntnisstand und zu dieser Uhrzeit zugegebenermaßen.

    Danke, dass ihr euhc so viel Zeit nehmt!


Log in to reply