La taille de (un pointeur) est-elle toujours égale à quatre?

227

Par exemple: sizeof(char*)retourne 4. Comme le fait int*, long long*tout ce que je l' ai essayé. Y a-t-il des exceptions à cela?

Joel
la source
51
Pourquoi marquer cela? Bonne question pour tout débutant.
Martin York
2
Je soupçonne qu'une autre question se cache dans celle-ci: "Quelle est la taille de?" ou peut être "Pourquoi la taille de <n'importe quel pointeur> == 4? Qu'est-ce qui est si spécial avec 4?". Ai-je raison?
2
Eh bien, cela dépend de votre plateforme. La plupart des implémentations partagent une même taille pour chaque type de pointeur sur une plate-forme spécifique.
phoeagon

Réponses:

194

La garantie que vous obtenez est la suivante sizeof(char) == 1. Il n'y a aucune autre garantie, y compris aucune garantie sizeof(int *) == sizeof(double *).

En pratique, les pointeurs seront de taille 2 sur un système 16 bits (si vous en trouvez un), 4 sur un système 32 bits et 8 sur un système 64 bits, mais il n'y a rien à gagner à s'appuyer sur un Taille.

David Thornley
la source
96
Et 3 octets sur un système 24 bits. Oui, j'ai travaillé sur un. Bienvenue dans le monde des appareils embarqués.
dwj
30
J'ai également travaillé sur des systèmes 16 bits avec des pointeurs 20 bits. Je devrais aller voir quelle taille de retours dans ce cas ...
Juge Maygarden
5
@monjardin: IIRC, le 8086 était comme ça. Il y avait une adresse 16 bits et un registre de segments 4 bits. Je crois qu'un pointeur "NEAR" normal était de 16 bits et un pointeur déclaré comme "FAR" était plus, probablement 24, mais je ne suis pas sûr.
rmeador
18
une autre garantie est que sizeof (char *) == sizeof (void *), car ils doivent avoir les mêmes représentations (représentation [taille] et valeur [ensemble de bits pertinents pour leur valeur])
Johannes Schaub - litb
7
Étant donné que la question demande des exceptions, il convient de noter que les pointeurs de fonction membre non statiques ont souvent une taille différente des pointeurs normaux et varient également selon la plate-forme, le type, etc. À part cela +1.
John5342
36

Même sur une plate-forme simple x86 32 bits, vous pouvez obtenir une variété de tailles de pointeurs, essayez ceci pour un exemple:

struct A {};

struct B : virtual public A {};

struct C {};

struct D : public A, public C {};

int main()
{
    cout << "A:" << sizeof(void (A::*)()) << endl;
    cout << "B:" << sizeof(void (B::*)()) << endl;
    cout << "D:" << sizeof(void (D::*)()) << endl;
}

Sous Visual C ++ 2008, j'obtiens 4, 12 et 8 pour les tailles de la fonction pointeurs vers membre.

Raymond Chen en a parlé ici .

Éclipse
la source
4
Les pointeurs vers les fonctions des membres sont une vraie douleur. Il est regrettable que tous les compilateurs ne le fassent pas comme le compilateur Digital Mars C ++, qui renvoie 4 dans tous les cas.
dalle
gcc 4.72 print all 8 ... Est-ce indéfini dans la norme c ++?
Gob00st
2
@ Gob00st: La seule chose qui est définie est que char est 1. Les autres types peuvent avoir n'importe quelle taille pertinente pour ce compilateur. Il n'y a aucune exigence de cohérence entre ces types de pointeurs.
Eclipse
OK merci. Pas étonnant que gcc et VC aient une implémentation différente.
Gob00st
5
@Eclipse oui il y a: char <= short <= int <= long <= long long
Cole Johnson
30

Juste une autre exception à la liste déjà publiée. Sur les plates-formes 32 bits, les pointeurs peuvent prendre 6 octets et non 4 octets:

#include <stdio.h>
#include <stdlib.h>

int main() {
    char far* ptr; // note that this is a far pointer
    printf( "%d\n", sizeof( ptr));
    return EXIT_SUCCESS;
}

Si vous compilez ce programme avec Open Watcom et l'exécutez, vous obtiendrez 6, car les pointeurs éloignés qu'il prend en charge sont constitués de valeurs de décalage 32 bits et de segments 16 bits.

dmityugov
la source
5
Pas de segment, mais plutôt de sélecteur - il ne fait pas partie de l'adresse mémoire, mais une entrée d'index dans le LDT ou le GDT et a des drapeaux d'accès
Roee Shenberg
1
Pourquoi y a-t-il des segments et des décalages dans x86 alors que l'espace d'adressage est plat?
phuclv
@ LưuVĩnhPhúc Parce qu'il économise de l'espace pour le cas très courant des pointeurs proches, qui peuvent être encodés plus courts.
Christopher Creutzig
1
@ChristopherCreutzig qui signifie que les segments sont utilisés pour étendre l'espace d'adressage comme PAE?
phuclv
@ LưuVĩnhPhúc Cela fait longtemps que j'ai fait l'assemblage sur quoi que ce soit 32 bits. La partie dont je semble me souvenir est que vous pouvez économiser de l'espace pour les pointeurs pointant vers le code que vous avez. De plus, toutes les architectures 32 bits - certainement pas toutes basées sur le x86 - utilisent un modèle de mémoire plat. Voir, par exemple, tenouk.com/Bufferoverflowc/Bufferoverflow1a.html pour plus de discussion à ce sujet, même si, comme je l'ai dit, cela fait un moment et je ne peux garantir rien.
Christopher Creutzig
24

si vous compilez pour une machine 64 bits, cela peut être 8.

FryGuy
la source
2
Bien que ce soit généralement le cas, ce n'est pas nécessairement vrai. Par exemple, si vous compilez sur une machine 64 bits où la taille du mot est de 64 bits, alors sizeof (char *) sera probablement 1. Sans parler des types de pointeurs les plus exotiques dans les machines même courantes, comme Eclipse et dmityugov écrire.
Kaz Dragon
@KazDragon, sizeof(char*)==1? Êtes-vous sûr? Tu ne veux pas dire size(char)==1?
Aaron McDaid
3
@AaronMcDaid J'ai bien voulu dire sizeof (char *). sizeof (char) est toujours 1. Mais si votre mot machine est de 64 bits, et que votre environnement de développement est implémenté de telle manière que CHAR_BITS = 64, alors il est possible qu'un pointeur tienne dans le même espace qu'un char et sera donc être aussi 1.
Kaz Dragon
ce n'est pas vrai dans x32-abi sites.google.com/site/x32abi
phuclv
1
@KazDragon Je construis (très lentement, sans tergiverser) une machine avec des mots de 16 bits et sans adressage d'octets. Bien qu'il ne puisse pas exécuter C de toute façon.
user253751
17

Techniquement parlant, la norme C ne garantit que sizeof (char) == 1, et le reste dépend de l'implémentation. Mais sur les architectures x86 modernes (par exemple les puces Intel / AMD), c'est assez prévisible.

Vous avez probablement entendu des processeurs décrits comme étant 16 bits, 32 bits, 64 bits, etc. Cela signifie généralement que le processeur utilise N bits pour les nombres entiers. Étant donné que les pointeurs stockent des adresses de mémoire et que les adresses de mémoire sont des entiers, cela vous indique effectivement combien de bits vont être utilisés pour les pointeurs. sizeof est généralement mesuré en octets, donc le code compilé pour les processeurs 32 bits indiquera la taille des pointeurs à 4 (32 bits / 8 bits par octet), et le code pour les processeurs 64 bits indiquera la taille des pointeurs à 8 (64 bits / 8 bits par octet). C'est de là que vient la limitation de 4 Go de RAM pour les processeurs 32 bits - si chaque adresse mémoire correspond à un octet, pour adresser plus de mémoire, vous avez besoin d'entiers supérieurs à 32 bits.

Joseph Garvin
la source
"Vous avez probablement entendu des processeurs décrits comme étant 16 bits, 32 bits, 64 bits, etc. Cela signifie généralement que le processeur utilise N bits pour les nombres entiers." -> J'ai une machine 64 bits mais la taille de (int) est de 4 octets. Si votre affirmation est vraie, comment cela pourrait-il être possible?!
Sangeeth Saravanaraj
6
@SangeethSaravanaraj: Pour une compatibilité ascendante avec le code 32 bits, ils ont décidé de continuer à avoir 4 octets et vous obligent à choisir d'utiliser le type à 8 octets en spécifiant 'long'. long est en fait la taille du mot natif sur x86-64. Une façon de voir cela est que les compilateurs remplissent généralement vos structures pour les aligner sur le mot (bien qu'il puisse y avoir des architectures où la taille et l'alignement des mots ne sont pas liés), donc si vous créez une structure avec un int (32 bits), et appelez sizeof () dessus, si vous en récupérez 8, vous savez que cela les remplit en taille de mot 64 bits.
Joseph Garvin
@SangeethSaravanaraj: Notez que théoriquement, la taille du mot natif du CPU et ce que le compilateur décide `` int '' peut être arbitrairement différente, c'est juste qu'il était conventionnel que `` int '' soit la taille du mot natif avant l'arrivée de x86-64, où il est long de se détendre en arrière.
Joseph Garvin
Merci pour l'explication! :)
Sangeeth Saravanaraj
7

La taille du pointeur dépend essentiellement de l'architecture du système dans lequel il est implémenté. Par exemple, la taille d'un pointeur en 32 bits est de 4 octets (32 bits) et de 8 octets (64 bits) sur une machine 64 bits. Les types de bits dans une machine ne sont rien d'autre qu'une adresse mémoire, qu'elle peut avoir. Les machines 32 bits peuvent avoir un 2^32espace d'adressage et les machines 64 bits peuvent avoir jusqu'à 2^64des espaces d'adressage. Ainsi, un pointeur (variable qui pointe vers un emplacement mémoire) devrait pouvoir pointer vers n'importe quelle adresse mémoire ( 2^32 for 32 bit and 2^64 for 64 bit) qu'une machine détient.

Pour cette raison, la taille d'un pointeur est de 4 octets sur une machine 32 bits et de 8 octets sur une machine 64 bits.

Rndp13
la source
6

En plus des différences de bits 16/32/64, des choses encore plus étranges peuvent se produire.

Il y a eu des machines où sizeof (int *) sera une valeur, probablement 4 mais où sizeof (char *) est plus grande. Les machines qui adressent naturellement des mots au lieu d'octets doivent "augmenter" les pointeurs de caractères pour spécifier la partie du mot que vous voulez vraiment afin d'implémenter correctement la norme C / C ++.

Ceci est maintenant très inhabituel, car les concepteurs de matériel ont appris la valeur de l'adressabilité des octets.

Darron
la source
4
Le compilateur C pour les machines vectorielles Cray, comme le T90, fait quelque chose de similaire. Les adresses matérielles sont de 8 octets et pointent vers des mots de 8 octets. void*et char*sont gérés par logiciel, et sont augmentés d'un décalage de 3 bits dans le mot - mais comme il n'y a pas réellement d'espace d'adressage 64 bits, le décalage est stocké dans les 3 bits de poids fort du 64 bits mot. Donc char*, ils int*sont de la même taille, mais ont des représentations internes différentes - et le code qui suppose que les pointeurs sont "vraiment" juste des entiers peut échouer gravement.
Keith Thompson
5

Les pointeurs 8 bits et 16 bits sont utilisés dans la plupart des microcontrôleurs à profil bas. Cela signifie que chaque machine à laver, micro, réfrigérateur, téléviseurs plus anciens et même voitures.

On pourrait dire que cela n'a rien à voir avec la programmation du monde réel. Mais voici un exemple réel: Arduino avec 1 à 4 à 4 Ko de RAM (selon la puce) avec des pointeurs à 2 octets.

Il est récent, bon marché, accessible à tous et mérite d'être codé.

Kobor42
la source
4

En plus de ce que les gens ont dit sur les systèmes 64 bits (ou autre), il existe d'autres types de pointeurs que de pointeurs sur objets.

Un pointeur vers un membre peut avoir presque n'importe quelle taille, selon la façon dont il est implémenté par votre compilateur: ils ne sont pas nécessairement tous de la même taille. Essayez un pointeur sur membre d'une classe POD, puis un pointeur sur membre hérité d'une des classes de base d'une classe à plusieurs bases. Ce que c'est drôle.

Steve Jessop
la source
3

D'après ce que je me souviens, il est basé sur la taille d'une adresse mémoire. Donc, sur un système avec un schéma d'adresse 32 bits, sizeof renverra 4, car cela fait 4 octets.

Will Mc
la source
4
Il n'y a pas une telle exigence. Il n'y a même pas d'exigence que sizeof (entier non signé) == sizeof (entier signé). La taille d'un pointeur sur un int sera toujours, par définition, sizeof (int *), sur un char sizeof (char *) etc. Se fier à toute autre hypothèse est une mauvaise idée pour la portabilité.
Mihai Limbășan
Ah, je vois maintenant. Merci pour l'info.
Will Mc
1
Pourrait toujours retourner 2, si CHAR_BIT est 16. sizeof () compte en nombre de caractères, pas d'octets.
MSalters
5
@Mihai: En C ++ sizeof (unsigned int) == sizeof (signed int), cette exigence se trouve dans 3.9.1 / 3. « Pour chacun de la norme entier signé de types, il existe un correspondant (mais différente) standard de type entier non signé: unsigned char, unsigned short int, unsigned int, unsigned long int, et unsigned long long int, dont chacun occupe la même quantité de stockage et présente les mêmes exigences d'alignement en tant que correspondant entier signé de type "
Ben Voigt
3

En général, sizeof (à peu près n'importe quoi) change lorsque vous compilez sur différentes plateformes. Sur une plate-forme 32 bits, les pointeurs ont toujours la même taille. Sur d'autres plates-formes (64 bits étant l'exemple évident), cela peut changer.

Sean Reilly
la source
3

Non, la taille d'un pointeur peut varier en fonction de l'architecture. Il existe de nombreuses exceptions.

Le juge Maygarden
la source
3

La taille du pointeur et de l'int entier est de 2 octets dans le compilateur Turbo C sur une machine Windows 32 bits.

La taille du pointeur dépend donc du compilateur. Mais généralement, la plupart des compilateurs sont implémentés pour prendre en charge une variable de pointeur de 4 octets en 32 bits et une variable de pointeur de 8 octets en machine 64 bits).

La taille du pointeur n'est donc pas la même sur toutes les machines.

finalsemester.co.in
la source
2

La raison pour laquelle la taille de votre pointeur est de 4 octets est que vous compilez pour une architecture 32 bits. Comme l'a souligné FryGuy, sur une architecture 64 bits, vous verriez 8.

Will Bickford
la source
2

Dans Win64 (Cygwin GCC 5.4) , voyons l'exemple ci-dessous:

Tout d'abord, testez la structure suivante:

struct list_node{
    int a;
    list_node* prev;
    list_node* next;
};

struct test_struc{
    char a, b;
};

Le code de test est ci-dessous:

std::cout<<"sizeof(int):            "<<sizeof(int)<<std::endl;
std::cout<<"sizeof(int*):           "<<sizeof(int*)<<std::endl;
std::cout<<std::endl;

std::cout<<"sizeof(double):         "<<sizeof(double)<<std::endl;
std::cout<<"sizeof(double*):        "<<sizeof(double*)<<std::endl;
std::cout<<std::endl;

std::cout<<"sizeof(list_node):      "<<sizeof(list_node)<<std::endl;
std::cout<<"sizeof(list_node*):     "<<sizeof(list_node*)<<std::endl;
std::cout<<std::endl;

std::cout<<"sizeof(test_struc):     "<<sizeof(test_struc)<<std::endl;
std::cout<<"sizeof(test_struc*):    "<<sizeof(test_struc*)<<std::endl;    

La sortie est ci-dessous:

sizeof(int):            4
sizeof(int*):           8

sizeof(double):         8
sizeof(double*):        8

sizeof(list_node):      24
sizeof(list_node*):     8

sizeof(test_struc):     2
sizeof(test_struc*):    8

Vous pouvez le voir en 64 bits, sizeof(pointer)c'est 8.

Jayhello
la source
1

Un pointeur n'est qu'un conteneur pour une adresse. Sur une machine 32 bits, votre plage d'adresses est de 32 bits, donc un pointeur sera toujours de 4 octets. Sur une machine 64 bits où vous avez une plage d'adresses de 64 bits, un pointeur sera de 8 octets.

Ed S.
la source
1
Sur une machine 32 bits avec octets 32 bits, sizeof (char *) pourrait être 1.
Robert Gamble
"... avec des octets 32 bits". Je ne savais pas que de telles choses existaient ... imaginez ça.
Ed S.
1
Sur un canard 32 bits, sizeof (char *) renvoie PI
Adriano Varoli Piazza
0

Par souci d'exhaustivité et d'intérêt historique, dans le monde 64 bits, il existait différentes conventions de plate-forme sur les tailles des types longs et longs, appelés LLP64 et LP64, principalement entre les systèmes de type Unix et Windows. Un ancien standard nommé ILP64 a également fait int = 64 bits de large.

Microsoft a maintenu LLP64 où longlong = 64 bits de large, mais longtemps resté à 32, pour un portage plus facile.

Type           ILP64   LP64   LLP64
char              8      8       8
short            16     16      16
int              64     32      32
long             64     64      32
long long        64     64      64
pointer          64     64      64

Source: https://stackoverflow.com/a/384672/48026

Hernán
la source