Pourquoi les pointeurs de fonction et les pointeurs de données sont-ils incompatibles en C / C ++?
J'ai lu que la conversion d'un pointeur de fonction à un pointeur de données et vice versa fonctionne sur la plupart des plates-formes, mais n'est pas garanti. Pourquoi est-ce le cas? Ne devrait pas être simplement des adresses dans la mémoire principale et donc compatible?
source d'informationauteur gexicide
Vous devez vous connecter pour publier un commentaire.
Une architecture n'est pas nécessaire de stocker le code et les données dans la même mémoire. Avec une architecture Harvard, le code et les données sont stockées dans un contexte totalement différent de la mémoire. La plupart des architectures, Von Neumann architectures avec le code et les données dans le même mémoire, mais C ne se limite pas uniquement à certains types d'architectures si possible.
Certains ordinateurs ont (eu) des espaces d'adressage différents pour le code et les données. Sur de tels matériels, il ne fonctionne tout simplement pas.
Le langage est conçu non seulement pour le courant des applications de bureau, mais pour lui permettre d'être mis en œuvre sur une large gamme de matériel.
Il semble que le langage C comité n'a jamais voulu
void*
être un pointeur de fonction, ils voulaient juste un pointeur générique d'objets.Le C99 Justification dit:
Note Rien n'est dit sur les pointeurs de fonctions dans le dernier paragraphe. Ils pourraient être différents des autres pointeurs, et le comité est conscient de cela.
Pour ceux qui se souviennent de MS-DOS, Windows 3.1 et plus la réponse est assez facile. L'ensemble de ces utilisés à l'appui de plusieurs différents modèles de mémoire, avec différentes combinaisons de caractéristiques pour le code et les données des pointeurs.
Ainsi, par exemple, pour le modèle Compact (petit code, de données de grande taille):
et, à l'inverse, dans le modèle Moyen (grand code, de données de petite taille):
Dans ce cas, vous n'avez pas de stockage séparé pour le code et la date, mais ne pouvait toujours pas de conversion entre les deux pointeurs (court de l'utilisation non-standard __près de et __ _ _ loin de modificateurs).
En outre, il n'y a aucune garantie que même si les pointeurs sont de la même taille, qu'ils pointent vers la même chose dans le DOS Petit modèle de mémoire, à la fois le code et les données utilisées près de pointeurs, mais ils ont souligné les différents segments. Si la conversion d'un pointeur de fonction à un pointeur de données ne serait pas vous donner un pointeur qui ont une relation à la fonction, et il n'y a donc pas d'utilisation d'une telle conversion.
Pointeurs void sont censés être en mesure d'accueillir un pointeur vers n'importe quel type de données, mais pas nécessairement un pointeur à une fonction. Certains systèmes ont des exigences différentes pour les pointeurs de fonctions en plus des pointeurs vers les données (e.g, il y a des Dsp avec différents adressage de données vs code, modèle moyen sur MS-DOS utilisé des pointeurs 32 bits pour le code, mais seulement 16 bits pointeurs pour les données).
En plus de ce qui est déjà dit ici, il est intéressant de regarder POSIX
dlsym()
:C++11 a une solution à la longue incompatibilité entre le C/C++ et POSIX à l'égard de
dlsym()
. On peut utiliserreinterpret_cast
de convertir un pointeur de fonction vers/à partir d'un pointeur de données aussi longtemps que la mise en œuvre prend en charge cette fonctionnalité.De la norme, 5.2.10 par. 8, "la conversion d'un pointeur de fonction à un objet de type pointeur ou vice versa, est conditionnelle-prise en charge". 1.3.5 définit "conditionnellement", appuyé comme un "programme de construction d'une mise en œuvre n'est pas nécessaire à l'appui".
En fonction de l'architecture cible, le code et les données peuvent être stockées dans fondamentalement incompatibles, physiquement distinctes des zones de mémoire.
indéfini ne veut pas forcément dire pas autorisée, il peut signifier que le compilateur réalisateur a plus de liberté pour le faire comme ils le veulent.
Par exemple, il peut ne pas être possible sur certaines architectures défini leur permet d'avoir encore un conforme " C " de la bibliothèque, même si vous ne pouvez pas faire cela.
Ils peuvent être de différents types avec différentes exigences de l'espace. L'attribution à l'un peut de manière irréversible la tranche de la valeur du pointeur de façon à ce que l'affectation de résultats dans quelque chose de différent.
Je crois qu'ils peuvent être de différents types, car la norme ne se limite possible des implémentations d'économiser de l'espace lorsqu'il n'est pas nécessaire, ou lorsque la taille pourrait provoquer le CPU supplémentaires, de la merde pour l'utiliser, etc...
Une autre solution:
En supposant que POSIX fonction des garanties et des pointeurs de données pour avoir la même taille et de la représentation (je ne trouve pas le texte, mais l'exemple l'OP cité montre qu'ils ont au moins prévu pour que cette exigence), la suivante devrait fonctionner:
Cela évite de violer l'aliasing règles en passant par la
char []
représentation, ce qui a permis de faire un alias de tous les types.Encore une autre approche:
Mais je vous recommande la
memcpy
approche si vous voulez absolument correct à 100% C.La seule véritable solution portable est de ne pas utiliser
dlsym
pour les fonctions, et au lieu d'utiliserdlsym
pour obtenir un pointeur vers les données qui contient des pointeurs de fonction. Par exemple, dans votre bibliothèque:et puis dans votre application:
D'ailleurs, c'est une bonne pratique en matière de conception, de toute façon, et il est facile de soutenir à la fois le chargement dynamique via
dlopen
et statique de la liaison de tous les modules sur les systèmes qui ne prennent pas en charge la liaison dynamique, ou lorsque l'utilisateur/intégrateur système ne souhaitez pas utiliser la liaison dynamique.Sur la plupart des architectures, des pointeurs vers tous les types de données ont la même représentation, de sorte que la conversion entre les données de type pointeur est un no-op.
Toutefois, il est concevable que des pointeurs de fonction peut nécessiter une représentation différente, peut-être qu'ils sont plus grands que les autres pointeurs. Si void* peut détenir des pointeurs de fonction, cela signifierait que le void*la représentation devrait être la plus grande taille. Et toutes les distributions de pointeurs de données vers/à partir de void* aurait pour effectuer cette copie supplémentaire.
Comme quelqu'un l'a mentionné, si vous avez besoin de ce que vous pouvez réaliser à l'aide d'une union. Mais la plupart des utilisations de void* sont seulement pour les données, de sorte qu'il serait onéreux à augmenter leur utilisation de la mémoire juste au cas où un pointeur de fonction doit être stocké.
Un exemple moderne d'où des pointeurs de fonction peuvent varier en taille de pointeurs de données: membre de classe C++ pointeurs de fonction
Directement tirée de la https://blogs.msdn.microsoft.com/oldnewthing/20040209-00/?p=40713/
tl;dr: Lors de l'utilisation de l'héritage multiple, un pointeur vers une fonction membre peut (selon le compilateur, la version, l'architecture, etc) en fait être stockées en tant que
ce qui est évidemment plus grand qu'un
void *
.Je sais que cela n'a pas été commenté depuis 2012, mais j'ai pensé qu'il serait utile d'ajouter que je ne savoir une architecture qui a très incompatible pointeurs pour les données et les fonctions depuis un appel sur cette architecture vérifie privilège et comporte des informations supplémentaires. Aucun montant de la coulée de l'aide. C'est Le Moulin.