Zeiger und Arrays

18. Mai 2009 · in C Geschrieben / C + +

In den ersten Beitrag dieser Serie sprechen wir ein wenig über Zeiger. In der zweiten, sprechen wir über Referenzen. Heute werden wir uns auf die enge Beziehung zu konzentrieren (ui!) Zwischen Zeigern und Arrays (oder Arrays).

Unter Hinweis auf Arrays

Ein Array oder ein Array oder eine Anordnung, es ist eine mathematische Abstraktion verwendet, um eine Reihe von homogenen Daten darstellen, dh die gleiche Art (int, float, etc.). Diese Abstraktion ist in Form einer Tabelle organisiert, mit Zeilen und Spalten. Jedes Element in dem Array nur Koordinaten (Zeile und Spalte), so dass ein bestimmtes Element E (i, j) ist das einzige Element in die "Linie i", "j-Spalte.

Die Syntax zur Deklaration Arrays lautet wie folgt:

  / / One-dimensional Arrays oder Vektoren
 10 ] ; // 10 elementos, do 0 ao 9 Erstausbildung int [10] / / 10 Elemente, von 0 bis 9
 23 ] ; // 23 elementos, do 0 ao 22 cvet char [23] / / 23 Elemente, von 0 bis 22

 / / Zweidimensionale Matrizen
 2 ] [ 3 ] ; // 2 linhas (0 a 1) e 3 colunas (0 a 2) int imat [2] [3] / / 2 Zeilen (0 bis 1) und 3 Spalten (0 bis 2)
 10 ] [ 2 ] ; // 10 linhas (0 a 9) e 2 colunas (0 a 1) DMAT double [10] [2] / / 10 Zeilen (0 bis 9) und 2 Spalten (0 bis 1) 

Jedes Element des Arrays ist unabhängig von den anderen und kann als der folgenden Syntax aufgerufen werden:

  10 ] ; Erstausbildung int [10];
 3 ] [ 4 ] ; int imat [3] [4];

 / / Ändern der vierte Element des Vektors der beruflichen Erstausbildung.
 / / Beachten Sie, dass fängt an, von der Null-Element ausführen
 ] = 13 ; Erstausbildung [3] = 13;

 / / Lesen der zweite Element des Vektors der beruflichen Erstausbildung
 ivet [ 1 ] ; int num = Erstausbildung [1];

 / / Ändern der Elemente der ersten Zeile, zweite Spalte imat
 ] [ 1 ] = 42 ; imat [0] [1] = 42;

 / / Reading das Element in der dritten Reihe, vierte Spalte imat
 imat [ 2 ] [ 3 ] ; int foo = imat [2] [3]; 

In diesem Beitrag wird die Diskussion über die Hintergrund-Matrix, untersuchen wir nur die Beziehung zwischen Arrays und Zeiger in einem recht intuitiv.

Größen-Arrays

Da ein Array ist eine Abstraktion, die mehrere Werte des gleichen Typs, wie groß enthält, ist es? Wie viel Platz einnimmt in der Erinnerung?

Betrachten Sie den untenstehenden Code ein:

  17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
  10 ] ; Erstausbildung int [10];
 13 ] ; cvet char [13];
 20 ] ; Doppel-dvet [20];

 3 ] [ 4 ] ; CMAT char [3] [4];
 5 ] [ 4 ] ; int imat [5] [4];

 "sizeof(int)     = " << sizeof ( int ) << endl ; cout << "sizeof (int) =" <<sizeof (int) <<endl;
 "sizeof(char)    = " << sizeof ( char ) << endl ; cout << "sizeof (char) =" <<sizeof (char) <<endl;
 "sizeof(double)  = " << sizeof ( double ) << endl ; cout << "sizeof (double) =" <<sizeof (double) <<endl;

 "sizeof(ivet)    = " << sizeof ( ivet ) << endl ; cout << "sizeof (berufliche Erstausbildung) =" <<sizeof (berufliche Erstausbildung) <<endl;
 "sizeof(cvet)    = " << sizeof ( cvet ) << endl ; cout << "sizeof (Weiterbildung) =" <<sizeof (Weiterbildung) <<endl;
 "sizeof(dvet)    = " << sizeof ( dvet ) << endl ; cout << "sizeof (dvet) =" <<sizeof (dvet) <<endl;
 "sizeof(cmat)    = " << sizeof ( cmat ) << endl ; cout << "sizeof (CMAT) =" <<sizeof (CMAT) <<endl;
 "sizeof(imat)    = " << sizeof ( imat ) << endl ; cout << "sizeof (imat) =" <<sizeof (imat) <<endl; 

Das Ergebnis ist durchaus sinnvoll. Der Raum durch eine Reihe besetzt ist gleich der Anzahl der Elemente durch die Größe des Typs des Elements multipliziert (Zeilen x Spalten x sizeof (Typ)). Allerdings, wenn jedes Element unabhängig ist, wird davon ausgegangen, dass jeder einen besonderen Platz in Speicher belegt, sonst wird man ein anderes Element zu überschreiben. Also, ist, dass jedes Element seinen eigenen Speicher-Adresse hat?

Arrays und Speicheradressen

Aus praktischen Gründen werden wir dann analysieren die möglichen Adressen ein Array von Zeichen, deren Größe wird nur vergeben, ein 1-Byte:

  15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
  5 ; const int max = 5;
 max ] = { 'A' , 'B' , 'C' , 'D' , 'E' } ; cvet char [max] = ( 'A', 'B', 'C', 'D', 'E');

 / / Anzeigen der Inhalte, Werte und Adressen der Daten.
 "Índice \t Valor \t Endereço do elemento \n " ) ; printf ( "Index \ t Wert \ t-Element-Adresse \ n");
 int i = 0 ; i < max ; i ++ ) { for (int i = 0; i <max; i + +) (
     "%d \t %c \t %p \n " , i, cvet [ i ] , & cvet [ i ] ) ; printf ( "% d \ t% c \ t% p \ n", i, cvet [i], & Weiterbildung [i]);
 )

 / / Drucken der Adresse des Arrays
 "Endereço da matriz: %p \n " , & cvet ) ; printf ( "Adresse des Arrays:% p \ n", & Weiterbildung);

 / / Drucken der Adresse des Arrays wieder
 "Endereço da matriz: %p \n " , cvet ) ; printf ( "Adresse des Arrays:% p \ n", cvet); 

Die Adressen der Elemente sind aufeinander aufbauend, dh jedes Element gespeichert neben dem ehemaligen. Außerdem gibt es zwei weitere interessante Fakten:

  1. Die Adresse des array (& Weiterbildung), in Zeile 25 dargestellt, ist das gleiche wie das erste Element des Arrays;
  2. Der sehr variable cvet kann als Zeiger interpretiert werden, wie in Zeile 28 gezeigt;

In C + + ist ein gemeinsames Array ein zusammenhängender Speicherblock, dessen Name interpretiert werden kann (cast) als Zeiger, der auf sein erstes Element. Zusätzlich ist es bis zu einem Punkt Zeiger auf ein Array machen, wo der Zeiger auf das Ziel der gleichen Art wie die Art der Array-Elemente. Während atrubuição ein Array in einen Zeiger, macht der Compiler eine implizite Typkonvertierung. Die Ziel-Zeiger als einen Zeiger auf den Speicherbereich durch das Array besetzten interpretiert werden.

Eine der Folgen nicht so offensichtlich ist, dass bei der impliziten Stimmen verloren, ist die Information, dass das Gebiet wurde ein Speicher-Array. So verloren die Informationen über die Größe des Arrays. Aus der Sicht des Zeigers ist, zeigt es an den Anfang eines beliebigen Block des Speichers, eine Nummer zu willkürlich. Check out ein Array und einen Zeiger zu gehen bedeutet, sich von einer Abstraktion restriktiver und mehr High-Level-Abstraktion zu einer weniger restriktiven und unteren Ebene.

Auf der anderen Seite versuchen, einen Zeiger auf ein Array zuweisen erzeugt einen Compiler-Fehler für unvereinbar Typen. Ein Array ist ein Speicherblock von n Daten (in Bytes), als Zeiger nur ein Eingang, eine Adresse hat. Der Compiler kann nicht im Voraus wissen, ob ein Zeiger auf eine Fläche von 1, 2 oder 200 bytes.

  15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
  300 ; const int max = 300;
 max ] ; cvet char [max];
 pc = 0 ; char * pc = 0;

 " \n Antes da atribuição \n " ) ; printf ( "\ n Bevor die Zuweisung \ n");
 "cvet = %p \n " , cvet ) ; printf ( "cvet =% p \ n", cvet);
 "pc   = %p \n " , pc ) ; printf ( "pc =% p \ n", pc);
 "sizeof(cvet) = %lu \n " , sizeof ( cvet ) ) ; printf ( "sizeof (Weiterbildung) =% lu \ n", sizeof (Weiterbildung));
 "sizeof(pc)   = %lu \n " , sizeof ( pc ) ) ; printf ( "sizeof (pc) =% lu \ n", sizeof (pc));

 pc = cvet;

 " \n Depois da atribuição \n " ) ; printf ( "\ n Nach der Zuweisung \ n");
 "cvet = %p \n " , cvet ) ; printf ( "cvet =% p \ n", cvet);
 "pc   = %p \n " , pc ) ; printf ( "pc =% p \ n", pc);
 "sizeof(cvet) = %lu \n " , sizeof ( cvet ) ) ; printf ( "sizeof (Weiterbildung) =% lu \ n", sizeof (Weiterbildung));
 "sizeof(pc)   = %lu \n " , sizeof ( pc ) ) ; printf ( "sizeof (pc) =% lu \ n", sizeof (pc)); 

Beachten Sie, dass vor der Zuweisung (Linie 25) den Zeiger Stck. null ist, so war es initialisiert. Da die Größen zeigen, dass das Array 300 Byte und der Zeiger nur 8 (meine Maschine hat, ist ein AMD-64). Nach der Abtretung sowohl sie den "Punkt" auf den gleichen Bereich des Speichers, aber die Größe nicht ändern. Es war eine implizite Umwandlung in char [300], um char *, und der Zeiger in diesem Spiel PC kann nicht wissen, die Größe des Speicherbereichs, auf die sie Punkte. Da das Array cvet weiterhin genau zu wissen, was er ist, ohne existenzielle Krise.

Zeigerarithmetik - Vulgar, so what?

Warum, wenn ich weiß, dass die Daten in einem Array angeordnet sind, Seite an Seite, kann ich einen Zeiger verwenden, die dann für die nächste Adresse zu springen und den Zugriff auf das nächste Element. Der Name dieser Arithmetik Zeigern.

  15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
  6 ; const int max = 6; 
 max ] = { 'B' , 'L' , 'A' , 'B' , 'O' , 'S' } ; cvet char [max] = ( 'B', 'L', 'A', 'B', 'O', 'S');

 pc = cvet ; char * pc = cvet;

 int i = 0 ; i < max ; i ++ ) { for (int i = 0; i <max; i + +) (
     "%c" , * ( pc + i ) ) ; printf ( "% c", * (PC + i));
 )
 " \n " ) ; printf ( "\ n");

 int i = 0 ; i < max ; i ++ ) { for (int i = 0; i <max; i + +) (
     "%c" , * pc ++ ) ; printf ( "% c", * pc + +);
 )
 " \n " ) ; printf ( "\ n");


 / / Nun wird die ganze
 max ] = { 1 , 2 , 3 , 4 , 5 , 6 } ; Erstausbildung int [max] = (1, 2, 3, 4, 5, 6);
 pi = ivet ; int * pi = beruflichen Erstausbildung;

 int i = 0 ; i < max ; i ++ ) { for (int i = 0; i <max; i + +) (
     "%p = %d \n " , pi, * pi ++ ) ; printf ( "% p =% d \ n", pi, pi * + +);
 )
 " \n " ) ; printf ( "\ n");

 / / In zwei Dimensionen
 2 ] [ 3 ] = { { 'B' , 'L' , 'A' } , { 'B' , 'O' , 'S' } } ; CMAT char [2] [3] = (( 'B', 'L', 'A'), ( 'B', 'O', 'S'));
 ppc ; char * ppc;

 char * ) cmat ; ppc = (char *) CMAT;

 int i = 0 ; i < 2 ; i ++ ) { for (int i = 0; i <2; i + +) (
     int j = 0 ; j < 3 ; j ++ ) { for (j = 0, j <3 j + +) (
         "%c" , * ( ppc + 3 * i + j ) ) ; printf ( "% c", * (ppc + 3 * i + j));
     )
     " \n " ) ; printf ( "\ n");
 )
 " \n " ) ; printf ( "\ n"); 

In Zeile 18 wird der Zeiger PC ist nun auf das Array cvet und damit für das erste Element, das Zeichen 'B'. In Zeile 21 den Inhalt des PC, der die Adresse, wo es mit dem 'B', dh gespeichert ist, wird dann erhöht de-referenziert. In der ersten Woche den Wert von i ist Null, so dass es Dereferenzierung auf den Wert 'B'. Im folgenden wird folgender Adresse ist gegen de-zu anderen Zeichen in der ursprünglichen Array gespeichert verwiesen wird. Es ist mehr oder weniger, was der Compiler intern, wenn Sie die Syntax verwenden cvet [i]. Die Abstraktion von Array bietet Ihnen ein freundlicher Umgang mit angrenzenden Gebieten des Speichers, * (PC + i).

Aber wenn die Entnahme von Array ist einfacher zu bedienen, dass Zeigerarithmetik?

Eine Antwort ist, Zeile 26. Sie hat die gleiche Sache, dass die Linie 21, aber ein wenig schneller. In der Syntax der Linie 21, oder auf ähnliche Weise, die Array-Syntax, kann der Zugriff auf Daten zusammengefasst werden in eine sehr grobe in den Kommandos:

  1. Nutzen Sie die Basisadresse des Arrays;
  2. Hinzufügen, um den Wert des Index-Adresse;
  3. De-Referenzierung der neuen Adresse;

Bereits mit Pointer-Arithmetik wie folgt aussieht:

  1. De-Referenzierung Diese Adresse;

Der Befehl increment (oder im Hinblick auf Adressen gemacht) werden nicht gezählt, da es Teil der Schleife, obwohl i + +; schneller ist als a = b + c;. Nun stellen Sie sich diesen kleinen Gewinn von 66% auf Daten von 1 MB angewandt. Es werden mehr als 2 Millionen Befehle weniger!

Die Technik der über einen Zeiger auf einen beliebigen Bereich des Speichers Griff ist in der Regel in geringen Programmierung verwendet (näher an der Maschine), eine Handhabung von Puffern und Streicher, unter anderem schmutzigen Tricks. In den Eingeweiden von Computern, Operationen, die große Bereiche der Sweep-Speicher, werden häufig mit Pointer-Arithmetik durchgeführt. Auf dieser Ebene herrscht Darwin höchste und nur die vorbereitete überleben. Von hier aus beginnt die Sprache zu einer Macht, die nur die reinen Herzens zu verstehen geben kann.

Ein wichtiger Hinweis ist, dass zwischen den Linien 31 und 38 ist die Erfahrung mit ganzen Zahlen wiederholt. Beachten Sie, dass die Zahlen 4 Bytes, die Zuwächse werden automatisch 4 von 4 Bytes, nicht von 1 bis 1, dh die Schrittweite automatisch für sizeof (Typ) berechnet. Verbessern bedeutet einen Zeiger auf die nächste Bereich des Arbeitsspeichers ähnlich wie die eigentlichen Daten, nicht nur die nächste Adresse zugreifen. Da die Größe eines char ist ein Byte, wenn incremetamos einen Zeiger auf char, bewegen sich nur 1 Byte. Wenn Förderung eines Zeigers zu verdoppeln, werden wir 8 Bytes zu bewegen, und so weiter.

Eine weitere Beobachtung ist, dass ein zweidimensionales Array linearisiert werden "kann", wie in den Zeilen 40 bis 52. Dies ist nützlich, wenn anwendbar, eine bessere Nutzung der Prozessor-Cache zu machen, zum Beispiel.

void *, die pansexual Zeiger

Früher habe ich gesagt, dass nur möglich war, ein Array in einen Zeiger, der für die gleiche Art wie die Array-Daten zuzuweisen. Ich eklatant Lüge! Der Grund dafür ist, dass für jemanden, der Post aufgegeben haben, bevor dieses Thema, es sicherer ist, zu glauben, dass sie :) !

Es gibt zwei Ausnahmen von der Regel. Die erste ist, wenn es eine explizite Konvertierung von Typen und Zeiger-Ziel "denkt", dass den Hinweis auf die richtige Art. Ein Beispiel ist in Zeile 44 des vorherigen Code.

Der zweite ist der Fall von Zeigern auf void. Ein Zeiger auf void ist ein Zeiger, dass keine Anforderungen an die Art der Daten, die im Bereich des Speichers, auf die sie Punkte macht. Er ist ein Zeiger pra einen allgemeinen Bereich des Arbeitsspeichers, etwas sehr niedrigen Niveau.

Um eine der Informationen benutzen, um durch einen Zeiger zeigte auf, vor der Leere de-referenzieren, müssen Sie eine gültige Art eine explizite Umwandlung machen, ist es ein int * ist ein de-int und char * ist de-referenziert referenziert char, guess what it de-referenziert wird ein void *?

  15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
  6 ; const int max = 6; 
 max ] = { 'B' , 'L' , 'A' , 'B' , 'O' , 'S' } ; cvet char [max] = ( 'B', 'L', 'A', 'B', 'O', 'S');

 pv = cvet ; void * pv = cvet;

 / / Compile error.
 / / * Pv
 / / Wie viel ist sizeof (void)?

 int i = 0 ; i < max ; i ++ ) { for (int i = 0; i <max; i + +) (
     "%c" , * ( ( ( char * ) pv ) + i ) ) ; printf ( "% c", * (((char *) pv) + i));
 )
 " \n " ) ; printf ( "\ n"); 

Zeiger auf void werden verwendet, wenn man braucht, um einen allgemeinen Bereich des Speichers Nummer ohne Kontrolle / Wissen von der Art der Daten, die diesem Bereich enthält, oder Funktionen, die keine Annahmen über die Typen der Parameter, wie die API lib pthreads (Link beliebig).

Geschlossen

Je mehr wir uns tauchen Sie ein in die Themen auf Zeiger, neben der Maschine wir. Ein Großteil der Leistungsfähigkeit von C und C + + kommt von dort, und viele der Probleme. Die Komplexität nimmt zu, und auch die Risiken. Für viele darin liegt der Spaß!

Links

Kommentare

One Response to "Zeiger und Vektoren

  1. Leandro Melo am 21. Mai 2009 21:50

    Nun didaktischen. Ich mochte die "pansexual Zeigern. :)

Lassen Sie eine Antwort




Powered by WP Hashcash