Pensez au vide comme à la «structure vide». Laisse-moi expliquer.
Chaque fonction prend une séquence de paramètres, où chaque paramètre a un type. En fait, nous pourrions regrouper les paramètres dans une structure, avec les emplacements de structure correspondant aux paramètres. Cela fait que chaque fonction a exactement un argument. De même, les fonctions produisent un résultat, qui a un type. Il peut s'agir d'un booléen, d'un flottant ou d'une structure contenant un ensemble arbitraire d'autres valeurs typées. Si nous voulons un languge qui a plusieurs valeurs de retour, il est facile d'insister simplement pour qu'il soit empaqueté dans une structure. En fait, nous pourrions toujours insister sur le fait qu'une fonction renvoie une structure. Désormais, chaque fonction prend exactement un argument et produit exactement une valeur.
Maintenant, que se passe-t-il lorsque j'ai besoin d'une fonction qui ne produit aucune valeur? Eh bien, considérez ce que j'obtiens lorsque je forme une structure avec 3 emplacements: elle contient 3 valeurs. Quand j'ai 2 emplacements, il contient deux valeurs. Lorsqu'il a un emplacement, une valeur. Et quand il n'a aucun emplacement, il contient ... euh, aucune valeur ou "aucune" valeur ". Donc, je peux penser à une fonction renvoyant void comme renvoyant une structure ne contenant aucune valeur. Vous pouvez même décider que" void " est juste un synonyme du type représenté par la structure vide, plutôt qu'un mot-clé dans la langue (peut-être que c'est juste un type prédéfini :)
De même, je peux penser à une fonction ne nécessitant aucune valeur comme acceptant une structure vide, par exemple, "void".
Je peux même implémenter mon langage de programmation de cette façon. La transmission d'une valeur void prend zéro octet, donc la transmission de valeurs void n'est qu'un cas particulier de transmission d'autres valeurs de taille arbitraire. Cela permet au compilateur de traiter facilement le résultat ou l'argument "void". Vous voulez probablement une fonctionnalité de langage qui peut rejeter un résultat de fonction; en C, si vous appelez la fonction de résultat non-void foo dans l'instruction suivante: foo (...); le compilateur sait que foo produit un résultat et l'ignore simplement. Si void est une valeur, cela fonctionne parfaitement et maintenant les "procédures" (qui ne sont qu'un adjectif pour une fonction avec un résultat void) ne sont que des cas spéciaux triviaux de fonctions générales.
Void * est un peu plus drôle. Je ne pense pas que les designers C aient pensé au vide de la manière ci-dessus; ils viennent de créer un mot-clé. Ce mot-clé était disponible quand quelqu'un avait besoin d'un point sur un type arbitraire, donc void * comme idiome en C. Cela fonctionne plutôt bien si vous interprétez void comme une structure vide. Un pointeur void * est l'adresse d'un endroit où cette structure vide a été placée.
Les moulages de vide * à T * pour les autres types T, fonctionnent également dans cette perspective. Les lancers de pointeurs sont une triche complète qui fonctionnent sur les architectures les plus courantes pour tirer parti du fait que si un type composé T a un élément avec le sous-type S placé physiquement au début de T dans sa disposition de stockage, alors le casting S * en T * et vice versa, l'utilisation de la même adresse de machine physique a tendance à fonctionner, car la plupart des pointeurs de machine ont une seule représentation. Le remplacement du type S par le type void donne exactement le même effet, et donc le lancer vers / depuis void * fonctionne.
Le langage de programmation PARLANSE met en œuvre les idées ci-dessus assez étroitement. Nous avons gaffé dans sa conception, et n'avons pas prêté attention à "void" comme type de retour et avons donc des mots-clés de langue pour la procédure. Il s'agit principalement d'un simple changement de syntaxe, mais c'est l'une des choses auxquelles vous ne vous déplacez pas une fois que vous obtenez un gros code de travail dans un langage.