web-dev-qa-db-fra.com

Combien de niveaux de pointeurs pouvons-nous avoir?

Combien de pointeurs (*) sont autorisés dans une seule variable?

Considérons l'exemple suivant.

int a = 10;
int *p = &a;

De même nous pouvons avoir

int **q = &p;
int ***r = &q;

etc.

Par exemple,

int ****************zz;
430
Parag

La norme C spécifie la limite inférieure:

5.2.4.1 Limites de traduction

276 L'implémentation doit pouvoir traduire et exécuter au moins un programme contenant au moins une instance de chacune des limites suivantes: [...]

279 - 12 déclarateurs de pointeur, de tableau et de fonction (dans n'importe quelle combinaison) modifiant un type arithmétique, structure, union ou vide dans une déclaration

La limite supérieure est spécifique à la mise en œuvre.

395
P.P.

En fait, les programmes C utilisent couramment l’indirection du pointeur infini. Un ou deux niveaux statiques sont communs. La triple indirection est rare. Mais l'infini est très commun.

L'infini du pointeur indirectionnel est obtenu à l'aide d'une structure, bien sûr, pas d'un déclarateur direct, ce qui serait impossible. Et une structure est nécessaire pour que vous puissiez inclure d'autres données dans cette structure aux différents niveaux où cela peut se terminer.

struct list { struct list *next; ... };

vous pouvez maintenant avoir list->next->next->next->...->next. Il s’agit en réalité de multiples indirections de pointeur: *(*(..(*(*(*list).next).next).next...).next).next. Et le .next est fondamentalement un noop quand il est le premier membre de la structure, nous pouvons donc l’imaginer comme ***..***ptr.

Il n’ya vraiment aucune limite à cela, car les liens peuvent être parcourus avec une boucle plutôt qu’une expression géante comme celle-ci, et de plus, la structure peut facilement être rendue circulaire.

Ainsi, en d'autres termes, les listes chaînées peuvent être l'exemple ultime de l'ajout d'un autre niveau d'indirection pour résoudre un problème, puisque vous le faites de manière dynamique à chaque opération Push. :)

149
Kaz

Théoriquement:

Vous pouvez avoir autant de niveaux de indirections que vous le souhaitez.

pratiquement:

Bien sûr, rien qui consomme de la mémoire ne peut être indéfini, il y aura des limitations dues aux ressources disponibles sur l'environnement hôte. Il y a donc pratiquement une limite maximale à ce qu'une mise en œuvre peut prendre en charge et la mise en œuvre doit la documenter de manière appropriée. Ainsi, dans tous ces artefacts, la norme ne spécifie pas la limite maximale, mais les limites inférieures.

Voici la référence:

C99 Standard 5.2.4.1 Limites de translation:

- 12 déclarateurs de pointeur, de tableau et de fonction (dans toute combinaison) modifiant un type arithmétique, structure, union ou vide dans une déclaration.

Ceci spécifie la limite inférieure que chaque implémentation doit prend en charge. Notez que dans une note de bas de page, la norme indique en outre:

18) Les implémentations doivent éviter d'imposer des limites de traduction fixes autant que possible.

80
Alok Save

Comme on l'a dit, pas de limite "en théorie". Cependant, par intérêt, je l’utilisais avec g ++ 4.1.2, et cela fonctionnait jusqu’à 20 000 personnes. Compiler était assez lent, donc je n'ai pas essayé plus haut. Donc, je suppose que g ++ n'impose aucune limite non plus. (Essayez de définir size = 10] et de regarder dans ptr.cpp si ce n'est pas immédiatement évident.)

g++ create.cpp -o create ; ./create > ptr.cpp ; g++ ptr.cpp -o ptr ; ./ptr

create.cpp

#include <iostream>

int main()
{
    const int size = 200;
    std::cout << "#include <iostream>\n\n";
    std::cout << "int main()\n{\n";
    std::cout << "    int i0 = " << size << ";";
    for (int i = 1; i < size; ++i)
    {
        std::cout << "    int ";
        for (int j = 0; j < i; ++j) std::cout << "*";
        std::cout << " i" << i << " = &i" << i-1 << ";\n";
    }
    std::cout << "    std::cout << ";
    for (int i = 1; i < size; ++i) std::cout << "*";
    std::cout << "i" << size-1 << " << \"\\n\";\n";
    std::cout << "    return 0;\n}\n";
    return 0;
}
74
BoBTFish

Cela semble amusant à vérifier.

  • Visual Studio 2010 (sous Windows 7), vous pouvez avoir 1011 niveaux avant d’obtenir cette erreur:

    erreur fatale C1026: débordement de la pile de l'analyseur, programme trop complexe

  • gcc (Ubuntu), 100k + * sans crash! Je suppose que le matériel est la limite ici.

(testé avec juste une déclaration de variable)

61
mihai

Il n'y a pas de limite, vérifiez l'exemple ici .

La réponse dépend de ce que vous entendez par "niveaux de pointeurs". Si vous voulez dire "Combien de niveaux d'indirection pouvez-vous avoir dans une seule déclaration?" la réponse est "au moins 12".

int i = 0;

int *ip01 = & i;

int **ip02 = & ip01;

int ***ip03 = & ip02;

int ****ip04 = & ip03;

int *****ip05 = & ip04;

int ******ip06 = & ip05;

int *******ip07 = & ip06;

int ********ip08 = & ip07;

int *********ip09 = & ip08;

int **********ip10 = & ip09;

int ***********ip11 = & ip10;

int ************ip12 = & ip11;

************ip12 = 1; /* i = 1 */

Si vous voulez dire "Combien de niveaux de pointeur pouvez-vous utiliser avant que le programme ne soit difficile à lire", c'est une question de goût, mais il y a une limite. Avoir deux niveaux d'indirection (un pointeur vers un pointeur vers quelque chose) est commun. Pas plus que cela devient un peu plus difficile à penser facilement; ne le faites pas à moins que l'alternative ne soit pire.

Si vous voulez dire "Combien de niveaux d'indirection de pointeur pouvez-vous avoir au moment de l'exécution", il n'y a pas de limite. Ce point est particulièrement important pour les listes circulaires, dans lesquelles chaque nœud pointe vers le suivant. Votre programme peut suivre les indications pour toujours.

27
Nandkumar Tekale

C'est en fait encore plus drôle avec le pointeur sur les fonctions.

#include <cstdio>

typedef void (*FuncType)();

static void Print() { std::printf("%s", "Hello, World!\n"); }

int main() {
  FuncType const ft = &Print;
  ft();
  (*ft)();
  (**ft)();
  /* ... */
}

Comme illustré ici cela donne:

Bonjour le monde!
Bonjour le monde!
Bonjour le monde!

Et cela n'implique aucune surcharge d'exécution, vous pouvez donc probablement les empiler autant de fois que vous le souhaitez ... jusqu'à ce que votre compilateur s'étouffe avec le fichier.

23
Matthieu M.

Il y a aucune limite. Un pointeur est un bloc de mémoire dont le contenu est une adresse.
Comme tu dis

int a = 10;
int *p = &a;

Un pointeur sur un pointeur est également une variable qui contient l'adresse d'un autre pointeur.

int **q = &p;

Ici, q est un pointeur sur un pointeur contenant l'adresse de p qui contient déjà l'adresse de a.

Un pointeur vers un pointeur n'a rien de particulièrement spécial.
Il n’ya donc pas de limite au nombre de chaînons contenant l’adresse d’un autre pointeur.
c'est à dire.

 int **************************************************************************z;

est autorisée.

19
Sachin Mhetre

Notez qu'il y a deux questions possibles ici: combien de niveaux d'indirection de pointeur nous pouvons atteindre dans un type C et combien de niveaux d'indirection de pointeur nous pouvons ranger dans un seul déclarateur.

La norme C permet d'imposer un maximum à l'ancien (et donne une valeur minimale pour cela). Mais cela peut être contourné via plusieurs déclarations typedef:

typedef int *type0;
typedef type0 *type1;
typedef type1 *type2; /* etc */

Donc, en fin de compte, il s’agit d’un problème d’implémentation lié à l’idée de la taille et de la complexité d’un programme C avant qu’il ne soit rejeté, ce qui est très spécifique au compilateur.

13
Kaz

Chaque développeur C++ devrait avoir entendu parler de la célèbre programmeur trois étoiles

Et il semble vraiment y avoir une "barrière de pointeur" magique qui doit être camouflée

Citation de C2:

Programmeur Trois étoiles

Un système de notation pour les programmeurs C. Plus vos pointeurs sont indirects (c’est-à-dire plus "*" avant vos variables), plus votre réputation sera élevée. Les programmeurs C sans étoiles sont pratiquement inexistants, car pratiquement tous les programmes non triviaux nécessitent l'utilisation de pointeurs. La plupart sont des programmeurs à une étoile. Dans les temps anciens (enfin, je suis jeune, cela ressemble au moins au temps ancien), on trouvait parfois un morceau de code créé par un programmeur trois étoiles et qui frissonnait. Certaines personnes ont même prétendu avoir vu du code à trois étoiles avec des pointeurs de fonction impliqués, à plus d'un niveau d'indirection. Sonnait aussi réel que les ovnis pour moi.

13
Mare Infinitus

J'aimerais souligner que la production d'un type avec un nombre arbitraire de * est quelque chose qui peut arriver avec la métaprogrammation de modèles. J'oublie exactement ce que je faisais, mais il a été suggéré que je pourrais produire de nouveaux types distincts comportant une sorte de méta-manœuvre entre eux en utilisant des types récursifs T *.

La métaprogrammation des templates est une lente descente dans la folie, il n'est donc pas nécessaire de trouver des excuses pour générer un type avec plusieurs milliers de niveaux d'indirection. C'est simplement un moyen pratique de mapper des entiers peano, par exemple, sur le développement de modèles en tant que langage fonctionnel.

3
JDługosz

La règle 17.5 de la norme 2004 MISRA C interdit plus de 2 niveaux d'indirection de pointeur.

2
kostmo

Il n'y a pas une telle chose comme limite réelle mais la limite existe. Tous les pointeurs sont des variables qui stockent généralement dans la pile pas tas. La pile est généralement petite (il est possible de changer sa taille lors de certaines liaisons). Alors disons que vous avez une pile de 4 Mo, ce qui est une taille assez normale. Et disons que nous avons un pointeur qui a une taille de 4 octets (la taille des pointeurs n’est pas la même en fonction de l’architecture, des paramètres de la cible et du compilateur).

Dans ce cas, 4 MB / 4 b = 1024 le nombre maximal possible serait 1048576, mais nous ne devrions pas ignorer le fait que d'autres éléments sont dans la pile.

Cependant, certains compilateurs peuvent avoir un nombre maximal de chaînes de pointeurs, mais la limite est la taille de la pile. Donc, si vous augmentez la taille de la pile lors de la liaison avec l'infini et que vous avez une machine avec une mémoire infinie, le système d'exploitation qui gère cette mémoire génère une chaîne de pointeurs illimitée.

Si vous utilisez int *ptr = new int; et placez votre pointeur dans le tas, ce n'est pas le cas . La limite habituelle serait la taille du tas, pas la pile.

EDIT Il suffit de se rendre compte que infinity / 2 = infinity. Si la machine a plus de mémoire, la taille du pointeur augmente. Donc, si la mémoire est infinie et la taille du pointeur est infinie, alors c'est une mauvaise nouvelle ... :)

1
ST3

Cela dépend de l'endroit où vous stockez les pointeurs. S'ils sont en pile, vous avez assez bas limite. Si vous le stockez en tas, votre limite est beaucoup plus élevée.

Regardez ce programme:

#include <iostream>

const int CBlockSize = 1048576;

int main() 
{
    int number = 0;
    int** ptr = new int*[CBlockSize];

    ptr[0] = &number;

    for (int i = 1; i < CBlockSize; ++i)
        ptr[i] = reinterpret_cast<int *> (&ptr[i - 1]);

    for (int i = CBlockSize-1; i >= 0; --i)
        std::cout << i << " " << (int)ptr[i] << "->" << *ptr[i] << std::endl;

    return 0;
}

Il crée 1 M pointeurs et aux spectacles ce qui pointe à quoi il est facile de remarquer ce que la chaîne va à la première variable number.

BTW. Il utilise 92K de RAM alors imaginez à quelle profondeur vous pouvez aller.

0
ST3