Quelques questions sur un tableau à instance unique dans typedef

Je parcourais du code en utilisant des entiers de longueur arbitraire en utilisant le code de la bibliothèque GNU Multi-Precision (GMP). Le type d’un entier MP est mpz_t tel que défini dans le fichier d’en-tête gmp.h.

Cependant, j’ai quelques questions sur la définition de niveau inférieur de ce type mpz_t défini par la mpz_t . Dans le code d’en-tête:

 /* THIS IS FROM THE GNU MP LIBRARY gmp.h HEADER FILE */ typedef struct { /* SOME OTHER STUFF HERE */ } __mpz_struct; typedef __mpz_struct mpz_t[1]; 

Première question: le [1] s’associe-t-il à la __mpz_struct ? En d’autres termes, la typedef définissant un type mpz_t tant que tableau __mpz_struct avec une occurrence?

Deuxième question: pourquoi le tableau? (Et pourquoi un seul événement?) S’agit-il d’un de ces piratages dont j’ai entendu parler?

Troisième question (peut-être indirectement liée à la deuxième question): La documentation GMP pour la fonction mpz_init_set(mpz_t, unsigned long int) indique de l’utiliser uniquement comme valeur de passage, bien que l’on suppose que cette fonction modifierait son contenu dans la syntaxe de la fonction appelée (et aurait donc besoin de passer par référence). Reportez-vous à mon code:

 /* FROM MY CODE */ mpz_t fact_val; /* declaration */ mpz_init_set_ui(fact_val, 1); /* Initialize fact_val */ 

Le tableau à une occurrence permet-il automatiquement de passer par référence (en raison de la décomposition de la sémantique de tableau / pointeur en C)? J’admets volontiers que je suis en train de sur-parsingr cela, mais j’adorerais toute discussion à ce sujet. Merci!

* Première question: le [1] s’associe-t-il à la __mpz_struct? En d’autres termes, la typedef définissant un type mpz_t en tant que tableau __mpz_struct avec une occurrence? *

Oui.

Deuxième question: pourquoi le tableau? (Et pourquoi un seul événement?) S’agit-il d’un de ces piratages dont j’ai entendu parler?

Me bat. Je ne sais pas, mais une possibilité est que l’auteur ait voulu créer un object passé automatiquement par référence, ou “oui”, éventuellement le struct hack. Si vous voyez un object mpz_t comme le dernier membre d’une structure, alors “presque certainement” c’est la structure. Une allocation ressemblant à

 malloc(sizeof(struct whatever) + sizeof(mpz_t) * some_number)` 

serait un cadeau mort.

Est-ce que le tableau à une seule occurrence permet de passer automatiquement par référence …?

Aha, vous l’avez compris aussi. “Oui”, une des raisons possibles est de simplifier la lecture par référence aux dépens de références plus complexes.

Je suppose qu’une autre possibilité est que quelque chose ait changé dans le modèle de données ou l’algorithme, et l’auteur a voulu trouver chaque référence et la changer d’une manière ou d’une autre. Un changement de type comme celui-ci laisserait le programme avec le même type de base mais générerait une erreur avec chaque référence non convertie.

Cela ne semble pas être un struct hack dans le sens décrit sur C2. Il semble qu’ils veulent que mpz_t ait une sémantique de pointeur (vraisemblablement, ils veulent que les gens l’utilisent comme un pointeur opaque). Considérez la différence syntaxique entre les extraits suivants:

 struct __mpz_struct data[1]; (&data[0])->access = 1; gmp_func(data, ...); 

Et

 mpz_t data; data->access = 1; gmp_func(data, ...); 

mpz_t donné que les tableaux C se désintègrent en pointeurs, cela permet également le passage automatique par référence pour le type mpz_t .

Il vous permet également d’utiliser un type de type pointeur sans avoir besoin de malloc ou de le free .

La raison en est la mise en œuvre de mpn . Plus précisément, si vous êtes incliné mathématiquement, vous réaliserez que N est l’ensemble des nombres naturels (1,2,3,4 …) alors que Z est l’ensemble des entiers (…, – 2, -1,0 , 1,2, …).

Implémenter une bibliothèque bignum pour Z équivaut à le faire pour N et prendre en compte certaines règles spéciales pour les opérations de signe, c.-à-d. Savoir si vous devez faire une addition ou une soustraction et quel est le résultat.

Maintenant, en ce qui concerne la manière dont une bibliothèque bignum est implémentée … voici une ligne pour vous donner un indice:

 typedef unsigned int mp_limb_t; typedef mp_limb_t * mp_ptr; 

Et maintenant, regardons une signature de fonction opérant sur celle-ci:

 __GMP_DECLSPEC mp_limb_t mpn_add __GMP_PROTO ((mp_ptr, mp_srcptr, mp_size_t, mp_srcptr,mp_size_t)); 

En gros, ce qui revient à dire qu’un “membre” est un champ entier représentant les bits d’un nombre et que le nombre entier est représenté par un grand tableau. La partie intelligente est que gmp fait tout cela de manière très efficace et bien optimisée.

Quoi qu’il en soit, revenons à la discussion. Comme vous le savez, la seule façon de faire passer des tableaux en C consiste à faire passer des pointeurs sur ces tableaux, ce qui permet effectivement de passer par référence. Maintenant, afin de garder une trace de ce qui se passe, deux types sont définis, un mp_ptr qui est un tableau de mp_limb_t assez grand pour stocker votre numéro et mp_srcptr qui est une version mp_srcptr de celui-ci, de sorte que vous ne pouvez pas altérer accidentellement les bits. de la source bignums sur ce que vous exploitez. L’idée de base est que la plupart des fonctions suivent ce modèle:

 func(ptr output, src in1, src in2) 

etc. Ainsi, je soupçonne mpz_* fonctions de mpz_* suivent cette convention simplement pour être cohérentes et c’est parce que c’est ainsi que les auteurs pensent.

Version courte: En raison de la manière dont vous devez implémenter une bibliothèque bignum, cela est nécessaire.