Introduction

Introduction

Nous sommes arrivés à un point crucial de notre série d'articles concernant le développement C. Ce n'est pas non plus, par coïncidence, cette partie de C qui donne beaucoup de maux de tête aux débutants. C'est là que nous entrons, et le but de cet article (l'un d'eux, de toute façon), est de démystifier les mythes sur les pointeurs et sur C en tant que langue difficile / impossible à apprendre et à lire. Néanmoins, nous recommandons une attention accrue et un peu de patience et vous verrez que les pointeurs ne sont pas aussi ahurissants que les légendes le disent.

Définitions et avertissements

Il semble naturel et bon sens que nous devrions commencer par les avertissements, et nous vous recommandons chaleureusement que vous vous en souvenez: alors que les pointeurs vous facilitent la vie en tant que développeur C, ils aussi peut introduire des bogues difficiles à trouver et un code incompréhensible. Vous verrez, si vous continuez à lire, ce dont nous parlons et la gravité desdits bugs, mais l'essentiel est, comme indiqué précédemment, être très prudent.

Une définition simple d'un pointeur serait «une variable dont la valeur est l'adresse d'une autre variable». Vous savez probablement que les systèmes d'exploitation traitent des adresses lors du stockage des valeurs, tout comme vous étiqueteriez les choses dans un entrepôt afin que vous ayez un moyen facile de les trouver en cas de besoin. D'un autre côté, un tableau peut être défini comme une collection d'articles identifiés par des index. Vous verrez plus tard pourquoi les pointeurs et les tableaux sont généralement présentés ensemble, et comment devenir efficace en C en les utilisant. Si vous avez une arrière-plan dans d'autres langages de niveau supérieur, vous connaissez le type de données de chaîne. En C, les tableaux sont l'équivalent des variables de type chaîne, et il est avancé que cette approche est plus efficace.



Pointeurs

Vous avez vu la définition d'un pointeur, commençons maintenant par quelques explications approfondies et, bien sûr, des exemples. Une première question que vous pouvez vous poser est «pourquoi devrais-je utiliser des pointeurs?". Bien que je puisse me faire flammer sur cette comparaison, je tenterai ma chance: utilisez-vous des liens symboliques dans votre système Linux? Même si vous n'en avez pas créé vous-même, votre système les ufse et cela rend le travail plus efficace. J'ai entendu des histoires d'horreur sur les développeurs de C senior qui jurent qu'ils n'ont jamais utilisé de pointeurs parce qu'ils sont «difficiles», mais cela signifie que le développeur est incompétent, rien de plus. De plus, il y a des situations où vous devrez utiliser des pointeurs, donc ils ne doivent pas être traités comme facultatifs, car ils ne sont pas. Comme précédemment, je crois à l'apprentissage par l'exemple, alors voilà:

int x, y, z; x = 1; y = 2; int * ptoi; / * ptoi est et représente, pointeur vers entier * / ptoi = & x; / * Ptoi pointe vers x * / z = * ptoi; / * z est maintenant 1, la valeur de X, vers laquelle ptoi pointe * / ptoi = & y; / * ptoi pointe maintenant vers y * /

Si vous vous grattez la tête dans la confusion, ne vous enfuisez pas: cela ne fait que mal la première fois, vous savez. Allons-y ligne par ligne et voyons ce que nous avons fait ici. Nous avons d'abord déclaré trois entiers, c'est X, Y et Z, et avons donné des valeurs X et Y 1 et 2, respectivement. C'est la partie simple. Le nouvel élément vient avec la déclaration du PTOI variable, qui est un pointeur vers un entier, pour que points Vers un entier. Ceci est accompli en utilisant l'astérisque avant le nom de la variable et on dit qu'il s'agit d'un opérateur de redirection. La ligne 'ptoi = & x;' signifie «Ptoi pointe maintenant vers X, qui doit être un entier, selon la déclaration de Ptoi ci-dessus». Vous pouvez maintenant travailler avec Ptoi comme vous le feriez avec X (enfin, presque). Sachant cela, la ligne suivante est l'équivalent de «z = x»;. Ensuite nous dérécision Ptoi, ce qui signifie que nous disons «Arrêtez de pointer vers X et commencez à pointer vers Y». Une observation importante est nécessaire ici: l'opérateur ne peut être utilisé que sur des objets résidents de la mémoire, ceux étant des variables (sauf le registre [1]) et.

[1] Les variables de type registre sont l'un des éléments de C qui existent, mais la majorité des programmeurs les évitent. Une variable avec ce mot-clé attaché suggère au compilateur qu'elle sera souvent utilisée et qu'elle devrait être stockée dans un processeur se regarde pour un accès plus rapide. La plupart des compilateurs modernes ignorent cet indice et décident par eux-mêmes de toute façon, donc si vous n'êtes pas sûr d'avoir besoin de vous inscrire, vous ne le faites pas.

Nous avons dit que Ptoi devait pointer vers un entier. Comment devrions-nous procéder si nous voulions un pointeur générique, donc nous n'aurons pas à nous soucier des types de données? Entrez le pointeur pour vide. C'est tout ce que nous vous dirons, et la première affectation est de découvrir les utilisations que le pointeur peut avoir le vide peut avoir et quelles sont ses limites.



Tableaux

Vous verrez dans ce sous-chapitre pourquoi nous avons insisté pour présenter des pointeurs et des tableaux dans un article, malgré le risque de surcharger le cerveau du lecteur. Il est bon de savoir que, lorsque vous travaillez avec des tableaux, vous n'avez pas à utiliser les pointeurs, mais c'est bien de le faire, car les opérations seront plus rapides, avec l'inconvénient d'un code moins compréhensible. Une déclaration de tableau est le résultat de déclarer un certain nombre d'éléments consécutifs disponibles via des index, comme ainsi:

int a [5]; int x; a [2] = 2; x = a [2];

A est un tableau à 5 éléments, le troisième élément étant 2 (la numérotation de l'index commence par zéro!), et x est défini comme étant aussi 2. De nombreux bogues et erreurs lorsqu'ils traitent pour la première fois avec les tableaux est que l'on oublie le problème 0-index. Lorsque nous avons dit des «éléments consécutifs», nous voulons dire qu'il est garanti que les éléments du tableau ont des emplacements consécutifs en mémoire, pas que si un [2] est 2, alors un [3] est 3. Il y a une structure de données dans C appelée une enum qui fait cela, mais nous ne les y occuperons pas pour l'instant. J'ai trouvé un ancien programme que j'ai écrit en apprenant C, avec l'aide de mon ami Google, qui inverse les personnages dans une chaîne. C'est ici:

#include #include int main () char fidèle [30]; int i; Char C; printf ("Tapez une chaîne .\ n "); fgets (filandre, 30, stdin); printf (" \ n "); pour(i = 0; i < strlen(stringy); i++) printf("%c", stringy[i]); printf("\n"); pour(i = strlen (filandre); i> = 0; i--) printf ("% c", filty [i]); printf ("\ n"); retour 0;  

C'est une façon de faire cela sans utiliser de pointeurs. Il a des défauts à bien des égards, mais il illustre la relation entre les chaînes et les tableaux. Tolty est un tableau de 30 caractères qui sera utilisé pour maintenir la saisie de l'utilisateur, je serai l'index du tableau et C sera le caractère individuel sur lequel être travaillé. Nous demandons donc une chaîne, nous l'enregistrons dans le tableau à l'aide de fgets, imprime la chaîne d'origine en commençant par filandre [0] et en continuant, en utilisant une boucle de manière incrémentielle, jusqu'à ce que la chaîne se termine. L'opération inverse donne le résultat souhaité: nous obtenons à nouveau la longueur de la chaîne avec strlen () et commençons un compte à rebours. Un autre aspect important est que tout tableau de caractères en C se termine par le caractère nul, représenté graphiquement par '\ 0'.

Comment ferions-nous tout cela en utilisant des pointeurs? Ne soyez pas tenté de remplacer le tableau par un pointeur vers Char, cela ne fonctionnera pas. Au lieu de cela, utilisez le bon outil pour le travail. Pour des programmes interactifs comme celui ci-dessus, utilisez des tableaux de caractères de longueur fixe, combinés à des fonctions sécurisées comme fgets (), afin que vous ne soyez pas mordu par des débordements tampons. Pour les constantes de chaîne, cependant, vous pouvez utiliser

char * myname = "David";

Et puis, en utilisant les fonctions qui vous sont fournies en chaîne.H, manipuler les données comme vous le voyez. En parlant de cela, quelle fonction choisissez-vous d'ajouter MyName aux chaînes qui s'adressent à l'utilisateur? Par exemple, au lieu de «Veuillez saisir un numéro», vous devriez avoir «David, veuillez entrer un numéro».



Pointeurs et tableaux

Vous pouvez et êtes encouragé à utiliser des tableaux en conjonction avec des pointeurs, bien qu'au début, vous pourriez être surpris à cause de la syntaxe. D'une manière générale, vous pouvez faire tout ce qui est lié au tableau avec des pointeurs, avec l'avantage de la vitesse à vos côtés. Vous pourriez penser qu'avec le matériel d'aujourd'hui, l'utilisation de pointeurs avec des tableaux juste pour gagner une certaine vitesse n'en vaut pas la peine. Cependant, à mesure que vos programmes augmentent en taille et en complexité, ladite différence commencera à être plus évidente, et si vous pensez à porter votre application sur une plate-forme intégrée, vous vous féliciterez. En fait, si vous avez compris ce qui a été dit jusqu'à présent, vous n'aurez pas de raisons de surprendre. Disons que nous avons une gamme d'entiers et que nous voulons déclarer un pointeur sur l'un des éléments de la table. Le code ressemblerait à ceci:

int myarray [10]; int * myptr; int x; myptr = & myArray [0]; x = * myptr;

Nous avons donc un tableau nommé MyArray, composé de dix entiers, un pointeur vers un entier, qui obtient l'adresse du premier élément du tableau, et x, qui obtient la valeur dudit premier élément via un pointeur. Maintenant, vous pouvez faire toutes sortes de trucs Nifty pour vous déplacer dans le tableau, comme

* (myptr + 1);

qui pointera vers le prochain élément de MyArray, à savoir MyArray [1].

Une chose importante à savoir, et en même temps qui illustre parfaitement la relation entre les pointeurs et les tableaux, est que la valeur d'un objet de type tableau est l'adresse de son «premier élément (zéro), donc si myptr = & myArray [ 0], alors myptr = MyArray. En quelque sorte un exercice, nous vous invitons à étudier un peu cette relation et à coder certaines situations où vous pensez que ce sera / pourrait être utile. C'est ce que vous rencontrerez en tant que pointeur arithmétique.

Considérations sur les chaînes en C et les appels

Avant que nous ayons vu que vous pouvez faire non plus

char * mystring; myString = "Ceci est une chaîne."

ou vous pouvez faire de même en utilisant

char mystring [] = "Ceci est une chaîne."

Dans le deuxième cas, comme vous l'avez peut-être inféré, MyString est un tableau assez grand pour conserver les données qui y sont attribuées. La différence est qu'en utilisant des tableaux, vous pouvez fonctionner sur des caractères individuels à l'intérieur de la chaîne, tandis qu'en utilisant l'approche du pointeur, vous ne pouvez pas. C'est un problème très important à retenir qui vous sauvera du compilateur d'avoir de grands hommes qui viennent chez vous et feront des choses terribles à votre grand-mère. Aller un peu plus loin, un autre problème dont vous devez être conscient est que si vous oubliez les pointeurs, les appels en C sont faits par valeur. Ainsi, lorsqu'une fonction a besoin de quelque chose d'une variable, une copie locale est faite et que le travail est effectué sur. Mais si la fonction modifie la variable, les modifications ne sont pas reflétées, car l'original reste intact. En utilisant des pointeurs, vous pouvez utiliser l'appel par référence, Comme vous le verrez dans notre exemple ci-dessous. De plus, l'appel par valeur pourrait devenir à forte intensité de ressources si les objets travaillés sont importants. Techniquement, il y a aussi un appel de Pointer, mais restez simple pour l'instant.

Disons que nous voulons écrire une fonction qui prend un entier comme argument et l'incrémente avec une certaine valeur. Vous serez probablement tenté d'écrire quelque chose comme ceci:

void incr (int a) a + = 20; 

Maintenant, si vous essayez cela, vous verrez que l'entier ne sera pas incrémenté, car seule la copie locale sera. Si tu avais écrit

void incr (int & a) a + = 20; 

Votre argument entier sera incrémenté avec vingt, ce que vous voulez. Donc, si vous aviez encore des doutes sur l'utilité des pointeurs, voici un exemple simple mais significatif.



Sujets quelque peu avancés

Nous avons pensé à mettre ces sujets dans une section spéciale car ils sont un peu plus difficiles à comprendre pour les débutants, mais sont des parties utiles et incontournables de la programmation C. Donc…

Pointeurs vers les pointeurs

Oui, les pointeurs sont des variables comme les autres, donc ils peuvent avoir d'autres variables pointer vers eux. Alors que les pointeurs simples comme indiqué ci-dessus ont un niveau de «pointage», les pointeurs vers les pointeurs en ont deux, donc une telle variable pointe vers une autre qui pointe vers un autre. Tu penses que c'est exaspérant? Vous pouvez avoir des pointeurs vers des pointeurs vers des pointeurs vers des pointeurs vers… .Ad Infinitum, mais vous avez déjà franchi le seuil de santé mentale et d'utilité si vous avez obtenu de telles déclarations. Nous vous recommandons d'utiliser CDECL, qui est un petit programme généralement disponible dans la plupart des distros Linux qui «se traduisent» entre C et C ++ et l'anglais et l'inverse. Ainsi, un pointeur vers un pointeur peut être déclaré comme

int ** ptrtoptr;

Maintenant, selon la façon dont les pointeurs de niveau multiple sont utiles, il y a des situations où vous avez des fonctions, comme la comparaison ci-dessus, et vous voulez en tirer un pointeur comme valeur de retour. Vous voudrez peut-être également un éventail de chaînes, ce qui est une fonctionnalité très utile, comme vous le verrez chez un coup de tête.

Tableaux multidimensionnels

Les tableaux que vous avez vus jusqu'à présent sont unidimensionnels, mais cela ne signifie pas que vous êtes limité à cela. Par exemple, un tableau bidimensionnel peut être imaginé dans votre esprit comme étant un tableau de tableaux. Mon conseil serait d'utiliser des tableaux multidimensionnels si vous ressentez le besoin, mais si vous êtes bon avec un bon et bon 'unidimensionnel, utilisez-le pour que votre vie de coder soit plus simple. Pour déclarer un tableau bidimensionnel (nous utilisons deux dimensions ici, mais vous n'êtes pas limité à ce nombre), vous ferez

 int bubimarray [4] [2];

qui aura pour effet de déclarer un tableau entier de 4 par 2. Pour accéder au deuxième élément verticalement (pensez à un puzzle de mots croisés si cela aide!) et le premier horizontalement, vous pouvez faire

Bidimarray [2] [1];

N'oubliez pas que ces dimensions sont réservées aux yeux: le compilateur alloue la mémoire et fonctionne avec le tableau de la même manière, donc si vous ne voyez pas l'utilité de cela, ne l'utilisez pas. Ergo, notre tableau ci-dessus peut être déclaré

int BidiMarRay [8]; / * 4 par 2, comme dit * /


Arguments de ligne de commande

Dans notre précédent épisode de la série, nous avons parlé de Main et comment il peut être utilisé avec ou sans arguments. Lorsque votre programme en a besoin et que vous avez des arguments, ils sont char argc et char * argv []. Maintenant que vous savez quels sont les tableaux et les pointeurs, les choses commencent à avoir plus de sens. Cependant, nous avons pensé à obtenir un peu de détail ici. char * argv [] peut également être écrit comme char ** argv. Comme une matière à réflexion, pourquoi pensez-vous que c'est possible? N'oubliez pas qu'Argv signifie «Argument Vector» et est un éventail de chaînes. Vous pouvez toujours vous fier au fait que Argv [0] est le nom du programme lui-même, tandis que Argv [1] est le premier argument et ainsi de suite. Donc, un court programme pour voir le nom de son 'et les arguments ressembleraient à ceci:

#include #include int main (int argc, char ** argv)  alors que(argc--) printf ("% s \ n", * argv ++); retour 0; 

Conclusion

Nous avons choisi les parties qui semblaient les plus essentielles pour la compréhension des pointeurs et des tableaux, et avons intentionnellement laissé de côté certains sujets comme les pointeurs vers des fonctions. Néanmoins, si vous travaillez avec les informations présentées ici et résolvez les exercices, vous aurez un assez bon début sur cette partie de C qui est considérée comme la principale source de code compliqué et incompréhensible.

Voici une excellente référence concernant les pointeurs C ++. Bien que ce ne soit pas C, les langues sont liées, donc l'article vous aidera à mieux comprendre les pointeurs.

Voici ce à quoi vous pouvez vous attendre ensuite:

  • je. C Développement sur Linux - Introduction
  • Ii. Comparaison entre C et d'autres langages de programmation
  • III. Types, opérateurs, variables
  • Iv. Contrôle de flux
  • V. Les fonctions
  • Vi. Pointeurs et tableaux
  • Vii. Structure
  • Viii. E / S de base
  • Ix. Style de codage et recommandations
  • X. Construire un programme
  • Xi. Emballage pour Debian et Fedora
  • Xii. Obtenir un forfait dans les référentiels officiels Debian

Tutoriels Linux connexes:

  • Une introduction à l'automatisation Linux, des outils et des techniques
  • Choses à installer sur Ubuntu 20.04
  • Masterring Bash Script Loops
  • Boucles imbriquées dans les scripts bash
  • Mint 20: Mieux que Ubuntu et Microsoft Windows?
  • Manipulation de Big Data pour le plaisir et le profit Partie 1
  • Choses à faire après l'installation d'Ubuntu 20.04 Focal Fossa Linux
  • Tutoriel de débogage GDB pour les débutants
  • Journalisation et audit avancés sur Linux
  • À quelle fréquence devez-vous redémarrer votre serveur Linux?