Mechthild: C - Zeigerfrage Array

Mache gerade meine ersten Schritte in C, weil ich das immer schon mal lernen wollte. Ich erhoffe mir dadurch ein tieferes Verständnis von anderen Sprachen, vor allem PHP.

Jetzt scheitere ich schon an einfachen Dingen :(

also:

ich habe ein Feld:
int field[4][4]={ 0 };

dieses möchte ich als Zeiger an eine Funktion übergeben (sagt man das so?):
printField(&field[0][0]);

die Funktion sieht so aus:

  
void printField(int *field){  
	int i,y;  
	for(i=0; i<4; i++){  
		for(y=0; y<4; y++){  
			printf("%d ", *(field+i*4+y));  
		}  
		printf("\n");  
	}  
}  

das funktioniert, aber nur deshalb, weil ich in der Lösung nachgeschlagen habe. Folgendes ist mir nicht klar:
printf("%d ", *(field+i*4+y));

was genau soll das? Ich hätte rein gefühlsmäßig folgendes notiert
printf("%d ", *field[x][y]); //PHP like sozusagen

  1. Hallo,

    Mache gerade meine ersten Schritte in C, weil ich das immer schon mal lernen wollte. Ich erhoffe mir dadurch ein tieferes Verständnis von anderen Sprachen, vor allem PHP.
    Jetzt scheitere ich schon an einfachen Dingen :(

    sehe ich nicht so - das Verständnis von Arrays und Zeigern, und in welchen Situationen sie äquivalent sind, ist alles andere als einfach.

    ich habe ein Feld:
    int field[4][4]={ 0 };

    dieses möchte ich als Zeiger an eine Funktion übergeben (sagt man das so?):
    printField(&field[0][0]);

    Syntaktisch passt das zusammen; logisch nicht so ganz. Logisch betrachtet deklarierst du ein Array mit 4 Elementen, die ihrerseits wieder Arrays mit 4 int-Feldern sind. Im Arbeitsspeicher werden diese 4x4 int-Felder natürlich lückenlos hintereinander abgelegt. Dadurch erzeugen die folgenden Deklarationen im Arbeitsspeicher alle dasselbe:

    int a[4][4];
    int b[2][8];
    int c[16];

    Diese Organisation der Daten im Arbeitsspeicher sollte man einmal verstanden haben, dann wird der Rest auch wieder einfacher.

    void printField(int *field){

    int i,y;
    for(i=0; i<4; i++){
    for(y=0; y<4; y++){
    printf("%d ", (field+i4+y));
    }
    printf("\n");
    }
    }

      
    Du deklarierst also eine Funktion, die einen Zeiger auf ein int erwartet. Ob das ein Zeiger auf eine einzelne int-Variable ist, oder ein Zeiger auf ein beliebiges(!) Element eines int-Arrays, ist syntaktisch wieder egal.  
    Ferner ist die Funktion so angelegt, dass sie selbst "weiß", dass es sich beim übergebenen int-Zeiger um einen Zeiger auf das erste von 16 int-Elementen handelt, die logisch als 4 Gruppen zu je 4 Elementen gesehen werden sollen.  
      
    
    > das funktioniert, aber nur deshalb, weil ich in der Lösung nachgeschlagen habe. Folgendes ist mir nicht klar:  
    > printf("%d ", \*(field+i\*4+y));  
      
    Das ist eine alternative (ich würde sagen weniger verständliche) Notation für  
      
    `printf("%d ", field[i*4+y]);`{:.language-c}  
      
    
    > Ich hätte rein gefühlsmäßig folgendes notiert  
    > printf("%d ", \*field[x][y]); //PHP like sozusagen  
      
    Ja. Nur dass der Compiler an dieser Stelle nicht mehr weiß, dass das per Zeiger übergebene Array als 4x4-Anordnung gemeint ist, weil du der Funktion gemäß Deklaration "irgendeinen" Zeiger auf ein int übergibst. Wenn das Array also entsprechend strukturiert ist, musst du die Reduzierung von zwei oder drei Indexen auf einen selbst vornehmen, so wie der vorliegende Code das tut.  
      
    So long,  
     Martin  
    
    -- 
    Zivilisation bedeutet, dass die Eskimos warme Wohnungen bekommen und dann arbeiten müssen, damit sie sich einen Kühlschrank leisten können.  
    Selfcode: fo:) ch:{ rl:| br:< n4:( ie:| mo:| va:) de:] zu:) fl:{ ss:) ls:µ js:(
    
    1. Hi Martin!

      Jetzt scheitere ich schon an einfachen Dingen :(

      sehe ich nicht so - das Verständnis von Arrays und Zeigern, und in welchen Situationen sie äquivalent sind, ist alles andere als einfach.

      ja, das merke ich so langsam. In PHP und Java war das Handling immer irgendwie simpel. Obwohl natürlich mehr dahintersteckt..

      ich habe ein Feld:
      int field[4][4]={ 0 };

      dieses möchte ich als Zeiger an eine Funktion übergeben (sagt man das so?):
      printField(&field[0][0]);

      falls ich das Feld und nicht den Zeiger übergeben möchte, würde ich prinfField(field[0][0]) schreiben?

      Syntaktisch passt das zusammen; logisch nicht so ganz. Logisch betrachtet deklarierst du ein Array mit 4 Elementen, die ihrerseits wieder Arrays mit 4 int-Feldern sind. Im Arbeitsspeicher werden diese 4x4 int-Felder natürlich lückenlos hintereinander abgelegt. Dadurch erzeugen die folgenden Deklarationen im Arbeitsspeicher alle dasselbe:

      int a[4][4];
      int b[2][8];
      int c[16];

      Warum passt das logisch nicht zusammen? Ich wollte ein zweidimensionales Feld deklarieren, dessen Elemente alle mit 0 initialisiert sind.

      Diese Organisation der Daten im Arbeitsspeicher sollte man einmal verstanden haben, dann wird der Rest auch wieder einfacher.

      DAS war der Hinweis. Jetzt verstehe ich auch folgendes. Vielen Dank!

      void printField(int *field){

      int i,y;  
      for(i=0; i<4; i++){  
        for(y=0; y<4; y++){  
        	printf("%d ", *(field+i*4+y));  
        }  
        printf("\n");  
      }  
      

      }

      
      >   
      > Du deklarierst also eine Funktion, die einen Zeiger auf ein int erwartet. Ob das ein Zeiger auf eine einzelne int-Variable ist, oder ein Zeiger auf ein beliebiges(!) Element eines int-Arrays, ist syntaktisch wieder egal.  
      > Ferner ist die Funktion so angelegt, dass sie selbst "weiß", dass es sich beim übergebenen int-Zeiger um einen Zeiger auf das erste von 16 int-Elementen handelt, die logisch als 4 Gruppen zu je 4 Elementen gesehen werden sollen.  
        
      ist das schlecht?  
        
      
      >   
      > > das funktioniert, aber nur deshalb, weil ich in der Lösung nachgeschlagen habe. Folgendes ist mir nicht klar:  
      > > printf("%d ", \*(field+i\*4+y));  
      >   
      > Das ist eine alternative (ich würde sagen weniger verständliche) Notation für  
      >   
      > `printf("%d ", field[i*4+y]);`{:.language-c}  
        
      verstehe... so langsam. Ich verstehe nur noch nicht 100%, warum ich hier field nutze und nicht \*field.  
        
        
      
      >   
      > > Ich hätte rein gefühlsmäßig folgendes notiert  
      > > printf("%d ", \*field[x][y]); //PHP like sozusagen  
      >   
      > Ja. Nur dass der Compiler an dieser Stelle nicht mehr weiß, dass das per Zeiger übergebene Array als 4x4-Anordnung gemeint ist, weil du der Funktion gemäß Deklaration "irgendeinen" Zeiger auf ein int übergibst. Wenn das Array also entsprechend strukturiert ist, musst du die Reduzierung von zwei oder drei Indexen auf einen selbst vornehmen, so wie der vorliegende Code das tut.  
        
      würde ich keinen Zeiger übergeben, sondern das Array:  
      void printField(int field){  
      [..]  
        
      könnte ich  
        
      for(i=0; i<4; i++){  
      for(y=0; y<4; y++){  
      printf("%d ", field[i][y]);  
        
      schreiben, richtig?  
        
      Vielen Dank nochmal, manchmal ist ein Hinweis eben doch mehr Wert
      
      1. n'Abend!

        In PHP und Java war das Handling immer irgendwie simpel. Obwohl natürlich mehr dahintersteckt..

        Java kann *ich* nicht beurteilen, weil ich es nicht wirklich kenne - und PHP kommt mir immer geheimnisvoll und schwer durchschaubar vor, weil ich zuerst Assembler, dann C gelernt habe und gewöhnt bin, maschinennah zu denken und jeden einzelnen Schritt sehen möchte. Sprachen wie PHP oder auch schon C++ machen für meinen Geschmack schon zuviel "von allein", ohne dass es für den Betrachter offensichtlich ist. Das ist gut für jemanden, der schnell das Wesentliche erkennen und erreichen will, aber schlecht für jemanden, der jeden einzelnen Schritt verstehen möchte.

        falls ich das Feld und nicht den Zeiger übergeben möchte, würde ich prinfField(field[0][0]) schreiben?

        Verflucht, da habe ich mich mit der Nomenklatur vertan. Der Begriff "Feld" wird in der deutschsprachigen Fachliteratur manchmal als Übersetzung für "Array" verwendet - ich meinte mit "Feld" aber das einzelne Element eines Arrays. Also nochmal:

        falls ich das Feld und nicht den Zeiger übergeben möchte, würde ich prinfField(field[0][0]) schreiben?

        Damit übergibst du das erste Arrayelement. Die Information über die Dimensionen des Arrays geht dabei ebenso verloren wie die Information, dass es überhaupt Teil eines Arrays ist.

        Du deklarierst also eine Funktion, die einen Zeiger auf ein int erwartet. Ob das ein Zeiger auf eine einzelne int-Variable ist, oder ein Zeiger auf ein beliebiges(!) Element eines int-Arrays, ist syntaktisch wieder egal.
        Ferner ist die Funktion so angelegt, dass sie selbst "weiß", dass es sich beim übergebenen int-Zeiger um einen Zeiger auf das erste von 16 int-Elementen handelt, die logisch als 4 Gruppen zu je 4 Elementen gesehen werden sollen.
        ist das schlecht?

        Nein, es ist eine gängige Vorgehensweise, weil bei der Übergabe von Arrays als Funktionsparameter in C nur ein Zeiger auf das Array übergeben wird und jegliche Information über die Struktur verlorengeht. Wollte man eine Funktion schreiben, die mit Arrays beliebiger Größe arbeiten kann, müsste man ihr die Dimensionen des Arrays als zusätzliche Parameter übergeben, während diese Information in anderen Sprachen als Eigenschaft am Array selbst "dranhängt".

        printf("%d ", *(field+i*4+y));
        printf("%d ", field[i*4+y]);
        verstehe... so langsam. Ich verstehe nur noch nicht 100%, warum ich hier field nutze und nicht *field.

        (1) Der Ausdruck  f[a+4*b]  bedeutet:
        Berechne a+4*b und nimm den als Index. Lies dann das Element aus dem Array f, das durch diesen Index adressiert wird.

        (2) Der Ausdruck  *(f+a+4*b)  bedeutet:
        Berechne a+4*b und gehe dann, ausgehend vom Zeiger f, um genau soviele Elemente im Speicher weiter. Lies das Element, zu dem du auf diese Weise kommst.

        Der Knackpunkt ist, dass der bloße Bezeichner eines Arrays gleichbedeutend ist mit einem Zeiger auf das erste Element des Arrays. Daher ist &f[0] (Adresse des Arrayelements 0) dasselbe wie f (Basisadresse des Arrays f), und f[1] (Wert des Arrayelements 1) dasselbe wie *(f+1) (Inhalt des Speichers, auf den f verweist, um ein Element erhöht).

        würde ich keinen Zeiger übergeben, sondern das Array:
        void printField(int field)

        So übergibst du kein Array, sondern einen einzelnen int-Wert.

        So long,
         Martin

        --
        Vielseitigkeit: Von vielen Dingen keine Ahnung haben.
        Selfcode: fo:) ch:{ rl:| br:< n4:( ie:| mo:| va:) de:] zu:) fl:{ ss:) ls:µ js:(
        1. Hallo!

          Java kann *ich* nicht beurteilen, weil ich es nicht wirklich kenne - und PHP kommt mir immer geheimnisvoll und schwer durchschaubar vor, weil ich zuerst Assembler, dann C gelernt habe und gewöhnt bin, maschinennah zu denken und jeden einzelnen Schritt sehen möchte. Sprachen wie PHP oder auch schon C++ machen für meinen Geschmack schon zuviel "von allein", ohne dass es für den Betrachter offensichtlich ist. Das ist gut für jemanden, der schnell das Wesentliche erkennen und erreichen will, aber schlecht für jemanden, der jeden einzelnen Schritt verstehen möchte.

          da sind wir uns sehr ähnlich. Ich möchte auch immer jeden Schritt wissen, sonst kapier ich es auch gar nicht so richtig. Deshalb macht mir C gerade so richtig Spaß.

          falls ich das Feld und nicht den Zeiger übergeben möchte, würde ich prinfField(field[0][0]) schreiben?

          Damit übergibst du das erste Arrayelement. Die Information über die Dimensionen des Arrays geht dabei ebenso verloren wie die Information, dass es überhaupt Teil eines Arrays ist.

          Jetzt hab ich es geschnallt. Man muss nur etwas um- und dabei an den Speicherbereich denken.

          Ferner ist die Funktion so angelegt, dass sie selbst "weiß", dass es sich beim übergebenen int-Zeiger um einen Zeiger auf das erste von 16 int-Elementen handelt, die logisch als 4 Gruppen zu je 4 Elementen gesehen werden sollen.
          ist das schlecht?

          Nein, es ist eine gängige Vorgehensweise, weil bei der Übergabe von Arrays als Funktionsparameter in C nur ein Zeiger auf das Array übergeben wird und jegliche Information über die Struktur verlorengeht. Wollte man eine Funktion schreiben, die mit Arrays beliebiger Größe arbeiten kann, müsste man ihr die Dimensionen des Arrays als zusätzliche Parameter übergeben, während diese Information in anderen Sprachen als Eigenschaft am Array selbst "dranhängt".

          Das heisst also, ob ich einen Array übergebe oder nur einen Pointer auf das erste Element, ist egal?

          (1) Der Ausdruck  f[a+4*b]  bedeutet:
          Berechne a+4*b und nimm den als Index. Lies dann das Element aus dem Array f, das durch diesen Index adressiert wird.

          ok, ist klar.

          (2) Der Ausdruck  *(f+a+4*b)  bedeutet:
          Berechne a+4*b und gehe dann, ausgehend vom Zeiger f, um genau soviele Elemente im Speicher weiter. Lies das Element, zu dem du auf diese Weise kommst.

          ist jetzt auch klar. Das Sternchen * rückentwickelt also den Ausdruck und gibt mir den Wert. Lasse ich das Sternchen weg, bekomme ich die Adresse.

          Der Knackpunkt ist, dass der bloße Bezeichner eines Arrays gleichbedeutend ist mit einem Zeiger auf das erste Element des Arrays. Daher ist &f[0] (Adresse des Arrayelements 0) dasselbe wie f (Basisadresse des Arrays f), und f[1] (Wert des Arrayelements 1) dasselbe wie *(f+1) (Inhalt des Speichers, auf den f verweist, um ein Element erhöht).

          verstehe. Bei *(f+n) wandere ich also n Schritte vor im Speicher. Kann man so auch in Speicherbereiche gelangen, die eigentlich nicht zu f gehören? Wäre das ein Buffer-overflow?

          1. Moin,

            da sind wir uns sehr ähnlich. Ich möchte auch immer jeden Schritt wissen, sonst kapier ich es auch gar nicht so richtig. Deshalb macht mir C gerade so richtig Spaß.

            genau. ;-)

            Das heisst also, ob ich einen Array übergebe oder nur einen Pointer auf das erste Element, ist egal?

            Ja. Oder anders gesagt: Wenn du ein Array übergibst, *ist* das in Wirklichkeit *immer* nur ein Zeiger auf das erste Element.

            Bei *(f+n) wandere ich also n Schritte vor im Speicher.

            Richtig.

            Kann man so auch in Speicherbereiche gelangen, die eigentlich nicht zu f gehören? Wäre das ein Buffer-overflow?

            Ganz genau. Entweder man gelangt so in Speicherbereiche, die einem selbst (also dem ausführenden Programm) gar nicht mehr "gehören" - dann bekommt man Haue von der Speicherverwaltung des OS ("Segmentation Fault", "Dieses Programm wurde wegen eines unzulässigen Vorgangs beendet"). Oder man erreicht Speicherbereiche, die noch zum Programm gehören, und schreibt sich damit selbst andere Daten kaputt.
            Dem richtig "guten" Hacker gelingt es dann und wann, durch Falscheingaben, die vom Programm nicht erkannt werden, einen Buffer Overflow so gezielt hervorzurufen, dass er auf diesem Umweg in bestimmten Variablen genau die Werte hat, die er dort haben möchte, aber auf ehrlichem Weg nicht erreicht.

            Und da C keine integrierte Überprüfung von Zeigern und Array-Indexen hat, ist diese Sprache besonders anfällig für Buffer Overflows. Es ist daher Aufgabe des Programmierers, auf solche Dinge sehr genau zu achten.

            Ciao,
             Martin

            --
            Wer im Glashaus sitzt, sollte Spaß am Fensterputzen haben.
            Selfcode: fo:) ch:{ rl:| br:< n4:( ie:| mo:| va:) de:] zu:) fl:{ ss:) ls:µ js:(
        2. Ferner ist die Funktion so angelegt, dass sie selbst "weiß", dass es sich beim übergebenen int-Zeiger um einen Zeiger auf das erste von 16 int-Elementen handelt, die logisch als 4 Gruppen zu je 4 Elementen gesehen werden sollen.
          ist das schlecht?

          Nein, es ist eine gängige Vorgehensweise, weil bei der Übergabe von Arrays als Funktionsparameter in C nur ein Zeiger auf das Array übergeben wird und jegliche Information über die Struktur verlorengeht.

          Also in C++ würde man je nachdem welche Größen fest bzw. dynamisch sind folgendes schreiben

          * wenn alles statisch fest auf 4 sein muß
          void printField(int field[4][4])
          {
            ...
          }

          * wenn daß äußere Feld dynamisch ist
          void printField(int (*field)[4], unsigned long size)
          {
            ...
          }

          * wenn alles dynamisch ist
          void printField(int** field, unsigned long size1, unsigned long size2)
          {
            ...
          }

          das sollte eigentlich auch in C gehen?

  2. Mache gerade meine ersten Schritte in C, weil ich das immer schon mal lernen wollte. Ich erhoffe mir dadurch ein tieferes Verständnis von anderen Sprachen, vor allem PHP.

    Hach, wehowei. Dennis Ritchie und seine Mitstreiter hatten Ahnung von dem, was sie taten. PHP hingegen ist ein ohne Sinn und Konzept zusammengeschusterter Haufen Scheiße. Das sieht irgendwie ein wenig aus wie C, aber das war's auch schon. Es ist schon beinahe tragisch, Jahrzehnte der Forschung an Programmiersprachen – und dann kommt so eine Missgeburt daher, die jeder Beschreibung spottet.
    Also bitte: Tieferes Verständnis für PHP wirst du aus C nicht erlernen.

    Aber davon mal abgesehen: Die beiden Sprachen unterscheiden sich zudem in den technischen Grundlagen. Das eine wird in Maschinencode übersetzt, das andere ist eine Interpretersprache.

    int field[4][4]={ 0 };

    dieses möchte ich als Zeiger an eine Funktion übergeben (sagt man das so?):
    printField(&field[0][0]);

    Du möchtest einen Zeiger auf das Feld übergeben.

    for(i=0; i<4; i++){
    for(y=0; y<4; y++){
    printf("%d ", *(field+i*4+y));
    }

    Folgendes ist mir nicht klar:
    printf("%d ", *(field+i*4+y));

    was genau soll das?

    C arbeitet vergleichsweise maschinennah.

    Deine Variable liegt irgendwo im Speicher des Computers, der Computerspeicher ist Byte für Byte über eine Adresse ansprechbar. Ein Zeiger auf eine Variable ist nichts anderes als die Adresse im Speicher, an der die Variable abgelegt ist (Speichermanagment lassen wir jetzt mal beiseite).

    Mit einer Zeichenkette ist das einfach zu verstehen:

    Buchstabe: abcdefg
    Byte:      0123456

    Die Zeichenkette belegt sieben aufeinanderfolgende Bytes im Speicher. Das c erreichst du, indem du die Startadresse der Zeichenkette nimmst und 2 hinzuaddierst.

    Dein Feld besteht aus vier Zeilen à vier Spalten bzw. Zellen. Die einzelnen Zellen liegen allesamt nacheinander im Speicher. Du kannst daher auf jede einzelne Zelle zugreifen, indem du die Adresse der Variable nimmst (&field) und dann die gewünschte Anzahl hinzuaddierst. Die erste Zelle liegt an der Startadresse, die zweite an Startadresse + 1, die dritte an Startadresse + 2 etc. Die Zellen der zweiten Zeile erreichst du, indem du 2 * Spaltenzahl hinzuaddierst, die Zellen der dritten Zeile über 3 * Spaltenzahl.

    Genau dies passiert in den beiden Schleifen:

    for(i=0; i<4; i++){ // Zeilenzähler
    for(y=0; y<4; y++){ // Spaltenzähler
    printf("%d ", *(field+i*4+y)); // Zelle = Startadresse von field + Zeilenzahl i * Zeilenlänge 4 + Spalte y

    Mit dem Sternchen wird der Zeiger sozusagen rückabgewickelt, denn du möchtest den Inhalt ausgeben, der an der betreffenden Adresse steht, nicht die Adresse selbst. Aber probier's mal aus: Nimm das Sternchen und die dazugehörige Klammer weg, dann solltest du in der Ausgabe nacheinander die Speicheradressen sehen.

    1. Hi Warmbier!

      Mache gerade meine ersten Schritte in C, weil ich das immer schon mal lernen wollte. Ich erhoffe mir dadurch ein tieferes Verständnis von anderen Sprachen, vor allem PHP.

      Hach, wehowei. Dennis Ritchie und seine Mitstreiter hatten Ahnung von dem, was sie taten. PHP hingegen ist ein ohne Sinn und Konzept zusammengeschusterter Haufen Scheiße. Das sieht irgendwie ein wenig aus wie C, aber das war's auch schon. Es ist schon beinahe tragisch, Jahrzehnte der Forschung an Programmiersprachen – und dann kommt so eine Missgeburt daher, die jeder Beschreibung spottet.
      Also bitte: Tieferes Verständnis für PHP wirst du aus C nicht erlernen.

      Mir gehts vor allem darum, dass ich das lernen will. Ausserdem beschäftige ich mich noch mit Python und da würde ich mir irgendwann einmal gerne eine eigene C-Erweiterung schreiben. Ich weiß, ein weiter Weg liegt vor mir. Aber mir machts Spaß!

      Aber davon mal abgesehen: Die beiden Sprachen unterscheiden sich zudem in den technischen Grundlagen. Das eine wird in Maschinencode übersetzt, das andere ist eine Interpretersprache.

      Das ist mir bewußt.

      int field[4][4]={ 0 };

      dieses möchte ich als Zeiger an eine Funktion übergeben (sagt man das so?):
      printField(&field[0][0]);

      Du möchtest einen Zeiger auf das Feld übergeben.

      ich habe jetzt Dank Martin auch den Sinn verstanden. Das war mir vorher überhaupt nicht klar, was das soll.

      C arbeitet vergleichsweise maschinennah.

      Deine Variable liegt irgendwo im Speicher des Computers, der Computerspeicher ist Byte für Byte über eine Adresse ansprechbar. Ein Zeiger auf eine Variable ist nichts anderes als die Adresse im Speicher, an der die Variable abgelegt ist (Speichermanagment lassen wir jetzt mal beiseite).

      Mit einer Zeichenkette ist das einfach zu verstehen:

      Buchstabe: abcdefg
      Byte:      0123456

      Die Zeichenkette belegt sieben aufeinanderfolgende Bytes im Speicher. Das c erreichst du, indem du die Startadresse der Zeichenkette nimmst und 2 hinzuaddierst.

      die Startadresse der Zeichenkette p ist dann &p, oder?

      Dein Feld besteht aus vier Zeilen à vier Spalten bzw. Zellen. Die einzelnen Zellen liegen allesamt nacheinander im Speicher. Du kannst daher auf jede einzelne Zelle zugreifen, indem du die Adresse der Variable nimmst (&field) und dann die gewünschte Anzahl hinzuaddierst. Die erste Zelle liegt an der Startadresse, die zweite an Startadresse + 1, die dritte an Startadresse + 2 etc. Die Zellen der zweiten Zeile erreichst du, indem du 2 * Spaltenzahl hinzuaddierst, die Zellen der dritten Zeile über 3 * Spaltenzahl.

      Genau dies passiert in den beiden Schleifen:

      for(i=0; i<4; i++){ // Zeilenzähler
      for(y=0; y<4; y++){ // Spaltenzähler
      printf("%d ", *(field+i*4+y)); // Zelle = Startadresse von field + Zeilenzahl i * Zeilenlänge 4 + Spalte y

      Mit dem Sternchen wird der Zeiger sozusagen rückabgewickelt, denn du möchtest den Inhalt ausgeben, der an der betreffenden Adresse steht, nicht die Adresse selbst. Aber probier's mal aus: Nimm das Sternchen und die dazugehörige Klammer weg, dann solltest du in der Ausgabe nacheinander die Speicheradressen sehen.

      ah, das dämmert mir. Probier ich gleich mal aus. Danke!
      Und stell dein Bier in Kühlschrank!

      1. Moin Mechthild,

        die Startadresse der Zeichenkette p ist dann &p, oder?

        Nein, die ist p. Deshalb halte ich auch überhaupt nichts von dem Beispiel-Quelltext, den du gepostet hast, der verwirrt die Leute nur. Bei char *p = "abcd"; ist die Startadresse des Speicherblocks, in dem "abcd" liegen, in p gespeichert. &p gäbe dir die Adresse des Pointers selber zurück, du hättest damit also ein char **.

        LG,
         CK

    2. Meine Herren!

      Hach, wehowei. Dennis Ritchie und seine Mitstreiter hatten Ahnung von dem, was sie taten. PHP hingegen ist ein ohne Sinn und Konzept zusammengeschusterter Haufen Scheiße.

      Ich bin auch kein PHP-Fan, aber diese Aussage halte ich für stark überzogen. PHP würde ursprünglich als Programmiersprache für didaktische Zwecke entwickelt. Deshalb kennt PHP vermutlich auch so komplizierte Gebilde wie Zeiger und statische Typisierung nicht. Heute wird PHP als Programmiersprache für den breiten Produktiv-Einsatz vor allem im Web-Bereich weiterentwickelt und hat sich da gut positionieren können. Diesen Aufgabenbereich erfüllt PHP m.M.n. auch besser als C das könnte. Ich jedenfalls würde ungern die Geschäftslogik einer Webseite mit C entwickeln wollen, ebenso würde ich sehr ungern einen Microkontroller mit PHP programmieren wollen.

      Das sieht irgendwie ein wenig aus wie C, aber das war's auch schon.

      Das stimmt, aber das würde ich nicht als reinen Nachteil von PHP betrachten. Die C-Syntax-Familie schlägt weite Wurzeln und erleichtert Entwicklern den Umstieg zwischen Sprachen dieser Familie. Auf der anderen Seite verleitet es auch dazu, die Details verwandter Konzepte in unterschiedlichen Programmiersprachen zu übergehen.

      Also bitte: Tieferes Verständnis für PHP wirst du aus C nicht erlernen.

      Er wird Referenzen zu schätzen lernen ;)

      Aber davon mal abgesehen: Die beiden Sprachen unterscheiden sich zudem in den technischen Grundlagen. Das eine wird in Maschinencode übersetzt, das andere ist eine Interpretersprache.

      Ob eine Sprache interpretiert oder kompiliert wird ist keine Eigenschaft der Sprache, sondern der Umgebung. Man kann C ebenso interpretieren, wie man PHP kompilieren kann. In der Praxis wird C aber meistens kompiliert und PHP interpretiert.

      Mit einer Zeichenkette ist das einfach zu verstehen:

      Buchstabe: abcdefg
      Byte:      0123456

      
      > Die Zeichenkette belegt sieben aufeinanderfolgende Bytes im Speicher. Das c erreichst du, indem du die  Startadresse der Zeichenkette nimmst und 2 hinzuaddierst.  
        
      Bemerkenswert hierbei ist, dass C die Größe der Elemente automatisch beim inkrementieren berücksichtigt. Das obere Beispiel funktioniert folglich auch, wenn ein Zeichen in der Zeichenkette mehrere Bytes belegen würde. Also angenommen ein Zeichen bräuchte zwei Bytes. Dann müsstest du, um das 'c' zu erreichen eigentlich zwei mal die Datengröße von 2 Bytes auf die Startadresse addieren. C weiß aber, dass ein Element in der Zeichenkette zwei Bytes belegt und kann diese Berechnung automatisch vornehmen. Das heißt `*(Startadresse + 2)`{:.language-c} dereferenziert die Adresse, an der das Low-Byte des Zeichen 'c' abgespeichert. Beim Dereferenzieren wird wiederum Rücksicht auf die Datengröße genommen und man erhält die zwei Byte große Zeichenkette 'c'.  
        
      Der Datentyp int hat auf 32-bit Systemen eine Speicherverbrauch von 4 Bytes. Da dein Pointer auf das Array, die Information mit sich trägt, dass es sich bei den Elementen um int-Werte handelt, weiß C, dass es pro Element 4 Bytes weiter im Speicher zu operieren hat.  
        
      Galileo Computing bietet imho. eine [gelungene Einführung](http://openbook.galileocomputing.de/c_von_a_bis_z/012_c_zeiger_001.htm#mjee28a882ef9125d2c4cc49121b2c65c1) zu Zeigern in C.
      
      1. Meine Herren!

        Buchstabe: abcdefg
        Byte:      0123456

          
        Dieses Beispiel, dass ich fälschlicherweise mit dem BB-Code für die Programmiersprache C ausgezeichnet habe, wird in der Vorschau-Ansicht wunderbar mit einer Monospace-Schriftart (consolas) dargestellt. In der Live-Ansicht leider nicht (Helvetica).  
          
        Das Problem scheint auch für CSS- und JS-Codebeispiele zu bestehen, andere hab ich nicht getestet.  
          
        Unabhängig davon, wäre es schön, wenn es eine Möglichkeit gäbe preformatierten Text auch ohne Code-Auszeichnung darzustellen.
        
        1. Om nah hoo pez nyeetz, 1UnitedPower!

          Buchstabe: abcdefg

          Byte:      0123456

          
          > Dieses Beispiel, dass ich fälschlicherweise mit dem BB-Code für die Programmiersprache C ausgezeichnet habe, wird in der Vorschau-Ansicht wunderbar mit einer Monospace-Schriftart (consolas) dargestellt. In der Live-Ansicht leider nicht (Helvetica).  
          > Das Problem scheint auch für CSS- und JS-Codebeispiele zu bestehen, andere hab ich nicht getestet.  
            
          Danke für den Hinweis.  
            
          In der Vorschau wird der Beitrag so dargestellt, wie ihn der unangemeldete Leser zu sehen bekommt.  
            
          Eine in den Benutzereinstellungen eingestellte Schriftart gilt auch für .nachricht-text und damit auch für darin enthaltene Code-Elemente. Ich schau mal, wie ich das am besten beheben kann.  
            
          
          > Unabhängig davon, wäre es schön, wenn es eine Möglichkeit gäbe preformatierten Text auch ohne Code-Auszeichnung darzustellen.  
            
          Du kannst einfach aus [code lang=foo] das lang-Attribut entfernen und die Fehlermeldung beim Absenden ignorieren.  
            
          Matthias
          
          -- 
          Der Unterschied zwischen Java und JavaScript ist größer als der zwischen [Blei und bleichen](http://selfhtml.apsel-mv.de/java-javascript/index.php?buchstabe=B#blei).  
          ![](http://www.billiger-im-urlaub.de/kreis_sw.gif)  
          
          
          1. Meine Herren!

            Eine in den Benutzereinstellungen eingestellte Schriftart gilt auch für .nachricht-text und damit auch für darin enthaltene Code-Elemente. Ich schau mal, wie ich das am besten beheben kann.

            Stimmt, ich hab vorläufig meine Schriftart auf 'Standard' gestellt, damit ist das Problem für mich indes aus der Welt.

      2. Moin 1UnitedPower,

        Hach, wehowei. Dennis Ritchie und seine Mitstreiter hatten Ahnung von dem, was sie taten. PHP hingegen ist ein ohne Sinn und Konzept zusammengeschusterter Haufen Scheiße.

        Ich bin auch kein PHP-Fan, aber diese Aussage halte ich für stark überzogen. PHP würde ursprünglich als Programmiersprache für didaktische Zwecke entwickelt.

        O.O Bitte was?

        Nein, wohl kaum. PHP wurde ursprünglich von Rasmus Lerdorf als Preprozessor entwickelt um das schreiben von Webseiten einfacher zu machen. Für didaktische Zwecke ist sie auch völlig ungeeignet, erlaubt sie einem doch zu schlampen wo immer es geht.

        Deshalb kennt PHP vermutlich auch so komplizierte Gebilde wie Zeiger und statische Typisierung nicht.

        PHP kennt Zeiger (dort heißen sie „Resourcen“ und „Referenzen“), allerdings in der Tat keine statische Typisierung.

        LG,
         CK

        1. Meine Herren!

          Nein, wohl kaum. PHP wurde ursprünglich von Rasmus Lerdorf als Preprozessor entwickelt um das schreiben von Webseiten einfacher zu machen.

          Du hast recht, wie kam ich denn darauf? Da hat mir irgend eine staubige Hirnwindung einen Streich gespielt.

          PHP kennt Zeiger (dort heißen sie „Resourcen“ und „Referenzen“)

          Wobei Referenzen keine Zeiger-Arithmetik kennen.

          1. Om nah hoo pez nyeetz, 1UnitedPower!

            Du hast recht, wie kam ich denn darauf? Da hat mir irgend eine staubige Hirnwindung einen Streich gespielt.

            Ranschburg-Phänomen - Die Sprache, die zum lehren entwickelt wurde, ist Pascal.

            Matthias

            --
            Der Unterschied zwischen Java und JavaScript ist größer als der zwischen Mars und Marschflugkörper.

            1. Meine Herren!

              Du hast recht, wie kam ich denn darauf? Da hat mir irgend eine staubige Hirnwindung einen Streich gespielt.

              Ranschburg-Phänomen - Die Sprache, die zum lehren entwickelt wurde, ist Pascal.

              Verrückt, ich habe PHP und Pascal tatsächlich sehr nahe bei einander gelernt.

      3. Ich bin auch kein PHP-Fan, aber diese Aussage halte ich für stark überzogen.

        Ich auch - ein bisschen.
        PHP bietet vieles was für Webentwicklung unverzeichtbar ist. Allerdings liegt das nicht am Aufbau der Sprache sondern an dem was PHP an Funktionen bietet. Das könnten sauberer aufgebaute Sprachen auch.

        Ich hätte oft gerne etwas mehr Typsicherheit, Sicherheit was Deklaration von Variablen angeht usw. Variablen können ein String sein, oder eine Zahl, gar nicht initialisiert sein, eine Funktion kann alle möglichen Typen zurückgeben die man zum Teil mit === prüfen muss... furchtbar! Die Zeit die ich mir spare wenn ich PHP lerne und mich nicht in typisierte Variablen hineindenken muss, geht 1000-fach wieder drauf wenn ich Fehler suche die durch eben sowas zustande kommen.

        Natürlich ist es schön wenn man einen String einfach nutzen kann statt ein Array of Character anzulegen so wie in C. Aber trotzdem halte ich PHP für eine absolut schlampige Sprache. Wie die sich so sehr durchsetzen kann dass es praktisch keine (vergleichbare nur sicherere) Alternative gibt ist mir unverständlich.

      4. Hi!

        Das stimmt, aber das würde ich nicht als reinen Nachteil von PHP betrachten. Die C-Syntax-Familie schlägt weite Wurzeln und erleichtert Entwicklern den Umstieg zwischen Sprachen dieser Familie. Auf der anderen Seite verleitet es auch dazu, die Details verwandter Konzepte in unterschiedlichen Programmiersprachen zu übergehen.

        ich finde PHP für Websachen auch vollkommen i.O. Besser als Perl zumindest. Wobei Perl mit Sicherheit die bessere Sprache ist. Zum Entwickeln habe ich Java immer sehr geschätzt, aber Java ist halt auch wieder ziemlich top level im Vergleich zu C. Und irgendwas gefällt mir an Java trotzdem nicht, alles sehr umständlich irgendwie.

        Mit einer Zeichenkette ist das einfach zu verstehen:

        Buchstabe: abcdefg

        Byte:      0123456

        
        > > Die Zeichenkette belegt sieben aufeinanderfolgende Bytes im Speicher. Das c erreichst du, indem du die  Startadresse der Zeichenkette nimmst und 2 hinzuaddierst.  
        >   
        > Bemerkenswert hierbei ist, dass C die Größe der Elemente automatisch beim inkrementieren berücksichtigt. Das obere Beispiel funktioniert folglich auch, wenn ein Zeichen in der Zeichenkette mehrere Bytes belegen würde. Also angenommen ein Zeichen bräuchte zwei Bytes. Dann müsstest du, um das 'c' zu erreichen eigentlich zwei mal die Datengröße von 2 Bytes auf die Startadresse addieren. C weiß aber, dass ein Element in der Zeichenkette zwei Bytes belegt und kann diese Berechnung automatisch vornehmen. Das heißt `*(Startadresse + 2)`{:.language-c} dereferenziert die Adresse, an der das Low-Byte des Zeichen 'c' abgespeichert. Beim Dereferenzieren wird wiederum Rücksicht auf die Datengröße genommen und man erhält die zwei Byte große Zeichenkette 'c'.  
          
        Das probiere ich heute abend mal aus... hört sich interessant an. Wenn ich also ein Dokument als utf-8 kodiere und eine Zeichenkette "aä" wähle, die folglich 3 Bytes lang ist, dann sollte das funktionieren. Teste ich mal.  
          
        
        > Der Datentyp int hat auf 32-bit Systemen eine Speicherverbrauch von 4 Bytes. Da dein Pointer auf das Array, die Information mit sich trägt, dass es sich bei den Elementen um int-Werte handelt, weiß C, dass es pro Element 4 Bytes weiter im Speicher zu operieren hat.  
        >   
        > Galileo Computing bietet imho. eine [gelungene Einführung](http://openbook.galileocomputing.de/c_von_a_bis_z/012_c_zeiger_001.htm#mjee28a882ef9125d2c4cc49121b2c65c1) zu Zeigern in C.  
          
        Danke für den Tip! Galileo Computing ist immer recht gut, stimmt.
        
        1. Moin Mechthild,

          Bemerkenswert hierbei ist, dass C die Größe der Elemente automatisch beim inkrementieren berücksichtigt. Das obere Beispiel funktioniert folglich auch, wenn ein Zeichen in der Zeichenkette mehrere Bytes belegen würde. Also angenommen ein Zeichen bräuchte zwei Bytes. Dann müsstest du, um das 'c' zu erreichen eigentlich zwei mal die Datengröße von 2 Bytes auf die Startadresse addieren. C weiß aber, dass ein Element in der Zeichenkette zwei Bytes belegt und kann diese Berechnung automatisch vornehmen. Das heißt *(Startadresse + 2) dereferenziert die Adresse, an der das Low-Byte des Zeichen 'c' abgespeichert. Beim Dereferenzieren wird wiederum Rücksicht auf die Datengröße genommen und man erhält die zwei Byte große Zeichenkette 'c'.

          Das probiere ich heute abend mal aus... hört sich interessant an. Wenn ich also ein Dokument als utf-8 kodiere und eine Zeichenkette "aä" wähle, die folglich 3 Bytes lang ist, dann sollte das funktionieren. Teste ich mal.

          Nein, da hast du etwas missverstanden. Auch wenn du dein Dokument in utf-8 kodierst, ist der interne Datentyp char in C immer noch 1 byte lang (auf gängiger Hardware). Und deshalb wird ein char \*ptr = "aä"; ++ptr; danach immer noch auf das erste Byte der UTF-8-Sequenz zeigen.

          LG,
           CK

          1. Moin Christian,

            hm, ich seh schon, ich muss da mehr üben...

            Das probiere ich heute abend mal aus... hört sich interessant an. Wenn ich also ein Dokument als utf-8 kodiere und eine Zeichenkette "aä" wähle, die folglich 3 Bytes lang ist, dann sollte das funktionieren. Teste ich mal.

            Nein, da hast du etwas missverstanden. Auch wenn du dein Dokument in utf-8 kodierst, ist der interne Datentyp char in C immer noch 1 byte lang (auf gängiger Hardware). Und deshalb wird ein char \*ptr = "aä"; ++ptr; danach immer noch auf das erste Byte der UTF-8-Sequenz zeigen.

            so ganz kapiert hab ich's noch nicht. Oder anders, das, was du schreibst, verstehe ich schon so einigermaßen. Was unitedpower schrieb, kann ich aber nicht so recht einordnen. Oder sollte das vermutlich nur ein Beispiel sein, um zu veranschaulichen, dass die Datengröße beim Dereferenzieren immer automatisch berechnet wird?

            Wie ist denn das dann eigentlich mit uft8 bzw. Mehrbytekodierungen? Ist die interne Größe von
            char *ptr = 'ä'; 1 Byte, egal wie das Dokument kodiert ist?
            das werde ich heute abend mal bisschen machen, mit Zeichen und Kodierungen spielen.

            1. Moin Mechthild,

              Nein, da hast du etwas missverstanden. Auch wenn du dein Dokument in utf-8 kodierst, ist der interne Datentyp char in C immer noch 1 byte lang (auf gängiger Hardware). Und deshalb wird ein char \*ptr = "aä"; ++ptr; danach immer noch auf das erste Byte der UTF-8-Sequenz zeigen.

              so ganz kapiert hab ich's noch nicht. Oder anders, das, was du schreibst, verstehe ich schon so einigermaßen. Was unitedpower schrieb, kann ich aber nicht so recht einordnen. Oder sollte das vermutlich nur ein Beispiel sein, um zu veranschaulichen, dass die Datengröße beim Dereferenzieren immer automatisch berechnet wird?

              Was 1UnitedPower veranschaulichen wollte, war die Tatsache, dass bei Pointer-Arithmetik die Anzahl der Bytes, die ein *(ptr+1) „nach vorne gehen“ muss, vom Compiler ausgerechnet wird anhand der Typen-Informationen. Einfaches Beispiel:

                
              #include <stdio.h>  
                
              int main(void) {  
                int ifield[] = {1,2};  
                char cfield[] = {'a','b'};  
                printf("address of ifield and ifield+1: %p %p\n", ifield, ifield+1);  
                printf("address of cfield and cfield+1: %p %p\n", cfield, cfield+1);  
                return 0;  
              }  
              
              

              Beispielhafter output:

                
              address of ifield and ifield+1: 0x7fff2ea67890 0x7fff2ea67894  
              address of cfield and cfield+1: 0x7fff2ea67880 0x7fff2ea67881  
              
              

              Wie du sehen kannst, ist die Differenz der beiden Integer-Pointer 4 (0x7fff2ea67890 - 0x7fff2ea67894 = 4) und die Differenz der beiden Char-Pointer 1 (0x7fff2ea67880 - 0x7fff2ea67881 = 1), und das obwohl wir bei beiden Arrays nur 1 addiert haben.

              Wie ist denn das dann eigentlich mit uft8 bzw. Mehrbytekodierungen? Ist die interne Größe von
              char *ptr = 'ä'; 1 Byte, egal wie das Dokument kodiert ist?

              Das fliegt dir um die Ohren:

                
              int main(void) {  
                char c = 'ä';  
                return 0;  
              }  
              
              
                
              → ckruse@achilles ~  % gcc -W -Wall -o test test.c  
              test.c: In function ‘main’:  
              test.c:4:12: warning: multi-character character constant [-Wmultichar]  
                 char c = 'ä';  
                          ^  
              test.c:4:3: warning: overflow in implicit constant conversion [-Woverflow]  
                 char c = 'ä';  
                 ^  
              test.c:4:8: warning: unused variable ‘c’ [-Wunused-variable]  
                 char c = 'ä';  
                      ^  
              → ckruse@achilles ~  %  
              
              

              Ein Byte kann halt nicht mehrere Bytes aufnehmen :-) Du musst das als char-Array handhaben:

                
              int main(void) {  
                char *c = "ä";  
                return 0;  
              }  
              
              
                
              → ckruse@achilles ~  % gcc -W -Wall -o test test.c  
              test.c: In function ‘main’:  
              test.c:4:9: warning: unused variable ‘c’ [-Wunused-variable]  
                 char *c = "ä";  
                       ^  
              → ckruse@achilles ~  %  
              
              

              C kennt halt keine Zeichen, sondern nur Bytes. Insofern ist der Datentyp char natürlich auch irreführend allerdings halt auch der Zeit geschuldet, in der C entstanden ist.

              LG,
               CK

              1. Hi Christian,

                sehr vielen Dank. Jetzt bin ich nochmal ein Stück schlauer geworden. Gut erklärt!

                Was 1UnitedPower veranschaulichen wollte, war die Tatsache, dass bei Pointer-Arithmetik die Anzahl der Bytes, die ein *(ptr+1) „nach vorne gehen“ muss, vom Compiler ausgerechnet wird anhand der Typen-Informationen. Einfaches Beispiel:

                ach soo. Ja, das hatte ich sowieso angenommen. Aber ist klar jetzt

                #include <stdio.h>

                int main(void) {
                  int ifield[] = {1,2};
                  char cfield[] = {'a','b'};
                  printf("address of ifield and ifield+1: %p %p\n", ifield, ifield+1);
                  printf("address of cfield and cfield+1: %p %p\n", cfield, cfield+1);
                  return 0;
                }

                
                >   
                > Beispielhafter output:  
                >   
                > ~~~
                  
                
                > address of ifield and ifield+1: 0x7fff2ea67890 0x7fff2ea67894  
                > address of cfield and cfield+1: 0x7fff2ea67880 0x7fff2ea67881  
                > 
                
                

                Wie du sehen kannst, ist die Differenz der beiden Integer-Pointer 4 (0x7fff2ea67890 - 0x7fff2ea67894 = 4) und die Differenz der beiden Char-Pointer 1 (0x7fff2ea67880 - 0x7fff2ea67881 = 1), und das obwohl wir bei beiden Arrays nur 1 addiert haben.

                super erklärt, danke. Das ist verstanden.

                Wie ist denn das dann eigentlich mit uft8 bzw. Mehrbytekodierungen? Ist die interne Größe von
                char *ptr = 'ä'; 1 Byte, egal wie das Dokument kodiert ist?

                Das fliegt dir um die Ohren:

                int main(void) {
                  char c = 'ä';
                  return 0;
                }

                  
                zum Glück! Ich war schon etwas verwundert wegen den 2 Bytes...  
                  
                  
                
                > Ein Byte kann halt nicht mehrere Bytes aufnehmen :-) Du musst das als char-Array handhaben:  
                  
                eben, eben. Wie gesagt, etwas irritiert gewesen.  
                  
                
                > C kennt halt keine Zeichen, sondern nur Bytes. Insofern ist der Datentyp `char` natürlich auch irreführend allerdings halt auch der Zeit geschuldet, in der C entstanden ist.  
                  
                auch hier muss man immer noch etwas umdenken, aber grundsätzlich ist mir das jetzt klar. Ein paar Übungsprogramme, dann sollte das gefestigt sein, hoffe ich.  
                  
                Danke euch allen!
                
        2. Das probiere ich heute abend mal aus... hört sich interessant an. Wenn ich also ein Dokument als utf-8 kodiere und eine Zeichenkette "aä" wähle, die folglich 3 Bytes lang ist, dann sollte das funktionieren. Teste ich mal.

          Nein, das funktioniert nicht. Dafür musst du spezielle Funktionen nutzen. Das 2-Byte-Zeichen was hier gemeint, muss IMMER 2 Byte haben.
          Oder anders, ein Datentyp in C hat immer die gleiche Größe.

    3. Moin Warmbier,

      Hach, wehowei. Dennis Ritchie und seine Mitstreiter hatten Ahnung von dem, was sie taten. PHP hingegen ist ein ohne Sinn und Konzept zusammengeschusterter Haufen Scheiße.

      Passend dazu auch diese Peinlichkeit: http://news.php.net/php.internals/70691

      LG,
       CK