Langage C - pointers_study notes

Cet article est une compréhension préliminaire des pointeurs du langage C et une analyse approfondie des pointeurs du langage C. Bienvenue dans le prochain article Langage C - Compréhension approfondie des pointeurs_Notes d'étude

1. Qu'est-ce qu'un pointeur ?

Concepts de base et propriétés associées

1.1 Définition du pointeur

Le pointeur est le numéro de l'octet dans la mémoire de l'ordinateur et c'est l'adresse de l'octet dans la mémoire de l'ordinateur. En termes simples, le pointeur est l'adresse et l'adresse est le pointeur.

1.2 Taille du pointeur

Comme mentionné précédemment, un pointeur représente une adresse en mémoire et l'adresse mémoire doit correspondre à la longueur du mot machine. Par conséquent, la taille du pointeur est liée à la longueur du mot machine et n'a rien à voir avec le type. Tant qu'il s'agit d'une variable de pointeur, la taille sera la même sous la même plate-forme.

Sur un ordinateur 32 bits, la taille d'un pointeur est de 4 octets (32 bits), tandis que sur un ordinateur 64 bits, la taille d'un pointeur est de 8 octets (64 bits).

1 octet == 8 bits,
32 bits == 4 octets
64 bits == 8 octets

2. Pourquoi utiliser des pointeurs ?

Quelle est l'importance des pointeurs dans l'utilisation du langage C

2.1 La signification des pointeurs

  1. L'importance des pointeurs est d'accéder directement à la mémoire via des adresses pour améliorer l'efficacité du programme.
  2. L'importance des pointeurs est qu'il n'est parfois pas pratique d'accéder directement aux données souhaitées. Dans ce cas, les pointeurs peuvent être utilisés pour un accès indirect.

2.2 Avantages des pointeurs

  1. Les pointeurs offrent de la flexibilité : le langage C utilise des pointeurs pour modifier et transférer facilement l'adresse des variables, ainsi que pour mettre en œuvre une allocation dynamique de mémoire et des opérations flexibles sur les structures de données. Les pointeurs peuvent être utilisés pour faire fonctionner des tableaux, des chaînes, des paramètres de fonction, etc., rendant la logique du programme plus claire et le code plus concis.
  2. Les pointeurs fournissent un accès efficace à la mémoire : grâce aux pointeurs, vous pouvez accéder à l'adresse d'une variable et opérer directement sur la mémoire, améliorant ainsi l'efficacité d'exécution du programme. Dans le même temps, les pointeurs peuvent également être utilisés pour optimiser la gestion de la mémoire, par exemple pour accéder à des tableaux multidimensionnels via des pointeurs vers des pointeurs.

3. Comment utiliser les pointeurs

L’utilisation de pointeurs et les précautions associées

3.1 Opérateurs liés aux pointeurs

Symboles et noms Instructions
& : opérateur d'adresse Utilisé pour obtenir l'adresse mémoire de la variable. Par exemple, int a = 10 ; int *p = &a;, où &a doit obtenir l’adresse mémoire de la variable a.
* : opérateur d'accès au pointeur La valeur utilisée pour accéder à l'adresse mémoire pointée par le pointeur. Par exemple, int b = * p;, où *p accède à la valeur de l'adresse mémoire pointée par le pointeur p et l'affecte à la variable b.
++ : opérateur d'incrémentation de pointeur Il est utilisé pour déplacer le pointeur vers l’arrière de la taille d’un élément. Par exemple, int *p = &a; p++;, où p++ déplace le pointeur p vers l'arrière d'un élément.
- - : opérateur de décrémentation du pointeur Utilisé pour avancer le pointeur d'un élément. Par exemple, int *p = &a; p- -;, où p- - fait avancer le pointeur p d'un élément.
-> : opérateur membre d'accès au pointeur Utilisé pour accéder aux membres d'une structure ou d'une classe via des pointeurs. Par exemple, struct Person { int age; }; struct Person p = { 20 }; int *p_age = &p.age; int age = p_age->age;, où p_age->age accède aux membres de la structure p via le pointeur p_age âge.

3.2 Définition et initialisation des pointeurs

  1. définir une variable de pointeur
<data_type>* <variable_name>;
//*号,左边是指向数据的类型,右边是指针变量的名字

Par exemple:

int* ptr;
//一个指向int 类型的指针变量,名字叫ptr
  1. Initialiser la variable de pointeur
<data_type>* <variable_name> = <address>;
//定义后面用赋值运算符传给他一个地址

Par exemple:

int x = 10;
int* ptr = &x;

Ici, ptr est initialisé à l'adresse de la variable x.

Vous pouvez également initialiser la variable pointeur lors de sa définition :

int x = 10;
int* ptr = &x;

3.3 Opérations sur le pointeur

1. Ajout/soustraction de pointeur

L'ajout de pointeurs peut être utilisé pour calculer la distance relative entre des pointeurs ou pour déplacer des pointeurs vers l'arrière ou vers l'avant d'un certain nombre d'éléments.

#include <stdio.h>

int main() {
    
    
    int arr[5] = {
    
    1, 2, 3, 4, 5};
    int* ptr1 = arr;
    int* ptr2 = ptr1 + 2;

    printf("ptr1: %p\n", ptr1);
    printf("ptr2: %p\n", ptr2);

    printf("Value pointed by ptr1: %d\n", *ptr1);
    printf("Value pointed by ptr2: %d\n", *ptr2);

    return 0;
}

Dans cet exemple, nous définissons un tableau d'entiers arr contenant 5 éléments et initialisons ptr1 à l'adresse du premier élément du tableau. Ensuite, en ajoutant 2 à ptr1, on obtient un nouveau pointeur ptr2, qui pointe vers le troisième élément du tableau. Enfin, nous utilisons la fonction printf pour imprimer les adresses des pointeurs et les valeurs vers lesquelles ils pointent.

Résultat de sortie :

ptr1: 0x7fff5fbff664
ptr2: 0x7fff5fbff668
Value pointed by ptr1: 1
Value pointed by ptr2: 3

On peut voir que l'adresse de ptr2 est 4 de plus que l'adresse de ptr1 (correspondant à la taille de la mémoire de deux entiers), et les valeurs vers lesquelles ils pointent sont respectivement 1 et 3, c'est-à-dire le premier et le troisième éléments du tableau. Cet exemple montre l'utilisation de l'ajout de pointeurs, qui peut être utilisé pour calculer la distance relative entre des pointeurs, ou pour déplacer le pointeur vers l'arrière ou vers l'avant d'un certain nombre d'éléments.

Remarque : Le nombre d'octets ajoutés au pointeur +1 dépend du type de pointeur ! ! Cela ne signifie pas que le pointeur +1 sautera définitivement un octet ! !

2. Auto-incrémentation/auto-décrémentation du pointeur

Les opérations d'incrémentation et de décrémentation du pointeur peuvent être utilisées pour déplacer le pointeur vers l'avant ou vers l'arrière d'un élément.

Par exemple:

#include <stdio.h>

int main() {
    
    
    int arr[5] = {
    
    1, 2, 3, 4, 5};
    int* ptr = arr;

    printf("Value of *ptr: %d\n", *ptr);
    ptr++;
    printf("Value of *ptr after increment: %d\n", *ptr);

    return 0;
}

Dans cet exemple, nous définissons un tableau d'entiers arr contenant 5 éléments et initialisons ptr à l'adresse du premier élément du tableau. Ensuite, nous utilisons l’opération d’incrémentation du pointeur pour déplacer ptr vers l’arrière d’un élément, qui pointe vers le deuxième élément du tableau. Ensuite, nous utilisons la fonction printf pour afficher la valeur pointée par ptr.

Le résultat est le suivant :

Value of *ptr: 1
Value of *ptr after increment: 2

On peut voir que l'opération d'incrémentation du pointeur fait reculer ptr d'un élément et que la valeur pointée par le nouveau pointeur change également.

3.4 pointeur const modifié

Le but des pointeurs modifiés const est de fournir un mécanisme de sécurité pour empêcher la modification des données pointées par le pointeur, et également de limiter l'utilisation des variables de pointeur.

  1. const est situé sur le côté gauche du signe * et modifie le pointeur,
    ce qui signifie que vous ne pouvez pas modifier la valeur de l'objet pointé par le pointeur const. Mais vous pouvez changer le pointeur du pointeur.
const int a = 10;
const int *ptr = &a;

//如果 *ptr=20; 则会报错
//如果 int b = 20;
//     ptr = &b; 则不报错
  1. const est situé sur le côté droit du signe * et modifie le nom de la variable du pointeur
    , indiquant que ce pointeur est un pointeur constant et ne peut pas être attribué à d'autres adresses ni modifier le pointeur du pointeur. Mais vous pouvez modifier la valeur de l'objet pointé par le pointeur.
int a = 10;
int * const ptr = &a;

//如果  *ptr=20; 则a的值变成20
//如果  int b = 20;
//      ptr = &b; 则会报错
  1. La modification const des deux côtés du signe *
    signifie que vous ne pouvez modifier ni le pointeur du pointeur ni la valeur de l'objet pointé par le pointeur.
int a = 10;
const int * const ptr = &a;

//如果  *ptr=20; 会报错
//如果  int b = 20;
//      ptr = &b; 也会报错

3.5 Quelques indicateurs communs

1. Pointeur vers une constante

#include <stdio.h>

int main() {
    
    
    const int k = 10;
    int *p = &k; // 指针p指向常量k

    printf("k = %d\n", k); // 输出k的值
    *p = 20; // 试图通过指针修改常量的值,无效
    printf("k = %d\n", k); // 再次输出k的值,仍然是10

    return 0;
}

Les constantes ne peuvent pas être modifiées et doivent être modifiées avec const.

2. Pointeur vers une variable

#include <stdio.h>

int main() {
    
    
    int x = 10;
    int *p = &x; // 指针p指向变量x

    printf("x = %d\n", x); // 输出x的值
    *p = 20; // 通过指针修改变量的值
    printf("x = %d\n", x); // 再次输出x的值,现在是20

    return 0;
}

Il s'agit également d'un scénario d'utilisation courant des pointeurs, utilisant la variable de pointeur p pour modifier la valeur de la variable x.

3. Pointeur vers le tableau

Les pointeurs vers des tableaux sont généralement utilisés pour manipuler des éléments de tableau, comme parcourir des tableaux, accéder à des éléments de tableau, etc.

Par exemple:

#include <stdio.h>

int main() {
    
    
    int arr[] = {
    
    1, 2, 3, 4, 5};
    int *ptr = arr; // 指向数组的指针

    // 遍历数组并输出元素值
    for (int i = 0; i < 5; i++) {
    
    
        printf("%d ", *(ptr + i));
    }
    printf("\n");

    // 修改数组元素的值
    *(ptr + 2) = 10; // 将数组的第3个元素修改为10
    printf("%d %d %d %d %d\n", arr[0], arr[1], arr[2], arr[3], arr[4]);

    return 0;
}

Dans l'exemple ci-dessus, nous définissons un tableau d'entiers arr, puis définissons un pointeur ptr pointant vers le tableau et l'initialisons à la première adresse du tableau. Grâce au pointeur ptr, on peut accéder directement aux éléments du tableau, par exemple utiliser *(ptr + i) pour accéder au i-ième élément du tableau. En même temps, nous pouvons également modifier la valeur de l'élément du tableau via un pointeur, par exemple, utiliser *(ptr + 2) pour modifier le troisième élément du tableau.

4. Pointeur vers la fonction

Les pointeurs vers des fonctions peuvent être utilisés pour appeler des fonctions ou pour transmettre des fonctions comme arguments.

Par exemple:

#include <stdio.h>

void myFunction(int x) {
    
    
    printf("The value of x is: %d\n", x);
}

int main() {
    
    
    void (*funcPtr)(int); // 定义指向函数的指针
    funcPtr = &myFunction; // 将指针指向myFunction函数
    (*funcPtr)(5); // 通过指针调用函数,输出"The value of x is: 5"
    return 0;
}

PS : Pour obtenir l'adresse d'une fonction, vous pouvez utiliser ptr = & (nom de la fonction) ou ptr = nom de la fonction, car pour les fonctions, & (nom de la fonction) et le nom de la fonction sont tous deux l'adresse de la fonction.

Dans l'exemple ci-dessus, nous avons défini une fonction myFunction qui accepte un paramètre entier et génère la valeur du paramètre. Ensuite, nous définissons un pointeur vers une fonction funcPtr, qui pointe vers la fonction myFunction. Enfin, nous appelons la fonction myFunction via le pointeur funcPtr et définissons la valeur du paramètre sur 5. Le résultat est « La valeur de x est : 5 ».

5. Pointeur vers la structure

Les pointeurs vers des structures sont généralement utilisés pour exploiter des variables de structure, telles que l'accès aux membres de la structure, la modification des variables de structure, etc.

Par exemple:

#include <stdio.h>

struct Person {
    
    
    char name[50];
    int age;
};

int main() {
    
    
    struct Person person1 = {
    
    "Alice", 25};
    struct Person *ptr = &person1; // 指向结构体的指针

    printf("Name: %s\n", ptr->name); // 通过指针访问结构体成员
    printf("Age: %d\n", ptr->age);

    ptr->age = 30; // 通过指针修改结构体成员的值
    printf("Updated Age: %d\n", ptr->age);

    return 0;
}

Dans l'exemple ci-dessus, nous avons défini une structure Person, qui contient un nom de membre de chaîne et un âge de membre entier. Ensuite, nous définissons un pointeur ptr pointant vers la structure Person et l'initialisons à l'adresse de person1. Grâce au pointeur ptr, nous pouvons accéder aux membres de la structure, par exemple, utiliser ptr->name pour accéder au membre name et utiliser ptr->age pour accéder au membre age. Dans le même temps, nous pouvons également modifier la valeur du membre de la structure via un pointeur, par exemple, utiliser ptr->age = 30 pour modifier la valeur du membre age à 30. Enfin, nous générons la valeur mise à jour du membre d'âge.

6. Pointeur vers pointeur (pointeur secondaire)

Un pointeur vers un pointeur est un pointeur vers une variable de pointeur et est généralement utilisé pour manipuler des variables de pointeur, par exemple pour modifier la valeur d'une variable de pointeur.

Par exemple:

#include <stdio.h>

int main() {
    
    
    int a = 10;
    int *p = &a; // 指向变量a的指针
    int **pp = &p; // 指向指针p的指针

    printf("a = %d\n", a);
    printf("*p = %d\n", *p);
    printf("**pp = %d\n", **pp);

    *pp = NULL; // 将指针p设置为NULL
    printf("p = %p\n", p); // 输出NULL

    return 0;
}

Dans l'exemple ci-dessus, nous définissons une variable entière a, puis définissons un pointeur p pointant vers a et un pointeur pp pointant vers le pointeur p. Grâce à pp, nous pouvons accéder à la valeur de p, par exemple, utilisez **pp pour accéder à la valeur de a. Dans le même temps, nous pouvons également modifier la valeur de p à pp, par exemple, utiliser *pp = NULL pour définir p sur NULL. Enfin, nous affichons la valeur de p et le résultat est NULL.

3.6 Points à noter lors de l'utilisation de pointeurs

  1. Les variables de pointeur doivent être initialisées en premier, sinon la variable de pointeur ne pointe vers aucune adresse mémoire valide, ce qui entraîne un comportement indéfini.
  2. L'adresse mémoire pointée par la variable pointeur doit être légale et ne peut pas pointer vers une mémoire qui a été libérée ou une adresse mémoire invalide, sinon un comportement indéfini en résultera.
  3. Une variable pointeur ne peut pas pointer vers une adresse mémoire qui n’a pas de valeur de ce type, sinon un comportement indéfini en résultera.
  4. Les variables de pointeur ne peuvent pas effectuer d'opérations arithmétiques, par exemple, deux variables de pointeur ne peuvent pas être ajoutées, sinon un comportement indéfini en résultera.
  5. Les variables de pointeur peuvent pointer vers des éléments du tableau, mais elles ne peuvent pas s'étendre sur les adresses mémoire entre les éléments du tableau, sinon un comportement indéfini en résultera.
  6. Les variables de pointeur peuvent pointer vers des variables de pointeur, mais vous devez accorder une attention particulière au problème des chaînes de pointeurs. N'accédez pas ou ne modifiez pas la valeur de la chaîne de pointeurs en dehors des limites, sinon cela entraînerait un comportement indéfini.
  7. Lorsque vous utilisez des variables de pointeur, vous devez faire attention à éviter l'accès au pointeur nul et les problèmes de pointeur sauvage, sinon un comportement indéfini en résulterait.
  8. Lorsque vous utilisez des variables de pointeur, vous devez faire attention aux fuites de mémoire. N'oubliez pas de libérer la mémoire allouée, sinon des fuites de mémoire se produiront.

Lors de l'utilisation de pointeurs en langage C, une attention particulière doit être accordée à la gestion de la mémoire et aux opérations du pointeur pour garantir la légalité et l'exactitude des variables du pointeur.

4. pointeur vide*

Le pointeur void* est un type spécial de pointeur dans le langage C. Il représente un pointeur vers un type de données inconnu. En d’autres termes, les pointeurs void* peuvent pointer vers n’importe quel type de données.

Mais il ne peut pas être déréférencé directement car il ne spécifie pas de type spécifique. Il est généralement utilisé comme paramètre ou valeur de retour d'une fonction pour gérer des types arbitraires de données à l'intérieur de la fonction.

Par exemple:

#include <stdio.h>

void print_int(void* ptr) {
    
    
    int value = *((int*) ptr);
    printf("%d\n", value);
}

int main() {
    
    
    int x = 42;
    print_int(&x);
    return 0;
}

Dans cet exemple, la fonction print_int() accepte un pointeur void* comme argument, le convertit en pointeur int*, puis le déréférence pour obtenir la valeur entière et l'imprime.

PS (note) : les pointeurs void* ne peuvent pas être directement déréférencés et doivent être forcés à taper.

Pour une analyse approfondie des pointeurs du langage C, bienvenue dans le prochain article Langage C - Compréhension approfondie des pointeurs_Study Notes

Acho que você gosta

Origin blog.csdn.net/yjagks/article/details/132645515
Recomendado
Clasificación