Recherche…
Introduction
Un pointeur est une adresse faisant référence à un emplacement en mémoire. Ils sont couramment utilisés pour permettre aux fonctions ou aux structures de données de connaître et de modifier la mémoire sans avoir à copier la mémoire mentionnée. Les pointeurs sont utilisables avec les types primitifs (intégrés) ou définis par l'utilisateur.
Les pointeurs utilisent les opérateurs "dereference" *
, "address of" &
, "arrow" ->
. Les opérateurs '*' et '->' sont utilisés pour accéder à la mémoire pointée, et l'opérateur &
est utilisé pour obtenir une adresse en mémoire.
Syntaxe
- <Type de données> * <Nom de la variable>;
- <Type de données> * <Nom de la variable> = & <Nom de la variable du même type de données>;
- <Type de données> * <Nom de la variable> = <Valeur du même type de données>;
- int * foo; // Un pointeur qui pointe vers un nombre entier
- int * bar = & myIntVar;
- long * bar [2];
- long * bar [] = {& myLongVar1, & myLongVar2}; // Égal à: long * bar [2]
Remarques
Soyez conscient des problèmes lors de la déclaration de plusieurs pointeurs sur la même ligne.
int* a, b, c; //Only a is a pointer, the others are regular ints.
int* a, *b, *c; //These are three pointers!
int *foo[2]; //Both *foo[0] and *foo[1] are pointers.
Notions de base sur les pointeurs
Note: dans tout ce qui suit, l'existence de la constante C ++ 11 nullptr
est supposée. Pour les versions antérieures, remplacez nullptr
par NULL
, la constante utilisée pour jouer un rôle similaire.
Créer une variable de pointeur
Une variable de pointeur peut être créée en utilisant la syntaxe spécifique *
, par exemple int *pointer_to_int;
.
Lorsqu'une variable est de type pointeur ( int *
), elle ne contient qu'une adresse mémoire. L'adresse mémoire est l'emplacement auquel sont stockées les données du type sous - jacent ( int
).
La différence est claire lorsque l'on compare la taille d'une variable avec la taille d'un pointeur au même type:
// Declare a struct type `big_struct` that contains
// three long long ints.
typedef struct {
long long int foo1;
long long int foo2;
long long int foo3;
} big_struct;
// Create a variable `bar` of type `big_struct`
big_struct bar;
// Create a variable `p_bar` of type `pointer to big_struct`.
// Initialize it to `nullptr` (a null pointer).
big_struct *p_bar0 = nullptr;
// Print the size of `bar`
std::cout << "sizeof(bar) = " << sizeof(bar) << std::endl;
// Print the size of `p_bar`.
std::cout << "sizeof(p_bar0) = " << sizeof(p_bar0) << std::endl;
/* Produces:
sizeof(bar) = 24
sizeof(p_bar0) = 8
*/
Prendre l'adresse d'une autre variable
Les pointeurs peuvent être assignés entre eux simplement comme des variables normales; dans ce cas, c'est l' adresse mémoire qui est copiée d'un pointeur à un autre, et non les données réelles sur lesquelles pointe un pointeur.
De plus, ils peuvent prendre la valeur nullptr
qui représente un emplacement de mémoire nul. Un pointeur égal à nullptr
contient un emplacement de mémoire non valide et ne fait donc pas référence à des données valides.
Vous pouvez obtenir l'adresse mémoire d'une variable d'un type donné en préfixant la variable avec l' adresse de l' opérateur &
. La valeur renvoyée par &
est un pointeur sur le type sous-jacent qui contient l'adresse mémoire de la variable (qui sont des données valides tant que la variable ne sort pas du cadre ).
// Copy `p_bar0` into `p_bar_1`.
big_struct *p_bar1 = p_bar0;
// Take the address of `bar` into `p_bar_2`
big_struct *p_bar2 = &bar;
// p_bar1 is now nullptr, p_bar2 is &bar.
p_bar0 = p_bar2;
// p_bar0 is now &bar.
p_bar2 = nullptr;
// p_bar0 == &bar
// p_bar1 == nullptr
// p_bar2 == nullptr
Contrairement aux références:
- l'affectation de deux pointeurs n'écrase pas la mémoire à laquelle se réfère le pointeur assigné;
- les pointeurs peuvent être nuls.
- l' adresse de l' opérateur est requise explicitement.
Accéder au contenu d'un pointeur
Comme prendre une adresse nécessite &
, aussi bien accéder au contenu nécessite l'utilisation de l' opérateur de déréférencement *
, comme préfixe. Lorsqu'un pointeur est déréférencé, il devient une variable du type sous-jacent (en fait, une référence à celui-ci). Il peut alors être lu et modifié, sinon const
.
(*p_bar0).foo1 = 5;
// `p_bar0` points to `bar`. This prints 5.
std::cout << "bar.foo1 = " << bar.foo1 << std::endl;
// Assign the value pointed to by `p_bar0` to `baz`.
big_struct baz;
baz = *p_bar0;
// Now `baz` contains a copy of the data pointed to by `p_bar0`.
// Indeed, it contains a copy of `bar`.
// Prints 5 as well
std::cout << "baz.foo1 = " << baz.foo1 << std::endl;
La combinaison de *
et de l'opérateur .
est abrégé par ->
:
std::cout << "bar.foo1 = " << (*p_bar0).foo1 << std::endl; // Prints 5
std::cout << "bar.foo1 = " << p_bar0->foo1 << std::endl; // Prints 5
Déréférencer les pointeurs invalides
Lors du déréférencement d'un pointeur, vous devez vous assurer qu'il pointe vers des données valides. Le fait de déréférencer un pointeur non valide (ou un pointeur nul) peut entraîner une violation de l'accès à la mémoire ou lire ou écrire des données erronées.
big_struct *never_do_this() {
// This is a local variable. Outside `never_do_this` it doesn't exist.
big_struct retval;
retval.foo1 = 11;
// This returns the address of `retval`.
return &retval;
// `retval` is destroyed and any code using the value returned
// by `never_do_this` has a pointer to a memory location that
// contains garbage data (or is inaccessible).
}
Dans un tel scénario, g++
et clang++
émettent correctement les avertissements:
(Clang) warning: address of stack memory associated with local variable 'retval' returned [-Wreturn-stack-address]
(Gcc) warning: address of local variable ‘retval’ returned [-Wreturn-local-addr]
Par conséquent, il faut faire attention lorsque les pointeurs sont des arguments de fonctions, car ils peuvent être nuls:
void naive_code(big_struct *ptr_big_struct) {
// ... some code which doesn't check if `ptr_big_struct` is valid.
ptr_big_struct->foo1 = 12;
}
// Segmentation fault.
naive_code(nullptr);
Opérations de pointeur
Il existe deux opérateurs pour les pointeurs: Adresse-de l'opérateur (&): Retourne l'adresse mémoire de son opérande. Opérateur Contents-of (Dereference) (*): Renvoie la valeur de la variable située à l'adresse spécifiée par son opérateur.
int var = 20;
int *ptr;
ptr = &var;
cout << var << endl;
//Outputs 20 (The value of var)
cout << ptr << endl;
//Outputs 0x234f119 (var's memory location)
cout << *ptr << endl;
//Outputs 20(The value of the variable stored in the pointer ptr
L'astérisque (*) est utilisé pour déclarer un pointeur dans le seul but d'indiquer qu'il s'agit d'un pointeur. Ne confondez pas cela avec l'opérateur de déréférence , qui est utilisé pour obtenir la valeur située à l'adresse spécifiée. Ce sont simplement deux choses différentes représentées avec le même signe.
Arithmétique du pointeur
Incrémenter / Décrémenter
Un pointeur peut être incrémenté ou décrémenté (préfixe et postfix). L'incrémentation d'un pointeur fait avancer la valeur du pointeur vers l'élément du tableau un élément au-delà de l'élément actuellement pointé. Décrémenter un pointeur le déplace vers l'élément précédent du tableau.
L'arithmétique du pointeur n'est pas autorisée si le type sur lequel pointe le pointeur n'est pas complet. void
est toujours un type incomplet.
char* str = new char[10]; // str = 0x010
++str; // str = 0x011 in this case sizeof(char) = 1 byte
int* arr = new int[10]; // arr = 0x00100
++arr; // arr = 0x00104 if sizeof(int) = 4 bytes
void* ptr = (void*)new char[10];
++ptr; // void is incomplete.
Si un pointeur sur l'élément final est incrémenté, le pointeur pointe sur un élément situé après la fin du tableau. Un tel pointeur ne peut pas être déréférencé, mais il peut être décrémenté.
L'incrémentation d'un pointeur sur l'élément one-past-the-end du tableau ou la décrémentation d'un pointeur sur le premier élément d'un tableau entraîne un comportement indéfini.
Un pointeur vers un objet non-tableau peut être traité, à des fins d'arithmétique de pointeur, comme s'il s'agissait d'un tableau de taille 1.
Addition soustraction
Les valeurs entières peuvent être ajoutées aux pointeurs. ils agissent comme incrémentation, mais par un nombre spécifique plutôt que par 1. Les valeurs entières peuvent également être soustraites des pointeurs, agissant comme une décrémentation du pointeur. Comme pour l'incrémentation / décrémentation, le pointeur doit pointer vers un type complet.
char* str = new char[10]; // str = 0x010
str += 2; // str = 0x010 + 2 * sizeof(char) = 0x012
int* arr = new int[10]; // arr = 0x100
arr += 2; // arr = 0x100 + 2 * sizeof(int) = 0x108, assuming sizeof(int) == 4.
Différence de pointeur
La différence entre deux pointeurs sur le même type peut être calculée. Les deux pointeurs doivent être dans le même objet tableau; Résultats de comportement autrement indéfinis.
Étant donné deux pointeurs P
et Q
dans le même tableau, si P
est le i
ème élément du tableau, et Q
est le j
e élément, alors P - Q
est i - j
. Le type du résultat est std::ptrdiff_t
, de <cstddef>
.
char* start = new char[10]; // str = 0x010
char* test = &start[5];
std::ptrdiff_t diff = test - start; //Equal to 5.
std::ptrdiff_t diff = start - test; //Equal to -5; ptrdiff_t is signed.