¿El tamaño de (algún puntero) siempre es igual a cuatro?

227

Por ejemplo: sizeof(char*)regresa 4. Como no int*, long long*todo lo que he probado. ¿Hay alguna excepción a esto?

Joel
fuente
51
¿Por qué marcar esto? Buena pregunta para cualquier principiante.
Martin York
2
Sospecho que otra pregunta se esconde en esta: "¿Qué es sizeof?" o puede ser "¿Por qué sizeof <any pointer> == 4? ¿Qué tiene de especial 4?". Estoy en lo cierto?
2
Bueno, depende de tu plataforma. La mayoría de las implementaciones comparten el mismo tamaño para cada tipo de puntero en una plataforma específica.
phoeagon

Respuestas:

194

La garantía que obtienes es eso sizeof(char) == 1. No hay otras garantías, incluida ninguna garantía de eso sizeof(int *) == sizeof(double *).

En la práctica, los punteros serán de tamaño 2 en un sistema de 16 bits (si puede encontrar uno), 4 en un sistema de 32 bits y 8 en un sistema de 64 bits, pero no se gana nada confiando en un determinado Talla.

David Thornley
fuente
96
Y 3 bytes en un sistema de 24 bits. Sí, he trabajado en uno. Bienvenido al mundo de los dispositivos integrados.
dwj
30
También he trabajado en sistemas de 16 bits con punteros de 20 bits. Debería ir a ver qué tamaño regresa en ese caso ...
Juez Maygarden
55
@monjardin: IIRC, el 8086 era así. Había una dirección de 16 bits y un registro de segmento de 4 bits. Creo que un puntero "NEAR" normal tenía 16 bits y un puntero declarado como "FAR" era más, probablemente 24, aunque no estoy seguro.
rmeador
18
Otra garantía es que sizeof (char *) == sizeof (void *), porque tienen que tener las mismas representaciones (objeto [tamaño] y valor [conjunto de bits relevantes para su valor] representación)
Johannes Schaub - litb
77
Como la pregunta solicita excepciones, debe tenerse en cuenta que los punteros de función miembro no estáticos a menudo tienen un tamaño diferente al de los punteros normales y también varían según la plataforma, el tipo, etc. Además de eso +1.
John5342
36

Incluso en una plataforma simple x86 de 32 bits, puede obtener una variedad de tamaños de puntero, pruebe esto por ejemplo:

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;
}

En Visual C ++ 2008, obtengo 4, 12 y 8 para los tamaños de la función de punteros a miembros.

Raymond Chen habló sobre esto aquí .

Eclipse
fuente
44
Los punteros a las funciones de los miembros son un verdadero dolor. Es lamentable que no a todos los compiladores les guste el compilador Digital Mars C ++, que devuelve 4 en todos los casos.
dalle
gcc 4.72 print all 8 ... ¿Esto no está definido en el estándar c ++?
Gob00st
2
@ Gob00st: Lo único que se define es que char es 1. Otros tipos pueden ser del tamaño que sea relevante para ese compilador. No hay requisitos de coherencia entre estos tipos de puntero.
Eclipse
OK gracias. Entonces no es de extrañar que gcc y VC tengan una implementación diferente.
Gob00st
55
@Eclipse sí hay: char <= short <= int <= long <= long long
Cole Johnson
30

Solo otra excepción a la lista ya publicada. En plataformas de 32 bits, los punteros pueden tomar 6, no 4 , bytes:

#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 compila este programa con Open Watcom y lo ejecuta, obtendrá 6, porque los punteros lejanos que admite consisten en valores de desplazamiento de 32 bits y segmento de 16 bits

dmityugov
fuente
55
No es un segmento, sino un selector, no es parte de la dirección de memoria, sino una entrada de índice en LDT o GDT y tiene algunos indicadores de acceso
Roee Shenberg,
1
¿Por qué hay segmentos y compensaciones en x86 mientras el espacio de direcciones es plano?
phuclv
@ LưuVĩnhPhúc Porque ahorra espacio para el caso muy común de punteros cercanos, que pueden codificarse más cortos.
Christopher Creutzig
1
@ChristopherCreutzig que significa que los segmentos se utilizan para ampliar el espacio de direcciones como PAE?
phuclv
@ LưuVĩnhPhúc Ha pasado mucho tiempo que he realizado el ensamblaje en cualquier cosa de 32 bits. La parte que recuerdo recordar es que puede ahorrar espacio para los punteros que apuntan cerca del código que tiene. Además, no todas las arquitecturas de 32 bits, ciertamente no todas basadas en x86, usan un modelo de memoria plana. Vea, por ejemplo, tenouk.com/Bufferoverflowc/Bufferoverflow1a.html para más discusión sobre esto, aunque, como dije, ha pasado un tiempo y no puedo responder por nada.
Christopher Creutzig
24

Si está compilando para una máquina de 64 bits, entonces puede ser 8.

FryGuy
fuente
2
Si bien este suele ser el caso, no es necesariamente cierto. Por ejemplo, si está compilando en una máquina de 64 bits donde el tamaño de la palabra es de 64 bits, entonces sizeof (char *) probablemente será 1. Sin mencionar los tipos de punteros más exóticos incluso en máquinas comunes, como Eclipse y dmityugov escribe.
Kaz Dragon
@KazDragon, sizeof(char*)==1? ¿Estás seguro? No quiere decir size(char)==1?
Aaron McDaid el
3
@AaronMcDaid Me refería a sizeof (char *). sizeof (char) es siempre 1. Pero si la palabra de su máquina es de 64 bits y su entorno de desarrollo se implementa de tal manera que CHAR_BITS = 64, entonces es posible que un puntero se ajuste en el mismo espacio que un char y, por lo tanto, también sea 1.
Kaz Dragon
no es cierto en x32-abi sites.google.com/site/x32abi
phuclv
1
@KazDragon Estoy construyendo (muy lentamente, cuando no estoy postergando) una máquina con palabras de 16 bits y sin direccionamiento de bytes. Aunque no puede ejecutar C de todos modos.
user253751
17

Técnicamente hablando, el estándar C solo garantiza que sizeof (char) == 1, y el resto depende de la implementación. Pero en las arquitecturas modernas x86 (por ejemplo, chips Intel / AMD) es bastante predecible.

Probablemente haya escuchado que los procesadores se describen como de 16 bits, 32 bits, 64 bits, etc. Esto generalmente significa que el procesador usa N bits para enteros. Dado que los punteros almacenan direcciones de memoria y las direcciones de memoria son enteros, esto le indica efectivamente cuántos bits se utilizarán para los punteros. sizeof generalmente se mide en bytes, por lo que el código compilado para procesadores de 32 bits informará que el tamaño de los punteros es de 4 (32 bits / 8 bits por byte), y el código de los procesadores de 64 bits informará que el tamaño de los punteros es de 8 (64 bits / 8 bits por byte). Aquí es de donde proviene la limitación de 4 GB de RAM para procesadores de 32 bits: si cada dirección de memoria corresponde a un byte, para direccionar más memoria necesita enteros mayores de 32 bits.

Joseph Garvin
fuente
"Probablemente haya escuchado que los procesadores se describen como de 16 bits, 32 bits, 64 bits, etc. Esto generalmente significa que el procesador usa N bits para enteros". -> Tengo una máquina de 64 bits pero el tamaño de (int) es de 4 bytes. Si su afirmación es cierta, ¿cómo podría ser posible?
Sangeeth Saravanaraj
66
@SangeethSaravanaraj: Por compatibilidad con versiones anteriores de código de 32 bits, decidieron que int continúe siendo de 4 bytes y requieren que opte por usar el tipo de 8 bytes especificando 'largo'. largo es en realidad el tamaño de la palabra nativa en x86-64. Una forma de ver esto es que, por lo general, los compiladores rellenarán sus estructuras para alinearlas con palabras (aunque puede haber arquitecturas en las que el tamaño y la alineación de las palabras no estén relacionados), por lo que si crea una estructura con un int (32 bits), y llame al sizeof () en él, si obtiene 8, sabe que los está rellenando con un tamaño de palabra de 64 bits.
Joseph Garvin
@SangeethSaravanaraj: Tenga en cuenta que, en teoría, el tamaño de la palabra nativa de la CPU y lo que el compilador decide que 'int' puede ser arbitrariamente diferente, es solo una convención que 'int' sea el tamaño de la palabra nativa antes de que x86-64 apareciera, donde es largo para facilitar la compatibilidad con versiones anteriores.
Joseph Garvin
¡Gracias por la explicación! :)
Sangeeth Saravanaraj
7

El tamaño del puntero depende básicamente de la arquitectura del sistema en el que se implementa. Por ejemplo, el tamaño de un puntero en 32 bits es de 4 bytes (32 bits) y 8 bytes (64 bits) en máquinas de 64 bits. Los tipos de bits en una máquina no son más que direcciones de memoria, que pueden tener. Las máquinas de 32 bits pueden tener 2^32espacio de direcciones y las máquinas de 64 bits pueden tener hasta 2^64espacios de direcciones. Por lo tanto, un puntero (variable que apunta a una ubicación de memoria) debería poder apuntar a cualquiera de las direcciones de memoria ( 2^32 for 32 bit and 2^64 for 64 bit) que tiene una máquina.

Por esta razón, vemos que el tamaño de un puntero es de 4 bytes en una máquina de 32 bits y 8 bytes en una máquina de 64 bits.

Rndp13
fuente
6

Además de las diferencias de 16/32/64 bits, pueden ocurrir cosas aún más extrañas.

Ha habido máquinas donde sizeof (int *) será un valor, probablemente 4 pero donde sizeof (char *) es mayor. Las máquinas que naturalmente abordan palabras en lugar de bytes tienen que "aumentar" los punteros de caracteres para especificar qué parte de la palabra realmente desea para implementar correctamente el estándar C / C ++.

Esto es ahora muy inusual ya que los diseñadores de hardware han aprendido el valor de la capacidad de direccionamiento de bytes.

Darron
fuente
44
El compilador de C para máquinas de vectores Cray, como el T90, hace algo similar. Las direcciones de hardware son de 8 bytes y apuntan a palabras de 8 bytes. void*y char*se manejan en software, y se aumentan con un desplazamiento de 3 bits dentro de la palabra, pero dado que en realidad no hay un espacio de direcciones de 64 bits, el desplazamiento se almacena en los 3 bits de orden superior de los 64 bits palabra. Por lo tanto char*, y int*son del mismo tamaño, pero tienen diferentes representaciones internas - y el código que asume que los punteros son "realmente" sólo números enteros pueden dejar mal.
Keith Thompson
5

Los punteros de 8 y 16 bits se utilizan en la mayoría de los microcontroladores de bajo perfil. Eso significa que cada lavadora, micro, nevera, televisores antiguos e incluso automóviles.

Se podría decir que no tienen nada que ver con la programación del mundo real. Pero aquí hay un ejemplo del mundo real: Arduino con 1-2-4k ram (dependiendo del chip) con punteros de 2 bytes.

Es reciente, barato, accesible para todos y vale la pena codificarlo.

Kobor42
fuente
4

Además de lo que la gente ha dicho sobre los sistemas de 64 bits (o lo que sea), hay otros tipos de puntero que puntero a objeto.

Un puntero a miembro puede tener casi cualquier tamaño, dependiendo de cómo lo implemente su compilador: ni siquiera son todos del mismo tamaño. Pruebe un puntero a miembro de una clase POD, y luego un puntero a miembro heredado de una de las clases base de una clase con múltiples bases. Qué divertido.

Steve Jessop
fuente
3

Por lo que recuerdo, se basa en el tamaño de una dirección de memoria. Entonces, en un sistema con un esquema de dirección de 32 bits, sizeof devolverá 4, ya que son 4 bytes.

Will Mc
fuente
44
No hay tal requisito. Ni siquiera hay un requisito de que sizeof (unsigned int) == sizeof (firmado int). El tamaño de un puntero a un int siempre será, por definición, sizeof (int *), a un char sizeof (char *) etc. Confiar en cualquier otra suposición es una mala idea para la portabilidad.
Mihai Limbășan
Ah, ya veo ahora. Gracias por la info.
Will Mc
1
Todavía podría devolver 2, si CHAR_BIT es 16. sizeof () cuenta en número de caracteres, no en octetos.
MSalters
55
@Mihai: en C ++ sizeof (unsigned int) == sizeof (signed int), este requisito se encuentra en 3.9.1 / 3. "Para cada uno de la norma entero de tipos, existe un tipo entero sin signo estándar correspondiente (pero diferente): unsigned char, unsigned short int, unsigned int, unsigned long int, y unsigned long long int, cada uno de los cuales ocupa la misma cantidad de almacenamiento y tiene los mismos requisitos de alineación como el correspondiente entero de tipo "
Ben Voigt
3

En general, el tamaño de (casi cualquier cosa) cambiará cuando compiles en diferentes plataformas. En una plataforma de 32 bits, los punteros son siempre del mismo tamaño. En otras plataformas (64 bits es el ejemplo obvio) esto puede cambiar.

Sean Reilly
fuente
3

No, el tamaño de un puntero puede variar según la arquitectura. Hay numerosas excepciones.

El juez Maygarden
fuente
3

El tamaño del puntero e int es de 2 bytes en el compilador Turbo C en la máquina con Windows de 32 bits.

Por lo tanto, el tamaño del puntero es específico del compilador. Pero, en general, la mayoría de los compiladores se implementan para admitir variables de puntero de 4 bytes en 32 bits y variables de puntero de 8 bytes en máquinas de 64 bits).

Por lo tanto, el tamaño del puntero no es el mismo en todas las máquinas.

finalsemester.co.in
fuente
2

La razón por la que el tamaño de su puntero es de 4 bytes es porque está compilando para una arquitectura de 32 bits. Como señaló FryGuy, en una arquitectura de 64 bits verías 8.

Will Bickford
fuente
2

En Win64 (Cygwin GCC 5.4) , veamos el siguiente ejemplo:

Primero, pruebe la siguiente estructura:

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

struct test_struc{
    char a, b;
};

El código de prueba está abajo:

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 salida está abajo:

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

Puedes ver eso en 64 bits, sizeof(pointer)es 8.

Jayhello
fuente
1

Un puntero es solo un contenedor para una dirección. En una máquina de 32 bits, su rango de direcciones es de 32 bits, por lo que un puntero siempre será de 4 bytes. En una máquina de 64 bits donde tiene un rango de direcciones de 64 bits, un puntero será de 8 bytes.

Ed S.
fuente
1
En una máquina de 32 bits con bytes de 32 bits, sizeof (char *) podría ser 1.
Robert Gamble
"... con bytes de 32 bits". No sabía que tales cosas existían ... imagina eso.
Ed S.
1
En un pato de 32 bits, sizeof (char *) devuelve PI
Adriano Varoli Piazza
0

Solo para completar e interés histórico, en el mundo de 64 bits había diferentes convenciones de plataforma sobre los tamaños de tipos largos y largos, llamados LLP64 y LP64, principalmente entre sistemas de tipo Unix y Windows. Un viejo estándar llamado ILP64 también hizo int = 64 bits de ancho.

Microsoft mantuvo LLP64 donde longlong = 64 bit de ancho, pero se mantuvo largo en 32, para facilitar la transferencia.

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

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

Hernán
fuente