Triple pointeurs en C: est-ce une question de style?
J'ai envie de triple pointeurs en C sont considérés comme "mauvais". Pour moi, il est logique d'utiliser à la fois.
À partir de la base, les seul pointeur a deux objectifs: créer un tableau, et pour permettre une fonction pour modifier son contenu (passage par référence):
char *a;
a = malloc...
ou
void foo (char *c); //means I'm going to modify the parameter in foo.
{ *c = 'f'; }
char a;
foo(&a);
La double pointeur peut être un tableau 2D (ou tableau de tableaux, puisque chaque "colonne" ou "ligne" n'a pas besoin d'être de la même longueur). Personnellement, j'aime l'utiliser quand j'en ai besoin pour passer un tableau 1D:
void foo (char **c); //means I'm going to modify the elements of an array in foo.
{ (*c)[0] = 'f'; }
char *a;
a = malloc...
foo(&a);
Pour moi, qui permet de décrire ce que toto est en train de faire. Cependant, il n'est pas nécessaire:
void foo (char *c); //am I modifying a char or just passing a char array?
{ c[0] = 'f'; }
char *a;
a = malloc...
foo(a);
fonctionnera également.
Selon la première réponse à cette question, si foo
ont été de modifier la taille du tableau, un double pointeur serait nécessaire.
On peut clairement voir comment un triple pointeur (et au-delà, vraiment) serait nécessaire. Dans mon cas, si j'étais le passage d'un tableau de pointeurs (ou tableau de tableaux), je voudrais l'utiliser. Bien entendu, il serait nécessaire si vous êtes de passage dans une fonction qui est en train de changer la taille d'un tableau multi-dimensionnel. Certainement un tableau de tableaux de tableaux n'est pas trop commun, mais les autres cas sont.
Alors, quelles sont certaines des conventions de là-bas? Est-ce vraiment juste une question de style et la lisibilité combiné avec le fait que beaucoup de gens ont un moment difficile de l'emballage de leurs têtes autour de pointeurs?
- Est-ce que c'rare de créer un double pointeur vers un type opaque (ce qui pourrait être un pointeur lui-même)? c'est à dire
typedef struct foo *foo_t;
puis plus tard surfoo_t **bar
- Regardez comment un pointeur vers un pointeur peut être un unique "vendre" le point de C (par rapport à Java et autres): stackoverflow.com/questions/18373657/...
- Ce livre se lit comme un blog, pas une question, StackOverflow n'est pas un endroit idéal pour l'affichage des essais plaider en faveur ou à l'encontre notamment des techniques de programmation.
- De plus: si j'avais été à "commencer à partir de la base" concernant les pointeurs, leur relation à des tableaux n'est pas quelque chose que je considère comme fondamentaux. L'objectif fondamental d'un pointeur est de permettre la manipulation des emplacements de stockage de données. Que certains de ces emplacements de stockage se trouvent être des tableaux de particulier arité est un détail intéressant, mais pas fondamentaux.
- En utilisant
typedef
, code de réduire le nombre de*
dans une variable ou d'un paramètre de déclaration.foo(bar *a)
présente bien desa
comme un pointeur de typebar
, même sitypedef int **** bar
. Suggèrent 3+*
devrait toujours être évitée et 2*
seulement utilisé dans certains situations. - Je vais voter pour le style et la lisibilité et ajouter comment c'est facile de faire des erreurs; au meilleur de ma connaissance, je pense que j'ai seulement vu un triple pointeur deux fois dans ma vie - dans la définition de scandir, et la source du traditionnel vi (l'un est la cause de chaînes C étant des pointeurs)
- une question qui se pose est que il n'y a pas des tableaux alloués dynamiquement dans C (vous devez utiliser un pointeur, puis l'adresse comme vous le feriez d'un tableau). Comme le titre l'indique c'est une question de style; devrait triple (et plus) des pointeurs être utilisées, si elles permettent de mettre en évidence un allouée dynamiquement tableau 2D (par exemple) va être modifié par une fonction. Est-il un autre notation pour passer un n-dimensions tableau dont les dimensions ne sont pas connues au moment de la compilation?
- StackOverflow est un endroit idéal pour mettre de l'information, de la période. Je me suis poser une question, bien que moins pratique. Encore, beaucoup de ces questions (même les moins populaires sont ceux qui apparaissent sur les résultats de la recherche et d'aider les gens.
Vous devez vous connecter pour publier un commentaire.
À l'aide de triple+ pointeurs est nuire à la fois la lisibilité et la maintenabilité.
Supposons que vous avez un peu de déclaration de fonction ici:
Hmmm. L'argument est-il un en trois dimensions de tableau en escalier, ou un pointeur à deux dimensions du tableau en escalier, ou un pointeur de pointeur de tableau (comme dans la fonction alloue un tableau et attribue un pointeur de int dans une fonction)
Nous le comparons à:
Sûrement, vous pouvez utiliser le triple de pointeurs sur int pour fonctionner sur les matrices. Mais ce n'est pas ce qu'ils sont. Le fait qu'ils sont mis en œuvre ici comme triple pointeurs est hors de propos à l'utilisateur.
Compliqué structures de données doivent être encapsulé. C'est l'une des idées de manifeste de la Programmation Orientée Objet. Même en C, vous pouvez appliquer ce principe dans une certaine mesure. Enveloppez la structure de données d'un struct (ou, très commun en C, en utilisant des "poignées", qui est, les pointeurs de type incomplète - cette doctrine sera expliqué plus tard dans la réponse).
Supposons que vous avez mis en œuvre les matrices telles que les tableaux irréguliers de
double
. Par rapport à contiguë 2D tableaux, ils sont pire lors de l'itération sur eux (comme ils n'appartiennent pas à un seul bloc de mémoire contiguë), mais de permettre l'accès à la notation de tableau et chaque ligne peut avoir différentes tailles.Alors maintenant, le problème est que vous ne pouvez pas modifier les représentations maintenant, comme l'utilisation de pointeurs est câblé sur le code de l'utilisateur, et maintenant vous êtes coincé avec inférieures de mise en œuvre.
Ce ne serait pas même un problème si vous encapsulé dans une structure (struct).
obtient tout simplement changé de
La poignée technique fonctionne comme ceci: dans le fichier d'en-tête, vous déclarez une incomplètes struct et toutes les fonctions que le travail sur le pointeur vers une struct:
dans le fichier source, vous déclarez la réelle structure et de définir toutes les fonctions:
Le reste du monde ne sait pas ce que fait le
struct Matrix_
contenir et il ne sait pas la taille de celui-ci. Cela signifie que les utilisateurs ne peuvent pas déclarer les valeurs directement, mais seulement par l'utilisation d'un curseur pourMatrix
et lacreate_matrix
fonction. Cependant, le fait que l'utilisateur ne connaît pas la taille signifie que l'utilisateur ne dépend pas d'elle - ce qui signifie que l'on peut supprimer ou ajouter des membres àstruct Matrix_
à volonté.struct
. Avais-je fait que dans mon code, j'aurais pu éviter le triple notation pointeur, depuis que j'ai été en passant un pointeur vers un tableau de pointeurs de certainsstruct
.Plusieurs indirection n'est pas mauvais style, ni de la magie noire, et si vous faites affaire avec de haute dimension de données, alors vous allez avoir affaire à de hauts niveaux d'indirection; si vous êtes vraiment affaire avec un pointeur vers un pointeur vers un pointeur vers
T
, alors ne soyez pas peur d'écrireT ***p;
. Ne cachez pas les pointeurs derrière typedefs sauf celui qui utilise le type de ne pas avoir à se soucier de son "pointeur-ness". Par exemple, si vous fournissez le type comme une "poignée" qui est passée autour d'une API, tels que:alors bien sûr,
typedef
loin. Mais sih
est jamais destiné à être déréférencé, commepuis ne pas
typedef
il. Si votre utilisateur a savoir queh
est un pointeur versint
1 pour l'utiliser correctement, alors que l'information doit pas être caché derrière un typedef.Je dirai que dans mon expérience, je n'ai pas eu à faire face à la hausse des niveaux d'indirection; triple indirection a été la plus forte, et je n'ai pas eu à l'utiliser plus d'une couple de fois. Si vous régulièrement de vous trouver en présence de >3-les dimensions données, alors vous allez voir de hauts niveaux d'indirection, mais si vous comprenez comment pointeur d'expressions et d'indirection travail il ne devrait pas être un problème.
1. Ou un pointeur de pointeur de
int
, ou un pointeur de pointeur de pointeur de pointeur destruct grdlphmp
, ou quoi que ce soit.La plupart du temps, l'utilisation de 3 niveaux d'indirection est un symptôme par la mauvaise conception de décisions prises ailleurs dans le programme. Par conséquent, il est considéré comme une mauvaise pratique et il y a des blagues sur les "trois étoiles programmeurs" où, contrairement à la notation pour les restaurants, plus d'étoiles, de moins bonne qualité.
La nécessité pour les 3 niveaux d'indirection provient souvent de la confusion sur la façon d'attribuer correctement les multi-dimensions des tableaux dynamiquement. C'est souvent enseigné mal même dans les livres de programmation, en partie du fait de la faire correctement était un lourd fardeau avant le standard C99. Mon Q&Un post Correctement l'allocation de matrices multi-dimensionnelles aborde cette question et montre également comment plusieurs niveaux d'indirection va rendre le code plus difficile à lire et à maintenir.
Bien que ce post explique, il y a certaines situations où un
type**
peut avoir du sens. Une variable tableau de chaînes de caractères de longueur variable est un exemple. Et quand ce besoin detype**
se pose, vous pourriez bientôt être tentés de l'utilisertype***
, parce que vous avez besoin de retourner votretype**
par le biais d'un paramètre de la fonction.Le plus souvent, ce besoin s'en fait sentir dans une situation où vous êtes à la conception d'une sorte de complexe d'ADT. Par exemple, disons que nous sommes le codage d'une table de hachage, où chaque indice est un "enchaînés" lié liste, et chaque nœud dans la liste liée à un tableau. La bonne solution est donc de re-concevoir le programme à utiliser les structures au lieu de plusieurs niveaux d'indirection. La table de hachage, liste et tableau doit être de différents types, types autonomes sans la moindre conscience les uns des autres.
Ainsi, en utilisant une conception appropriée, nous permettra d'éviter les multiples étoiles automatiquement.
Mais comme avec toutes les règles de bonne programmation pratique, il y a toujours des exceptions. Il est parfaitement possible d'avoir une situation comme:
Vous peut mettre en œuvre le ci-dessus comme un guide pratique, mais il peut également exister des raisons valables pour garder les choses simples et il suffit d'utiliser un
char* [n]
. Vous avez alors deux options pour allouer dynamiquement:ou
L'ancien est plus formellement correct, mais aussi de la lourdeur. Car il doit être utilisé comme
(*arr_ptr)[i] = "string";
, tandis que l'alternative peut être utilisé commeptr_ptr[i] = "string";
.Maintenant, supposons que nous avons à la place de malloc appel à l'intérieur d'une fonction et le type de retour est réservé à un code d'erreur, comme il est de coutume avec les C Api. Les deux solutions seront alors ressembler à ceci:
ou
Il est assez difficile d'argumenter et de dire que le premier est plus lisible, et il est également livré avec la complexité de l'accès nécessaire par l'appelant. Les trois étoiles alternative est en fait plus élégant, dans ce cas très précis.
De sorte qu'il n'est pas bon pour rejeter les 3 niveaux d'indirection de façon dogmatique. Mais le choix de les utiliser doivent être bien informés, avec une prise de conscience qu'ils peuvent créer laid code et qu'il existe d'autres alternatives.
Malheureusement, vous avez mal compris la notion de pointeur et les matrices ° C. n'oubliez pas que tableaux ne sont pas des pointeurs.
Lorsque vous déclarez un pointeur, alors vous devez l'initialiser avant de l'utiliser dans le programme. Cela peut être fait en passant l'adresse d'une variable ou par allocation dynamique de la mémoire.
Dans ce dernier, le pointeur peut être utilisé comme des tableaux indexés, mais ce n'est pas un tableau).
De nouveau mal. Les tableaux ne sont pas des pointeurs, et vice-versa. Un pointeur de pointeur n'est pas le tableau 2D.
Je vous suggère de lire le c-la faq de la section 6. Les tableaux et les Pointeurs.
a
à une fonction, il va compiler et exécuter! Il n'y a tout simplement pas de possibilité de lire/écrire la valeur de derrière le pointeur, comme l'adresse peut pointer vers une section de mémoire vous n'êtes pas autorisé à lire/écrire à! À cet effet, il est une bonne idée de mettre ena = NULL
. Mais vous n'avez pas à le faire!Après deux niveaux d'indirection, la compréhension devient difficile. Si d'ailleurs la raison pour laquelle vous êtes le passage de ces triple (ou plus) les pointeurs dans vos méthodes est de sorte qu'ils peuvent ré-allouer et re-régler certains ont indiqué à la mémoire, qui est loin de la notion de méthodes comme des "fonctions" que le juste retour des valeurs et n'affectent pas l'état. Cela nuit également à la compréhension et à la maintenabilité au delà d'un certain point.
Mais plus fondamentalement, vous avez touché à l'un des principaux stylistique des objections à la triple pointeur ici:
C'est la "et au-delà", c'est la question ici: une fois que vous obtenez à trois niveaux, où avez-vous l'arrêter? Sûrement, il possible d'avoir un aribitrary nombre de niveaux d'indirection. Mais c'est mieux d'avoir juste un coutumier limite dans un endroit où la lisibilité est bonne, mais la souplesse est adéquate. Deux est un bon nombre. "Les trois étoiles de la programmation", comme il est parfois appelé, est controversée au mieux; c'est brillant, ou un casse-tête pour ceux qui en ont besoin pour maintenir le code plus tard.
InitMem
fonction). Ils sont également très utiles lors de la parallélisation de choses (par exemple, avec OpenMP), de sorte qu'il ya moins d'erreur dans ce qui est partagé et ce qui est privé (que l'on pourrait dire seulement favorise la lisibilité).