8
votes

Pourquoi "long *" et "int *" ne sont-ils pas compatibles dans le code 32 bits?

Je me demandais pourquoi le code suivant ne compile pas: xxx

J'utilise GCC, et que ni les appels ne fonctionnent non plus en C ou C ++. Comme il s'agit d'un système de 32 bits, les deux int et longs sont des entiers 32 bits signés (qui peuvent être vérifiés avec la taille de la taille de la compilation). < p> La raison pour laquelle je demande est que j'ai deux fichiers d'en-tête distincts, ni sont sous mon contrôle, et on fait quelque chose comme: Typedef non signé long u32; et l'autre: Typedef non signé Int uint32_t; . Les déclarations sont essentiellement compatibles, sauf lorsque je les utilise comme des pointeurs que dans l'extrait de code ci-dessus, je dois lancer explicitement.

Une idée de cette idée?

c c++

1 commentaires

Bonne question et réponses intéressantes. La solution techniquement judicieuse consiste probablement à modifier l'un des fichiers d'en-tête Typedfs - bien que peut-être difficile pour d'autres raisons que vous avez dit.


10 Réponses :


26
votes

... parce que la norme C ++ définit int et longtemps être deux types distincts, quelle que soit leur plage de valeurs, leur représentation, etc. Si par "fondamentalement compatible", vous voulez dire convertible les uns aux autres, puis oui.


0 commentaires

7
votes

long et int sont deux types distincts, même si elles ont la même taille. Il y aurait d'énormes conséquences dans le monde de modèle C ++ si elles étaient traitées de la même manière par certains compilateurs.


0 commentaires

28
votes

juste parce que long et int soit 32 bits sur votre compilateur et matériel particulier, ne signifie pas qu'ils seront toujours 32 bits sur chaque type de matériel et chaque compilateur.

C (et C ++) ont été conçus pour être portables source entre différents compilateurs et différents matériels.


0 commentaires

18
votes

Je peux voir deux questions distinctes ici.

Premièrement, sur les architectures modernes, il est assez sûr de supposer que les pointeurs ont la même taille (c'est-à-dire qu'il n'y a pas de pointeurs proches / loin; mais les pointeurs vers des fonctions membres ne sont pas des pointeurs réguliers et peut être une taille différente); et sur un système 32 bits que la taille est généralement de 32 bits. C Il va même jusqu'à la lancer automatiquement Void * code> à autre chose parce que, après tout, un pointeur n'est vraiment qu'une adresse mémoire. Cependant, les définitions linguistiques distinguent différents pointeurs comme différents types. Je crois que la raison en est que différents types peuvent avoir un alignement différent (la règle Void * code> est que rien ne peut vraiment être de type vide code>, donc si vous avez un pointeur pour void code> Vous connaissez probablement le type correct et, implicitement, l'alignement correct ( voir la note em>)) p>

second, comme d'autres ont signalé, long code> s et intens code> sont des types fondamentalement différents avec des conversions par défaut intégrées. Les normes nécessitent long code> S pour être au moins aussi grand que int s, mais éventuellement plus grand. Sur votre architecture, l'alignement est probablement le même mais sur d'autres architectures qui pourraient également être différents. P>

Il est possible de fudger dans votre cas, mais ce n'est pas portable. Si vous n'êtes pas #include code> les déclarations de fonction correctes et que vous transmettez simplement simplement les déclarer, les choses vous-même doivent fonctionner comme par magie car dans votre cas long code> s et int code> int code > S sont compatibles (en supposant qu'aucune question de signature; aussi en C ++, vous aurez besoin externe "C" code> à la fois de vos déclarations et des implémentations de fonction réelles afin que vous n'obtiens pas les erreurs de liaison). Jusqu'à ce que vous passionniez sur un compilateur différent ou un autre système d'exploitation, ou une architecture différente, etc. p>

par exemple, dans C ++, vous pouvez le faire: P>

$ g++ -c lib.cc -o lib.o
$ g++ main.cc lib.o
$ ./a.out
foo_long 5 at address 0x22cce4
foo_int 10 at address 0x22cce0


2 commentaires

Cela devrait être la réponse sélectionnée.


Il n'est pas sûr de supposer que les pointeurs des membres (en C ++) ont la même taille que les autres pointeurs.



2
votes

Ceci est dû au fait que sur certaines plates-formes longues et INT sont de taille différente.

long foo(int bar);
int foo(int bar);


2 commentaires

Ce n'est pas une chose longue / int - vous ne pouvez jamais surcharger sur le type de retour. Le compilateur pourrait traiter FOO (Int Bar) et FOO (Long Bar) comme la même qui semble faux mais utile si vous imaginez appeler foo (1);


La conversion entière n'est pas la question de la question. Il s'agit de la conversion du pointeur. La conversion implicite entre les types d'entier est autorisée dans tous les cas.



2
votes

int * et long * sont des types distincts, qui ne sont pas nécessairement les mêmes. Dans chaque mise en œuvre réelle, je pense qu'ils sont, mais ce n'est ni ici ni là pour un compilateur conformément aux normes.

Je crois que c'était l'une des premières machines PDP dans lesquelles un char * était plus grand qu'une int *. La raison de c'était la taille étrange d'INT sur cette architecture (36bits). Donc, le système emballerait plusieurs caractères 9 bits dans un seul Int, donc un char * contenait l'adresse du format de (int *, décalé dans l'INT). **

La norme spécifie que tous les pointeurs sont représentables comme un vide * et insinuit que Char * doit être identique au vide *, mais il n'y a pas d'exigence particulière pour que les autres types de pointeur soient convertibles.

** Je suis incapable de trouver des références à cela, la source de cela pourrait donc être un exemple théorique (mais toujours valide) plutôt qu'une implémentation réelle. C ++ FAQ Lite


2 commentaires

Chaque mise en œuvre réelle? Considérez le monde intégré 8 bits et 16 bits. Souvent, Int est de 16 bits, longue est de 32 bits.


Je ne parle pas d'Int et de long, je parle de Int * and Long *. Je ne suis pas au courant de toute mise en œuvre dans laquelle ces types de pointeur n'ont pas la même taille. Les observations de l'affiche originale que Int et Long ont la même taille sur sa plate-forme sont un hareng rouge complet dans ce cas.



4
votes

Puisque je n'aime pas particulièrement aucune des réponses que donnée jusqu'à présent, je suis allé à la norme C ++:

4.7 conversions intégrales [CONV.Integal]

1 une rvalue d'un type d'entier peut être converti en une rvalue d'un autre type entier. Une rvalue d'un Le type d'énumération peut être converti en une rvalue d'un type d'entier.

Ceci dit qu'il est autorisé à convertir implicitement un entier à un autre, de sorte que les deux types (comme ils ont la même taille) sont interchangeables en tant que rapeaux.

4.10 conversions de pointeur [CONV.PTR]

1 une expression constante intégrale ( expr.Const ) RValue de type entier qui évalue à zéro (appelé un null la constante du pointeur) peut être convertie en un type de pointeur. Les le résultat est une valeur (appelée NULL valeur du pointeur de ce type) distingué de chaque pointeur à un objet ou une fonction. Deux null Les valeurs du pointeur du même type doivent comparer égale. La conversion d'un Null pointeur constant à un pointeur au type qualifié CV est un seul conversion, et pas la séquence d'un conversion du pointeur suivie de une conversion de qualification ( conv allélal ).

2 une rvalue de type "pointeur à cv t," où T est un type d'objet, peut être converti en une rvalue de type "Pointeur à cv nul." Le résultat de convertir un "pointeur en cv t" à un "pointeur à cv vide" pointe vers le Début de l'emplacement de stockage où L'objet de type T réside, comme Si l'objet est un plus dérivé objet ( intro.Object ) de type t (c'est-à-dire pas une classe de base SubObject).

3 une rvalue de type "pointeur à cv d," où d est un type de classe, peut être converti en une rvalue de type "Pointeur à cv b," où B est une base classe ( classe.dived ) de D. Si B est une inaccessible ( classe.access ) ou ambigu ( classe.member.lookup ) classe de base de D, un programme qui nécessite cela la conversion est mal formée. Le résultat de la conversion est un pointeur à le sous-objet de la classe de base de la Objet de classe dérivé. Le null La valeur du pointeur est convertie en null Valeur du pointeur du type de destination.

Il est uniquement autorisé à convertir implicitement:

  • 0 à n'importe quel type de pointeur (ce qui en fait un pointeur nul)
  • Tout type de pointeur à vide * (correctement qualifié CV)
  • Pointeur dérivé sur un pointeur de base (correctement qualifié CV)

    Donc, même si le type de machine sous-jacent est identique, il n'est pas autorisé à convertir implicitement entre les deux types.


0 commentaires

1
votes

int et longs sont définis comme des types distincts afin que vous puissiez programmer portable.


1 commentaires

+1 pour noter la portabilité. Vous pouvez remplacer "programme" par "surcharge".



0
votes

Vous n'avez pas besoin d'écrire la distribution explicite à la main sur chaque appel. Une macro simple peut le faire: xxx


1 commentaires

J'espère que vous réalisez que ce n'est pas portable. C'est équivalent à un réinterpret_cast. Dans le cas et longtemps, ne partagez pas le même "type sous-jacent", vous obtiendrez des résultats étranges sur les grandes machines d'Endian.



1
votes

Je ne pense pas que ces réponses coupées à la chasse.

La réponse est la suivante: la conversion valide entre les types n'implique pas la conversion valide entre les pointeurs. Cela a du sens, non? Vous souhaitez que le code suivant compile xxx

mais laisser ce code compile serait une recette de désastre: xxx

Juste parce qu'il existe une conversion entre long et int ne signifie pas qu'il devrait y avoir une conversion de pointeur. Mais, vous pouvez protester, long et int ont exactement la même représentation sur votre compilateur! Et vous auriez raison mais cela ne change pas le fait qu'en ce qui concerne la norme et le compilateur sont concernés, ce sont des types différents. Et vous ne pouvez pas convertir implicitement entre les pointeurs en types à moins qu'il n'y ait une relation d'héritage.

De plus, plus généralement, tandis que C ++ peut changer s'il est valable non valide sur les définitions locales de la taille de la taille Code> int , etc. Sont, il ne change pas s'il est syntaxiquement correct. Élimination complète de la distinction entre long et int fera cela.


0 commentaires