Pointers and Arrays

18 mai 2009 · Posted in C / C + +

Dans le premier message de cette série, nous parler un peu de pointeurs. Dans le second, on parle de références. Aujourd'hui nous allons nous concentrer sur la relation intime (ui!) Entre les pointeurs et les tableaux (ou tableaux).

Rappelant Arrays

Un tableau ou un tableau, ou un arrangement, il est une abstraction mathématique utilisée pour représenter un ensemble de données homogènes, c'est à dire du même type (int, float, etc.) Cette abstraction est organisée sous forme de tableau, avec des rangées et des colonnes. Chaque élément du tableau a pour coordonnées uniquement (lignes et colonnes), de sorte qu'un élément de donnée E (i, j) représente le seul élément de la ligne "i", "la colonne j.

La syntaxe pour les tableaux déclarant est la suivante:

  / / Un des tableaux de dimension ou de vecteurs
 10 ] ; // 10 elementos, do 0 ao 9 int FPI [10] / / 10 éléments de 0 à 9
 23 ] ; // 23 elementos, do 0 ao 22 char cvet [23] / / 23 éléments, de 0 à 22

 / / Deux-matrices dimensionnelles
 2 ] [ 3 ] ; // 2 linhas (0 a 1) e 3 colunas (0 a 2) int imat [2] [3] / / 2 lignes (0 à 1) et 3 colonnes (0 à 2)
 10 ] [ 2 ] ; // 10 linhas (0 a 9) e 2 colunas (0 a 1) DMAT double [10] [2] / / 10 lignes (0 à 9) et 2 colonnes (de 0 à 1) 

Chaque élément du tableau est indépendant des autres et peut être consulté que la syntaxe suivante:

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

 / / Changer le quatrième élément de la formation professionnelle initiale vecteur.
 / / N'oubliez pas que commence à courir à partir de l'élément zéro
 ] = 13 ; ivet [3] = 13;

 / / Lecture du deuxième élément du vecteur ivet
 ivet [ 1 ] ; int num = FPI [1];

 / / Modification de l'élément de la première rangée, deuxième colonne imat
 ] [ 1 ] = 42 ; imat [0] [1] = 42;

 / / Lecture de l'élément dans la troisième rangée, quatrième colonne imat
 imat [ 2 ] [ 3 ] ; int foo = imat [2] [3]; 

A ce poste, nous n'allons pas discuter de la matrice de fond, nous étudions uniquement la relation entre les tableaux et les pointeurs dans un assez intuitif.

Les tailles des tableaux

Comme un tableau est une abstraction qui contient plusieurs valeurs du même type, quelle est sa taille? Quel est l'espace qu'il occupe dans la mémoire?

Considérons le code ci-dessous:

 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 
  10 ] ; ivet int [10];
 13 ] ; cvet char [13];
 20 ] ; double 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 (FPI) =" <<sizeof (FPI) <<endl;
 "sizeof(cvet)    = " << sizeof ( cvet ) << endl ; cout << "sizeof (cvet) =" <<sizeof (cvet) <<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; 

Le résultat est assez raisonnable. L'espace occupé par un tableau est égal au nombre d'éléments multiplié par la taille du type de l'élément (lignes colonnes x x sizeof (type)). Toutefois, si chaque élément est indépendant, il est supposé que chacune occupe une place à part dans la mémoire, sinon on va écraser un autre élément. Alors, est-ce que chaque élément a sa propre adresse mémoire?

Les tableaux et les adresses mémoire

Pour plus de commodité, nous allons ensuite analyser les adresses possibles d'un tableau de caractères, dont la taille est seulement donné un octet 1:

  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');

 / / Afficher le contenu, les valeurs et les adresses des données.
 "Índice \t Valor \t Endereço do elemento \n " ) ; printf ( "Index \ t Valeur \ address élément t \ 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 \% p \ t \ n", i, cvet [i], & cvet [i]);
 )

 / / Impression de l'adresse du tableau
 "Endereço da matriz: %p \n " , & cvet ) ; printf ( "Adresse du tableau:% p \ n", & cvet);

 / / Printing l'adresse du tableau à nouveau
 "Endereço da matriz: %p \n " , cvet ) ; printf ( "Adresse du tableau:% p \ n", cvet); 

Les adresses des éléments sont séquentiels, c'est à dire, chaque élément est stocké à côté du premier. Outre, deux autres faits intéressants:

  1. L'adresse de l'array (& cvet), illustré à la ligne 25, est le même que le premier élément du tableau;
  2. Le cvet très variable peut être interprété comme un pointeur, comme le montre la ligne 28;

En C + +, un tableau commun est un bloc contigu de mémoire dont le nom peut être interprété (CAST) comme un pointeur qui pointe vers son premier élément. En outre, il est valide pour faire un point pointeur sur un tableau où le pointeur vers la destination est du même type que le type d'éléments du tableau. Pendant atrubuição un tableau à un pointeur, le compilateur fait une conversions de type implicites. Le pointeur de destination doit être interprété comme un pointeur vers la zone de mémoire occupée par le tableau.

L'une des conséquences pas si évident, c'est que pendant le casting implicite, est perdu les informations que la zone était un tableau de mémoire. Ainsi est perdu les informations de la taille du tableau. Du point de vue de l'aiguille, elle pointe vers le début d'un bloc d'arbitraire de la mémoire, une taille trop arbitraire. Départ d'un tableau et aller vers un pointeur, c'est aller à partir d'une abstraction plus restrictive et plus haut niveau d'abstraction dans une mesure moins restrictive et de niveau inférieur.

D'autre part tenter d'assigner un pointeur vers un tableau génère une erreur de compilation pour les types incompatibles. Un tableau est un bloc de mémoire de données (n octets), comme un pointeur a une seule entrée, une adresse. Le compilateur ne peut pas savoir à l'avance si un pointeur vers une zone de 1, 2 ou 200 octets.

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

 " \n Antes da atribuição \n " ) ; n printf ( "\ préalablement à la cession \ n");
 "cvet = %p \n " , cvet ) ; printf ( "= p cvet% \ n", cvet);
 "pc   = %p \n " , pc ) ; printf ( "PC =% p \ n", pc);
 "sizeof(cvet) = %lu \n " , sizeof ( cvet ) ) ; printf ( "sizeof (cvet) =% lu \ n", sizeof (cvet));
 "sizeof(pc)   = %lu \n " , sizeof ( pc ) ) ; printf ( "sizeof (PC) =% lu \ n", sizeof (pc));

 pc = cvet;

 " \n Depois da atribuição \n " ) ; n printf ( "\ Après l'affectation \ n");
 "cvet = %p \n " , cvet ) ; printf ( "= p cvet% \ n", cvet);
 "pc   = %p \n " , pc ) ; printf ( "PC =% p \ n", pc);
 "sizeof(cvet) = %lu \n " , sizeof ( cvet ) ) ; printf ( "sizeof (cvet) =% lu \ n", sizeof (cvet));
 "sizeof(pc)   = %lu \n " , sizeof ( pc ) ) ; printf ( "sizeof (PC) =% lu \ n", sizeof (pc)); 

Notez que préalablement à la cession (ligne 25), le PC pointeur. est nulle, de sorte qu'il a été initialisé. Puisque la taille indiquent que le tableau a 300 octets et le pointeur de seulement 8 (ma machine est un AMD 64). Après la cession de deux passent le «point» à un même espace de mémoire, mais leur taille ne change pas. Il y avait un casting implicite de char [300] vers char *, et le pointeur dans la mesure où le jeu PC ne peut pas savoir la taille de la zone mémoire dans laquelle il pointe. Depuis le tableau continue cvet sachant exactement ce qu'il est, sans aucune crise existentielle.

L'arithmétique des pointeurs - Vulgaire, et alors?

Pourquoi, si je sais que les données dans un tableau sont disposées côte à côte, je peux utiliser un pointeur qui saute à l'adresse suivante et accéder à l'élément suivant. Le nom de ceci est l'arithmétique des pointeurs.

  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");


 / / Maintenant, l'ensemble
 max ] = { 1 , 2 , 3 , 4 , 5 , 6 } ; ivet int [max] = (1, 2, 3, 4, 5, 6);
 pi = ivet ; int * pi = FPI;

 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");

 / / Dans deux dimensions
 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 (int j = 0, j <3 j + +) (
         "%c" , * ( ppc + 3 * i + j ) ) ; printf ( "% c", * (PPC + 3 * i + j));
     )
     " \n " ) ; printf ( "\ n");
 )
 " \n " ) ; printf ( "\ n"); 

Dans la ligne 18 le PC pointeur est maintenant pointer vers le cvet tableau et donc pour son premier élément, le caractère 'B'. À la ligne 21 le contenu du PC, qui est l'adresse où il était stocké avec le 'B', ie est incrémenté puis déréférencé. Dans la première semaine, la valeur de i est nul, de sorte qu'il déréférencement à la valeur 'B'. Dans la suite, l'adresse suivante est de renvoyer à d'autres caractères stockés dans le tableau original. Il est plus ou moins ce que le compilateur ne l'interne lorsque vous utilisez la syntaxe cvet [i]. L'abstraction du tableau vous donne un moyen plus convivial de traiter les zones contiguës de mémoire * (pc + i).

Mais si l'abstraction de tableau est simple à utiliser que l'arithmétique des pointeurs?

Une des réponses est la ligne 26. Elle fait la même chose que la ligne 21, mais un peu plus vite. Dans la syntaxe du 21 en ligne, ou même, la syntaxe de tableau, l'accès à l'une de l'information peut être résumée dans un très rugueuse dans les commandes:

  1. Prendre l'adresse de base du tableau;
  2. Ajouter à l'adresse de la valeur de l'indice;
  3. De-référençant la nouvelle adresse;

Déjà avec l'arithmétique des pointeurs ressemble à ceci:

  1. De-référence à cette adresse;

L'incrément de commande (ou réalisés en matière de traite) ne comptent pas parce qu'elle fait partie de la boucle, même si i + +; est plus rapide que a = b + c;. Maintenant, imaginez ce petit gain de 66% appliquée à une zone de données de 1 MB. Il n'y aura plus de 2 millions de commandes de moins!

La technique d'utilisation d'un pointeur pour gérer une zone arbitraire de la mémoire est généralement utilisé dans la programmation de bas niveau (plus proche de la machine), la manipulation des tampons et les cordes, entre autres trucs sales. Dans les entrailles des ordinateurs, des opérations que les zones de balayer beaucoup de mémoire, sont souvent effectuées avec l'arithmétique des pointeurs. A ce niveau, Darwin règne en maître et seul le prêt survivre. De là, la langue commence à donner un pouvoir que seuls les cœurs purs peuvent comprendre.

Une remarque importante est celle entre les lignes 31 et 38 de l'expérience est répétée avec des nombres entiers. Notez que comme les entiers sont de 4 octets, les augmentations sont automatiquement mises à 4 de 4 octets, et non 1 à 1, à savoir l'augmentation est calculé automatiquement pour sizeof (type). Améliorer un pointeur moyens d'accéder à la zone suivante de la mémoire semblables à des données réelles, pas seulement l'adresse suivante. Comme la taille d'un char occupe un octet, quand incremetamos un pointeur vers un char, se déplacer seulement 1 octet. Si la poursuite d'un pointeur sur un double, nous passerons 8 octets, et ainsi de suite.

Une autre observation est que un tableau à deux dimensions peut être "linéarisée" comme indiqué dans lignes 40 à 52. Ceci est utile, le cas échéant, de faire un meilleur usage de cache du processeur, par exemple.

void *, les pointeurs pan-sexuelle

Plus tôt, j'ai dit que c'était seulement possible d'assigner un tableau à un pointeur qui était pour le même type que les données du tableau. Je mentir sans vergogne! La raison en est que, pour quelqu'un qui a renoncé au poste avant ce sujet, il est plus sûr de croire qu'il peut :) !

Il existe deux exceptions à la règle. La première est quand il ya une conversion explicite de types et de cibler le pointeur "pense" qui pointe vers le bon type. Un exemple est à la ligne 44 du code précédent.

Le second est le cas des pointeurs vers nulle. Un pointeur vers le vide est un pointeur qui ne demande rien sur le type de données qui est dans la zone de mémoire pour lequel il pointe. Il est un pointeur pra un domaine général et de la mémoire, quelque chose de très bas niveau.

Afin d'utiliser toute l'information pointée par un pointeur nul avant de-référence, elle, vous devez effectuer une conversion explicite à un type valide, il est un int * est déréférencé en int et char * est déréférencé à char, devinez quoi, il est déréférencé le vide *?

  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;

 / / Erreur de compilation.
 / / * PV
 / / Quel est le montant 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"); 

Pointeur vers vide sont utilisés lorsque l'on doit pointer vers une zone générale de la mémoire sans avoir le contrôle / la connaissance du type de données que contient ce domaine, ou les fonctions qui ne peuvent pas faire des hypothèses sur les types de ses paramètres, tels que le pthreads lib API (lien arbitraire).

Fermeture

Plus nous plonger dans les sujets sur les pointeurs, à côté de la machine que nous. Une grande partie de la puissance de C et C + + vient de là, et beaucoup de problèmes. La complexité est croissante et aussi les risques. Pour beaucoup c'est là que réside le plaisir!

Liens

Commentaires

One Response to "Pointers and Arrays

  1. Leandro Melo, le 21 Mai, 2009 21:50

    Eh bien didactique. J'ai aimé les «pointeurs pan-sexuelle. :)

Laissez une réponse




Powered by WP Hashcash