Comment aborder les pointeurs en C?

Je suis très nouveau en C et j’ai quelques difficultés à apprendre sur les pointeurs. J’ai expérimenté la permutation et c’est tout ce que je peux faire avec elles 🙂 Je sais que chaque variable a sa propre adresse dans les cellules de mémoire (c’est ce que mon conférencier nous a dit) et la valeur de chaque variable peut être obtenue en allant à l’adresse associée récupérer la valeur qui y est stockée. J’ai vu des en-têtes de fonctions tels que:

int doSomething(char **hihi); 

ma tête est tellement confuse. Je sais que le pointeur est également une variable et qu’il stocke uniquement les informations d’adresse dans sa cellule de mémoire. J’ai lu qu’ils sont étroitement liés aux tableaux

 arr = &arr[0]; 

C’est tout ce que je sais sur les pointeurs et je me demande comment je peux approfondir ma vision des pointeurs. J’ai cherché sur le net et je n’ai trouvé aucun aide-mémoire utile couvrant les pointeurs. Et je veux aussi savoir pourquoi ils sont si importants et est-il possible de comprendre ce qui se passe sans utiliser printf() pour imprimer leurs adresses ( p ) et leurs valeurs ( \*p )?

Imprimer les adresses et les valeurs est un moyen raisonnable de les regarder. Mais si vous pouvez mettre en place un débogueur, c’est bien mieux, car vous pouvez suivre les pointeurs plus rapidement, les regarder changer au fur et à mesure, et ainsi de suite.

Si vous êtes familier avec les “raccourcis” dans Windows ou les liens symboliques dans les systèmes de fichiers linux, il peut être utile, tout comme de commencer, de penser à un pointeur comme un raccourci (lien symbolique) vers un autre object est une structure, un type intégré, un autre pointeur, etc.).

Un raccourci est toujours un fichier. Il occupe son propre espace sur le lecteur de disque, il fait référence à un autre fichier et il peut être modifié pour faire référence à un fichier différent de celui utilisé auparavant. De même, un pointeur en C est un object qui occupe de la mémoire, contient l’adresse d’un autre emplacement de mémoire et peut être modifié pour contenir une adresse différente simplement en l’affectant.

Une différence est que si vous double-cliquez sur un raccourci, il se comporte comme si vous double-cliquiez sur la chose sur laquelle il pointe. Ce n’est pas le cas avec les pointeurs – vous devez toujours déréférencer explicitement un pointeur avec “*” ou “->” afin d’accéder à la chose sur laquelle il pointe. Une autre différence est qu’il est assez courant d’avoir des pointeurs qui pointent vers quelque chose en C.

En ce qui concerne le jargon, il suffit de l’apprendre malheureusement. “int doSomething (char ** hihi)” signifie “une fonction appelée doSomething, qui renvoie un entier et prend en paramètre un pointeur pour pointer un caractère”. Le point crucial est que ” char ** hihi ” signifie “pointeur à pointeur à caractère. Nous appellerons pointeur à pointeur à caractère hihi”. Vous dites que le “type” de hihi est char **, et que le “type” de * hihi (ce que vous obtenez lorsque vous déréférenciez le pointeur) est char *, et le type de ** hihi est char.

Fréquemment en C, un pointeur sur un caractère signifie une chaîne (en d’autres termes, il s’agit d’un pointeur sur le premier caractère d’un tableau à terminaison NUL). Si souvent “char *” signifie “chaîne”, mais ce n’est pas obligatoire. Cela pourrait simplement signifier un pointeur sur un caractère. Un peu comme un raccourci vers un fichier de 1 octet dans Windows (enfin, avec FAT32 quand même), un pointeur sur un caractère en C est en réalité plus gros que ce à quoi il pointe 🙂

De même, un caractère ** signifie souvent non seulement un pointeur sur un pointeur de chaîne, mais sur un tableau de pointeurs de chaîne. Ce n’est peut-être pas le cas, mais si c’est le cas, la petite image suivante pourrait vous aider:

 hihi
  ____ ____ ________ _________ _______
 | ____ |  -----> | ____ |  * hihi ---> | ___ A ____ |  | ___ B _____ |  | ___ C ___ |
                 | ____ |  * (hihi + 1) ------------------ ^ ^
                 | ____ |  * (hihi + 2) --------------------------------- |
                 |  ... |  etc.

hihi pointe vers la tour, ce qui est ma façon de représenter un tableau de pointeurs. Comme vous l’avez déjà noté, j’aurais pu écrire hihi [0] à la place de * hihi, hihi [1] à la place de * (hihi + 1), etc.

Il s’agit d’un bloc de mémoire contigu et chaque bloc de la taille d’un pointeur contient l’adresse (c’est-à-dire qu’il “pointe vers”) un autre bloc de mémoire, qui contient bien un ou plusieurs caractères. Ainsi, hihi [0] est l’adresse du premier caractère de la chaîne A, hihi [1] est l’adresse du premier caractère de la chaîne B.

Si hihi ne pointe pas sur un tableau, mais sur un seul pointeur, la tour est un bungalow. De même, si * hihi ne pointe pas sur une chaîne, mais sur un seul caractère, le bloc long et mince est un carré. Vous pourriez demander: “Comment puis-je savoir combien d’étages la tour a?”. C’est un gros problème en programmation C: en général, soit la documentation de la fonction vous l’indique (elle peut indiquer «1», ou «12», ou «suffisamment pour ce que vous me demandez de faire», ou bien vous passez le nombre supplémentaire d’étages en tant que paramètre supplémentaire, sinon la documentation vous dira que le tableau est “NULL terminé”, ce qui signifie qu’il continuera à lire jusqu’à ce qu’il voit l’adresse / la valeur NULL, puis s’arrête. La fonction principale effectue les deux deuxième et troisième chose – argc contient le nombre d’arguments, et pour être sûr, argv est terminé par NULL.

Ainsi, chaque fois que vous voyez un paramètre de pointeur, vous devez consulter la documentation de la fonction pour savoir si elle attend un pointeur sur un tableau et, dans l’affirmative, quelle doit être sa taille. Si vous ne faites pas attention à cela, vous allez créer une sorte de bogue appelé “buffer overflow”, dans lequel une fonction attend un pointeur sur un grand tableau, vous lui atsortingbuez un pointeur sur un petit tableau et gribouillant la fin. de ce que vous avez donné et commence à corrompre la mémoire.

Je pense que c’est là que les livres classiques sont plus utiles que la plupart des ressources en ligne. Si vous pouvez en obtenir une copie, lisez très attentivement le langage de programmation C (aussi appelé K & R). Si vous voulez en savoir plus, optez pour la Programmation Expert C: Deep Secrets (google it).

Un pointeur est un lieu.

Un tableau est un groupe consécutif de lieux.

Il y a toujours une valeur à un endroit. (Il peut s’agir de résidus indésirables).

Chaque variable a sa place.

Pour les variables de pointeur, la valeur à sa place est une place .

C’est comme une chasse au trésor. “Dans la boîte aux lettres 13, recherchez une note qui vous indique quelle boîte aux lettres contient votre carte d’anniversaire.”

Et si la boîte aux lettres 13 contient une note indiquant “13”, votre carte d’anniversaire sera longue à venir! (C’est une erreur causée par une référence de pointeur circulaire. 😉

Bien que lire K & R soit le meilleur choix ici, je vais essayer de le clarifier un peu:

Un pointeur lui-même est une variable. Mais au lieu de stocker une valeur, il ne stocke qu’une adresse. Pensez-y comme à un index: comme dans votre carnet d’adresses, l’index de quelque chose que vous recherchez (par exemple, le numéro de téléphone d’un nom), pointe vers l’endroit où les informations sont stockées. Dans votre carnet d’adresses, vous pouvez lire “voir à la page 23 le numéro de téléphone de Joe”. Dans le cas du pointeur, il est simplement indiqué “regardez l’adresse de mémoire 1234 pour récupérer les informations que je pointe”. Comme la valeur du pointeur lui-même n’est qu’une adresse mémoire, vous pouvez en faire une arithmétique – comme pour append des valeurs (ce qui revient à accéder aux éléments d’un tableau: si le pointeur pointe sur un tableau, l’adresse qui suit celle du pointeur pointe sur accéder au prochain élément du tableau). Votre exemple de fonction int doSomething(char *hihi) aura hihi pointant vers l’adresse mémoire que vous avez transmise lors de son appel. Ceci est utile si vous souhaitez transmettre de plus grandes quantités de données. Au lieu de les copier (ce qui se produit dans une fonction comme void blah(int a) avec la valeur a), vous ne copiez que son emplacement.

J’ai omis quelques détails dans ce qui précède, mais j’espère que cela vous donnera au moins une compréhension de base. Je suggère fortement de lire K & R ou un livre similaire sur le sujet.

 /* Given a ssortingng of characters like this one: */ char *ssortingng = "Hello!\n"; /* Memory will contain something like: 0x00100 'H' 0x00101 'e' 0x00102 'l' 0x00103 'l' 0x00104 'o' 0x00105 '!' 0x00106 '\n' 0x00107 '\0' */ /* And the program code will say: */ ssortingng=0x00100; /* C doesn't really have arrays */ char c=ssortingng[3]; /* is just syntactic sugar for: */ char c=*((char*)((void*)ssortingng + 3 * sizeof(char))); /* ie. 0x00100 + 3 * 1 */ /* ie. 0x00103 */ /* and * dereferences 0x00103, this means char_in(0x00103) */ /* When you pass a pointer you are actually passing the value of the memory position */ int a; /* allocates space for a random four byte value in 0x00108 */ scanf("%d",&a); /* &a = 0x00108 scanf now knows that it has to store the value in 0x0108 */ /* Even if you declare: */ int b[23]; /* b will be just a pointer to the beginning of 23 allocated ints. ie. 0x0010C */ /* pointer arithmetic is different from normal types in that: */ b++; /* is actually: */ b+=4; /* b+=1*sizeof(int); */ /* So pointers in C work more like arrays */ 

Davos, es-tu au collège? Êtes-vous un majeur en informatique? Une chose que vous pourriez envisager est de prendre un cours d’assemblage (MIPS, x86). J’étais un étudiant en génie élecsortingque et je devais suivre ce type de cours de bas niveau. Une chose que j’ai constatée est qu’avoir une compréhension claire du langage d’assemblage m’a vraiment aidé lorsque j’ai commencé à apprendre le C ++. En particulier, cela m’a permis de mieux comprendre les indicateurs.

Les pointeurs et le déréférencement sont des concepts fondamentaux au niveau du langage d’assemblage. Si vous apprenez des indicateurs pour la première fois, je trouve que, d’une certaine manière, “C” le cache un peu trop et que cela devient plus clair au niveau du langage d’assemblage. Ensuite, vous pouvez utiliser ces connaissances de niveau inférieur et voir comment le langage “C” ajoute simplement une syntaxe.

Juste une pensée. En plus de cela, K & R est un excellent livre, comme plusieurs personnes l’ont mentionné. Il peut également s’avérer utile de mettre en œuvre des types de données abstraits tels que des listes chaînées, notamment si vous tracez des diagrammes montrant la structure de la mémoire.

IMHO le meilleur moyen “d’obtenir” des pointeurs est de faire de la programmation en langage assembleur. Une fois que vous avez l’habitude de penser en termes de contenu de registre brut (sans distinction réelle entre les données et les adresses autres que celles que vous utilisez) et les instructions de chargement, les types de pointeurs de C seront beaucoup plus utiles (et votre appréciation de ce que C fait pour vous sera grandement amélioré).

Beaucoup de bonnes réponses données ci-dessus. Une autre idée est qu’un pointeur est toujours un type entier non signé. L’object ou la variable pointée en mémoire peut être de tout type.

Dans un système d’exploitation 32 bits, l’entier est un nombre de 4 octets et doit être compris entre

0

Dans un système d’exploitation 64 bits, l’entier est un nombre de 8 octets et doit être compris entre

0

Une valeur de pointeur = 0 est interprétée comme une valeur d’indicateur spécial appelée NULL, qui indique que ce pointeur ne pointe pas vers une variable utilisable.

Les pointeurs sont utilisés pour l’indirection. Certains registres d’une CPU agissent en tant que pointeurs, par exemple les registres de compteur de programmes (PC) et d’adresses.

Si vous voulez vraiment comprendre les indicateurs, vous avez besoin d’une bonne conférence universitaire.

Le meilleur que j’ai jamais vu est celui-ci .

Rien d’autre ne se compare.

Consultez également la conférence sur Turing. C’est très bien fait.

On dirait que vous avez les bases couvertes. Je ne voudrais pas sauter aux feuilles de sortingche tant que vous n’êtes pas allé plus loin dans la littérature. La raison pour laquelle vous faites attention lorsque vous travaillez avec des pointeurs est qu’ils vous permettent d’accéder directement à des adresses de mémoire spécifiques et que, si votre code le permet, des erreurs surviendront. Un tableau pointe vers le premier emplacement et, en fonction du type, vous pouvez accéder à d’autres emplacements du tableau en utilisant un pointeur du même type que celui-ci.

Je comprendrais d’abord les variables, lvalue, rvalue et assignations, puis les pointeurs en tant que types de variables, puis le déréférencement de pointeur et les autres opérations de pointeur. Il faudrait un peu plus de précisions à ce sujet et il existe déjà de nombreuses références valables.

Vous pouvez trouver un tutoriel d’introduction ici (explication très détaillée). Lien PDF direct .

Avez-vous lu K & R ? Si vous ne l’avez pas fait, je dirais que c’est par là que vous devriez commencer.

Voici quelques explications de base expliquant le fonctionnement des pointeurs C.

Une suggestion peu conventionnelle: http://www.youtube.com/watch?v=Rxvv9krECNw

C’est une conférence de Higher Computing 1 à mon université l’année dernière. Les premières minutes seront un peu inutiles (type sujet soumis admin), mais sinon c’est une très bonne explication