que - punteros c++
Detalles de la implementación del puntero en C (11)
La representación en memoria de un puntero es lo mismo que un entero de la misma longitud de bit que la arquitectura.
Creo que esta suposición es falsa porque en el 80186, por ejemplo, un puntero de 32 bits se mantiene en dos registros (un registro de desplazamiento y un registro de segmento), y la mitad de la palabra que entró en el registro importa durante el acceso.
La multiplicación y división de tipos de datos de puntero solo están prohibidas por el compilador.
No puedes multiplicar o dividir tipos. ;PAG
No estoy seguro de por qué querría multiplicar o dividir un puntero.
Todos los valores de puntero se pueden convertir en un solo entero. En otras palabras, ¿qué arquitecturas siguen haciendo uso de los segmentos y las compensaciones?
El estándar C99 permite que los punteros se almacenen en intptr_t
, que es un tipo entero. Entonces sí.
Incrementar un puntero es equivalente a agregar sizeof (el tipo de datos puntiagudos) a la dirección de memoria almacenada por el puntero. Si p es un int32 *, p + 1 es igual a la dirección de memoria de 4 bytes después de p.
x + y
donde x
es una T *
e y
es un número entero es equivalente a (T *)((intptr_t)x + y * sizeof(T))
hasta donde sé. La alineación puede ser un problema, pero el relleno puede proporcionarse en el sizeof
. No estoy realmente seguro.
Me gustaría conocer las arquitecturas que violan las suposiciones que he enumerado a continuación. Además, me gustaría saber si alguna de las suposiciones es falsa para todas las arquitecturas (es decir, si alguna de ellas está completamente equivocada).
sizeof (int *) == sizeof (char *) == sizeof (void *) == sizeof (func_ptr *)
La representación en memoria de todos los punteros para una arquitectura dada es la misma independientemente del tipo de datos al que se apunta.
La representación en memoria de un puntero es lo mismo que un entero de la misma longitud de bit que la arquitectura.
La multiplicación y división de tipos de datos de puntero solo están prohibidas por el compilador. NOTA: Sí, sé que esto es absurdo. Lo que quiero decir es: ¿hay soporte de hardware para prohibir este uso incorrecto?
Todos los valores de puntero se pueden convertir en un solo entero. En otras palabras, ¿qué arquitecturas siguen haciendo uso de los segmentos y las compensaciones?
Incrementar un puntero es equivalente a agregar
sizeof(the pointed data type)
a la dirección de memoria almacenada por el puntero. Sip
es unint32*
,p+1
es igual a la dirección de memoria de 4 bytes después dep
.
Estoy más acostumbrado a los punteros que se utilizan en un espacio de memoria virtual contiguo. Para ese uso, generalmente puedo pensar en ellos como direcciones en una línea numérica. Ver comparación de puntero de desbordamiento de pila.
Me gustaría conocer las arquitecturas que violan las suposiciones que he enumerado a continuación.
Veo que Stephen C mencionó las máquinas PERQ y MSalters mencionó 68000 y PIC.
Estoy decepcionado de que nadie más haya respondido a la pregunta nombrando a cualquiera de las arquitecturas extrañas y maravillosas que tienen compiladores de C que cumplen con los estándares y que no se ajustan a ciertas suposiciones injustificadas.
sizeof (int *) == sizeof (char *) == sizeof (void *) == sizeof (func_ptr *)?
No necesariamente. Algunos ejemplos:
La mayoría de los compiladores para procesadores de 8 bits de la arquitectura Harvard (PIC y 8051 y M8C) hacen que sea sizeof (int *) == sizeof (char *), pero diferente del tamaño de (func_ptr *).
Algunos de los chips muy pequeños en esas familias tienen 256 bytes de RAM (o menos) pero varios kilobytes de PROGMEM (Flash o ROM), por lo que los compiladores a menudo hacen que sizeof (int *) == sizeof (char *) sea igual a 1 (a byte único de 8 bits), pero sizeof (func_ptr *) igual a 2 (dos bytes de 8 bits).
Los compiladores para muchos de los chips más grandes en esas familias con unos pocos kilobytes de RAM y 128 o más kilobytes de PROGMEM hacen que sizeof (int *) == sizeof (char *) sea igual a 2 (dos bytes de 8 bits), pero sizeof ( func_ptr *) igual a 3 (tres bytes de 8 bits).
Algunos chips de arquitectura Harvard pueden almacenar exactamente 2 ^ 16 ("64KByte") completos de PROGMEM (Flash o ROM), y otros 2 ^ 16 ("64KByte") de E / S de memoria RAM asignada. Los compiladores para tal chip make sizeof (func_ptr *) siempre son 2 (dos bytes); pero a menudo tiene una forma de convertir los otros tipos de punteros sizeof (int *) == sizeof (char *) == sizeof (void *) en un puntero genérico de 3 bytes "a ptr largo" que tiene el bit de magia adicional que indica Si ese puntero apunta a RAM o PROGMEM. (Ese es el tipo de puntero que debe pasar a una función "print_text_to_the_LCD ()" cuando llama a esa función desde muchas subrutinas diferentes, a veces con la dirección de una cadena variable en el búfer que puede estar en cualquier lugar de la RAM, y otras veces con una de muchas cadenas constantes que podrían estar en cualquier lugar en PROGMEM). Tales compiladores a menudo tienen palabras clave especiales ("corto" o "cerca", "largo" o "lejos") para que los programadores indiquen específicamente tres tipos diferentes de punteros de caracteres en el mismo programa: cadenas constantes que solo necesitan 2 bytes para indicar dónde en PROGMEM están ubicadas, cadenas no constantes que solo necesitan 2 bytes para indicar dónde están ubicadas en la RAM, y el tipo de punteros de 3 bytes que acepta "print_text_to_the_LCD ()".
La mayoría de las computadoras construidas en las décadas de 1950 y 1960 usan una longitud de palabra de 36 bits o una longitud de palabra de 18 bits , con un bus de direcciones de 18 bits (o menos). Escuché que los compiladores de C para tales computadoras a menudo usan bytes de 9 bits , con sizeof (int *) == sizeof (func_ptr *) = 2 que da 18 bits, ya que todos los enteros y funciones tienen que estar alineados con la palabra; pero sizeof (char *) == sizeof (void *) == 4 para aprovechar las instrucciones especiales de PDP-10 que almacenan dichos punteros en una palabra completa de 36 bits. Esa palabra completa de 36 bits incluye una dirección de palabra de 18 bits, y algunos bits más en los otros 18 bits que (entre otras cosas) indican la posición de bit del carácter apuntado a dentro de esa palabra.
¿La representación en memoria de todos los punteros para una arquitectura dada es la misma independientemente del tipo de datos al que se apunta?
No necesariamente. Algunos ejemplos:
En cualquiera de las arquitecturas que mencioné anteriormente, los punteros vienen en diferentes tamaños. Entonces, ¿cómo podrían tener la misma representación?
Algunos compiladores en algunos sistemas usan "descriptors" para implementar punteros de caracteres y otros tipos de punteros. Dicho descriptor es diferente para un puntero que apunta al primer "char" en un " char big_array[4000]
" que para un puntero que apunta al primer "char" en un " char small_array[10]
", que posiblemente sean datos diferentes tipos, incluso cuando la matriz pequeña comienza a comenzar exactamente en la misma ubicación en la memoria ocupada previamente por la matriz grande. Los descriptores permiten que tales máquinas atrapen y atrapen los desbordamientos de búfer que causan tales problemas en otras máquinas.
Los "punteros bajos en grasa" utilizados en SAFElite y "procesadores de software" similares tienen "información adicional" análoga sobre el tamaño del búfer al que apunta el puntero. Los punteros bajos en grasa tienen la misma ventaja de atrapar y atrapar los desbordamientos de búfer.
¿La representación en memoria de un puntero es lo mismo que un entero de la misma longitud de bit que la arquitectura?
No necesariamente. Algunos ejemplos:
En las máquinas de "arquitectura etiquetada" , cada palabra de la memoria tiene algunos bits que indican si esa palabra es un número entero, un puntero o algo más. Con tales máquinas, mirar los bits de la etiqueta le diría si esa palabra era un entero o un puntero.
Escuché que las minicomputadoras Nova tienen un "bit de direccionamiento indirecto" en cada palabra que inspiró "código de subproceso indirecto" . Parece que almacenar un entero borra ese bit, mientras que almacenar un puntero establece ese bit.
La multiplicación y división de tipos de datos de puntero solo están prohibidas por el compilador. NOTA: Sí, sé que esto es absurdo. Lo que quiero decir es: ¿hay soporte de hardware para prohibir este uso incorrecto?
Sí, algún hardware no admite directamente tales operaciones.
Como otros ya han mencionado, la instrucción "multiplicar" en el 68000 y el 6809 solo funciona con (algunos) "registros de datos"; no se pueden aplicar directamente a los valores en "registros de dirección". (Sería bastante fácil para un compilador solucionar estas restricciones: mover esos valores de un registro de direcciones al registro de datos apropiado y luego usar MUL).
¿Todos los valores de puntero se pueden convertir en un solo tipo de datos?
Sí.
Para que memcpy () funcione correctamente , el estándar de C obliga a que cada valor de puntero de todo tipo se pueda convertir en un puntero de vacío ("void *").
Se requiere que el compilador haga este trabajo, incluso para arquitecturas que todavía usan segmentos y compensaciones.
¿Todos los valores de puntero se pueden convertir en un solo entero? En otras palabras, ¿qué arquitecturas siguen haciendo uso de los segmentos y las compensaciones?
No estoy seguro.
Sospecho que todos los valores de puntero se pueden convertir a los tipos de datos integrales "size_t" y "ptrdiff_t" definidos en " <stddef.h>
".
Incrementar un puntero es equivalente a agregar sizeof (el tipo de datos puntiagudos) a la dirección de memoria almacenada por el puntero. Si p es un int32 *, p + 1 es igual a la dirección de memoria de 4 bytes después de p.
No está claro lo que estás preguntando aquí.
P: Si tengo una matriz de algún tipo de estructura o tipo de datos primitivos (por ejemplo, " #include <stdint.h> ... int32_t example_array[1000]; ...
"), e incremento un puntero que puntos en esa matriz (por ejemplo, "int32_t p = & example_array [99]; ... p ++; ..."), ¿el puntero ahora apunta al siguiente miembro consecutivo de esa matriz, que es sizeof (los datos en punta)? tipo) bytes más adelante en la memoria?
R: Sí, el compilador debe hacer que el puntero, después de incrementarlo una vez, apunte al siguiente int32_t consecutivo independiente en la matriz, los bytes sizeof (el tipo de datos señalados) más adelante en la memoria, para cumplir con los estándares.
P: Entonces, si p es un int32 *, entonces p + 1 es igual a la dirección de memoria 4 bytes después de p?
R: Cuando sizeof (int32_t) es en realidad igual a 4, sí. De lo contrario, como en ciertas máquinas direccionables por palabra, incluyendo algunos DSP modernos donde sizeof (int32_t) puede ser igual a 2 o incluso 1, entonces p + 1 es igual a la dirección de memoria 2 o incluso 1 "C bytes" después de p.
P: Entonces, si tomo el puntero y lo convierto en un "int" ...
R: Un tipo de "Todo el mundo es una herejía VAX".
P: ... y luego lanza esa "int" de nuevo en un puntero ...
R: Otro tipo de "Todo el mundo es una herejía VAX".
P: Entonces, si tomo el puntero p, que es un puntero a un int32_t, y lo convierto en un tipo integral que es lo suficientemente grande como para contener el puntero, y luego agrego sizeof( int32_t )
a ese tipo integral, y luego sizeof( int32_t )
ese tipo de integral de nuevo en un puntero - cuando hago todo eso, el puntero resultante es igual a p + 1?
No necesariamente.
Muchos DSPs y algunos otros chips modernos tienen direccionamiento orientado a palabras, en lugar del procesamiento orientado a bytes utilizado por chips de 8 bits.
Algunos de los compiladores de C para dichos chips incluyen 2 caracteres en cada palabra, pero se necesitan 2 palabras para mantener int32_t, por lo que informan que sizeof( int32_t )
es 4. (He escuchado rumores de que hay un compilador de C para el Motorola 56000 de 24-bit que hace esto).
Se requiere que el compilador organice las cosas de manera que hacer "p ++" con un puntero a int32_t incremente el puntero al siguiente valor de int32_t. Hay varias formas para que el compilador haga eso.
Una forma compatible con los estándares es almacenar cada puntero a un int32_t como una "dirección de palabra nativa". Debido a que se necesitan 2 palabras para mantener un solo valor int32_t, el compilador de C compila " int32_t * p; ... p++
" en un lenguaje ensamblador que incrementa el valor del puntero en 2. Por otra parte, si ese es el valor " int32_t * p; ... int x = (int)p; x += sizeof( int32_t ); p = (int32_t *)x;
", ese compilador de C para el 56000 lo compilará en lenguaje ensamblador que incrementa el valor del puntero en 4.
Estoy más acostumbrado a los punteros que se utilizan en un espacio de memoria virtual contiguo.
Varios PIC y 8086 y otros sistemas tienen RAM no contigua, unos pocos bloques de RAM en las direcciones que "hicieron el hardware más simple". Con la E / S asignada en memoria o nada adjunto a los espacios en el espacio de direcciones entre esos bloques.
Es aún más incómodo de lo que parece.
En algunos casos, como con el hardware de banda de bits utilizado para evitar problemas causados por la read-modify-write , el mismo bit en la RAM se puede leer o escribir con 2 o más direcciones diferentes.
EDITAR: No responda preguntas cuando su nivel de azúcar en la sangre es bajo. Tu cerebro (ciertamente, el mío) no funciona como esperas. :-(
Nitpick menor:
p es un int32 * entonces p + 1
está mal, debe estar sin signo int32, de lo contrario se ajustará a 2 GB.
Curiosidad interesante: recibí esto del autor del compilador de C para el chip Transputer. Me dijo que para ese compilador, NULL se definió como -2GB. ¿Por qué? Debido a que el Transputer tenía un rango de dirección firmado: -2GB a + 2GB. ¿Puedes creer eso? Increíble no es?
Desde entonces, he conocido a varias personas que me han dicho que la definición de NULL así no funciona. Estoy de acuerdo, pero si no terminas, los punteros NULL se encuentran en el centro de tu rango de direcciones.
¡Creo que la mayoría de nosotros podemos estar contentos de no estar trabajando en Transputers!
En general, la respuesta a todas las preguntas es " sí ", y se debe a que solo las máquinas que implementan lenguajes populares vieron directamente la luz y persistieron en el siglo actual. Aunque los estándares lingüísticos se reservan el derecho de variar estas "invariantes" o afirmaciones, nunca ha ocurrido en productos reales, con la posible excepción de los elementos 3 y 4 que requieren cierta corrección para ser universalmente cierto.
Ciertamente, es posible crear diseños MMU segmentados, que se corresponden aproximadamente con las arquitecturas basadas en capacidades que fueron populares académicamente en los últimos años, pero tal sistema normalmente no se ha visto un uso común con dichas características habilitadas. Un sistema de este tipo podría haber entrado en conflicto con las afirmaciones, ya que probablemente habría tenido grandes punteros.
Además de las MMU segmentadas / de capacidad, que a menudo tienen punteros grandes, los diseños más extremos han intentado codificar los tipos de datos en los punteros. Pocos de estos fueron construidos alguna vez. (Esta pregunta presenta todas las alternativas a las arquitecturas básicas orientadas a la palabra, un puntero es una palabra.)
Específicamente:
- La representación en memoria de todos los punteros para una arquitectura dada es la misma independientemente del tipo de datos al que se apunta. Verdadero, excepto por los diseños pasados extremadamente extravagantes que intentaron implementar la protección no en lenguajes muy tipificados sino en hardware.
- La representación en memoria de un puntero es lo mismo que un entero de la misma longitud de bit que la arquitectura. Tal vez, ciertamente, algún tipo de tipo integral es el mismo, vea LP64 vs LLP64 .
- La multiplicación y división de tipos de datos de puntero solo están prohibidas por el compilador. A la derecha
- Todos los valores de puntero se pueden convertir en un solo entero. En otras palabras, ¿qué arquitecturas siguen haciendo uso de los segmentos y las compensaciones? Hoy en día, nada usa segmentos ni compensaciones, pero a menudo un C
int
es lo suficientemente grande, es posible que necesite unlong
olong long
para sostener un puntero. - Incrementar un puntero es equivalente a agregar sizeof (el tipo de datos puntiagudos) a la dirección de memoria almacenada por el puntero. Si p es un int32 *, p + 1 es igual a la dirección de memoria de 4 bytes después de p. Sí.
Es interesante observar que cada CPU de Intel Architecture, es decir, cada PeeCee individual, contiene una unidad de segmentación elaborada de complejidad épica, legendaria. Sin embargo, está efectivamente desactivado. Cada vez que se inicia un sistema operativo de PC, establece las bases del segmento en 0 y las longitudes del segmento en ~ 0, anulando los segmentos y dando un modelo de memoria plana.
Hubo muchas arquitecturas "dirigidas por palabra" en las décadas de 1950, 1960 y 1970. Pero no puedo recordar ningún ejemplo convencional que tuviera un compilador de C. Recuerdo las máquinas PERL de ICL / Three Rivers en la década de 1980 que tenían una dirección de escritura y un almacén de control de escritura (microcódigo). Una de sus instancias tenía un compilador de C y un sabor de Unix llamado PNX , pero el compilador de C requería un microcódigo especial.
El problema básico es que los tipos de caracteres * en las máquinas dirigidas por palabras son incómodos, sin embargo, los implementa. A menudo sizeof(int *) != sizeof(char *)
con sizeof(int *) != sizeof(char *)
...
Curiosamente, antes de C había un lenguaje llamado BCPL en el que el tipo de puntero básico era una dirección de palabra; es decir, al incrementar un puntero, se le dio la dirección de la siguiente palabra, y ptr!1
le dio la palabra ptr + 1
. Había un operador diferente para direccionar un byte: ptr%42
si recuerdo.
No puedo darte ejemplos concretos de todo esto, pero haré lo mejor que pueda.
sizeof(int *) == sizeof(char *) == sizeof(void *) == sizeof(func_ptr *)
No conozco ningún sistema en el que sepa que esto es falso, pero considere:
Los dispositivos móviles a menudo tienen cierta cantidad de memoria de solo lectura en la que se almacena el código del programa. Los valores de solo lectura (variables const) pueden posiblemente almacenarse en la memoria de solo lectura. Y dado que el espacio de direcciones ROM puede ser más pequeño que el espacio de direcciones RAM normal, el tamaño del puntero también puede ser diferente. Del mismo modo, los punteros a las funciones pueden tener un tamaño diferente, ya que pueden apuntar a esta memoria de solo lectura en la que se carga el programa, y que de lo contrario no pueden modificarse (por lo que sus datos no pueden almacenarse en ella).
Así que no conozco ninguna plataforma en la que haya observado que lo anterior no se cumple, pero puedo imaginar sistemas en los que podría ser el caso.
La representación en memoria de todos los punteros para una arquitectura dada es la misma independientemente del tipo de datos al que se apunta.
Piense en los punteros de los miembros frente a los punteros regulares. No tienen la misma representación (o tamaño). Un puntero de miembro consta de un puntero de this
y un desplazamiento.
Y como se mencionó anteriormente, es posible que algunas CPUs carguen datos constantes en un área separada de la memoria, que utiliza un formato de puntero separado.
La representación en memoria de un puntero es lo mismo que un entero de la misma longitud de bit que la arquitectura.
Depende de cómo se define esa longitud de bit. :) Un int
en muchas plataformas de 64 bits sigue siendo de 32 bits. Pero un puntero es de 64 bits. Como ya se dijo, las CPU con un modelo de memoria segmentada tendrán punteros que consistirán en un par de números. Del mismo modo, los punteros de miembros consisten en un par de números.
La multiplicación y división de tipos de datos de puntero solo están prohibidas por el compilador.
En última instancia, los tipos de datos de punteros solo existen en el compilador. Con lo que trabaja la CPU no son los punteros, sino los enteros y las direcciones de memoria. Por lo tanto, no hay ningún otro lugar donde estas operaciones en los tipos de punteros estén prohibidas. También podría solicitar que la CPU prohíba la concatenación de objetos de cadena C ++. No puede hacerlo porque el tipo de cadena C ++ solo existe en el lenguaje C ++, no en el código de máquina generado.
Sin embargo, para responder a lo que quiere decir , busque las CPUs Motorola 68000. Creo que tienen registros separados para enteros y direcciones de memoria. Lo que significa que pueden prohibir fácilmente tales operaciones sin sentido.
Todos los valores de puntero se pueden convertir en un solo entero.
Estás a salvo allí. Los estándares C y C ++ garantizan que esto siempre sea posible, sin importar el diseño del espacio de memoria, la arquitectura de la CPU y cualquier otra cosa. Específicamente, garantizan un mapeo definido por la implementación . En otras palabras, siempre puede convertir un puntero en un entero, y luego volver a convertir ese entero para obtener el puntero original. Pero los lenguajes C / C ++ no dicen nada acerca de cuál debería ser el valor entero intermedio. Eso depende del compilador individual y del hardware al que se dirige.
Incrementar un puntero es equivalente a agregar sizeof (el tipo de datos puntiagudos) a la dirección de memoria almacenada por el puntero.
De nuevo, esto está garantizado. Si considera eso conceptualmente, un puntero no apunta a una dirección, apunta a un objeto , entonces esto tiene mucho sentido. Añadir uno al puntero obviamente lo hará apuntar al siguiente objeto. Si un objeto tiene 20 bytes de largo, entonces el incremento del puntero lo moverá 20 bytes, de modo que se mueva al siguiente objeto .
Si un puntero era simplemente una dirección de memoria en un espacio de direcciones lineal, si era básicamente un número entero, entonces incrementarlo agregaría 1 a la dirección, es decir, se movería al siguiente byte .
Finalmente, como mencioné en un comentario a su pregunta, tenga en cuenta que C ++ es solo un lenguaje. No importa en qué arquitectura se compila. Muchas de estas limitaciones pueden parecer poco claras en las CPU modernas. Pero, ¿qué pasa si estás apuntando a las CPU de antaño? ¿Qué pasa si estás apuntando a la CPU de la próxima década? Ni siquiera sabes cómo funcionarán, así que no puedes asumir mucho sobre ellos. ¿Qué pasa si estás apuntando a una máquina virtual? Ya existen compiladores que generan códigos de bytes para Flash, listos para ejecutarse desde un sitio web. ¿Qué sucede si desea compilar su código fuente de C ++ a Python?
Mantenerse dentro de las reglas especificadas en la norma garantiza que su código funcionará en todos estos casos.
No sé sobre los otros, pero para DOS, la suposición en el # 3 es falsa. DOS es de 16 bits y utiliza varios trucos para asignar muchos más de 16 bits de memoria.
No tengo en mente ejemplos específicos del mundo real, pero la "autoridad" es el estándar C. Si el estándar no exige algo, puede crear una implementación conforme que intencionalmente no cumpla con otras suposiciones. Algunos de estos supuestos son verdaderos la mayoría del tiempo solo porque es conveniente implementar un puntero como un entero que representa una dirección de memoria que puede ser recuperada directamente por el procesador, pero esto es solo una consecuencia de la "conveniencia" y no se puede mantener como Una verdad universal.
- No requerido por la norma ( ver esta pregunta ). Por ejemplo,
sizeof(int*)
puede ser diferente alsize(double*)
. Se garantiza quevoid*
puede almacenar cualquier valor de puntero. - No requerido por la norma. Por definición, el tamaño es parte de la representación. Si el tamaño puede ser diferente, la representación también puede ser diferente.
- No necesariamente. De hecho, "la longitud de bits de una arquitectura" es una declaración vaga. ¿Qué es un procesador de 64 bits, realmente? ¿Es la dirección del bus? Tamaño de los registros? ¿Bus de datos? ¿Qué?
- No tiene sentido "multiplicar" o "dividir" un puntero. Está prohibido por el compilador pero, por supuesto, puede multiplicar o dividir la representación subyacente (lo que realmente no tiene sentido para mí) y eso resulta en un comportamiento indefinido.
- Tal vez no entiendo tu punto, pero todo en una computadora digital es solo un tipo de número binario.
- Sí; mas o menos. Se garantiza que apunta a una ubicación que es un
sizeof(pointer_type)
más lejos. No es necesariamente equivalente a la suma aritmética de un número (es decir, más lejos es un concepto lógico aquí. La representación real es específica de la arquitectura)
Para 6 .: un puntero no es necesariamente una dirección de memoria. Vea, por ejemplo, " The Great Pointer Conspiracy " del usuario de Desbordamiento de pila jalf :
Sí, utilicé la palabra "dirección" en el comentario anterior. Es importante darse cuenta de lo que quiero decir con esto. No me refiero a "la dirección de memoria en la que se almacenan físicamente los datos", sino simplemente a un resumen "todo lo que necesitamos para localizar el valor. La dirección de i puede ser cualquier cosa, pero una vez que la tenemos, siempre podemos encontrar y modificar i ".
Y:
Un puntero no es una dirección de memoria! Mencioné esto arriba, pero digámoslo de nuevo. El compilador generalmente implementa los punteros simplemente como direcciones de memoria, sí, pero no tienen que serlo ".
Un poco más de información sobre los punteros del estándar C99:
- 6.2.5 §27 garantiza que
void*
ychar*
tienen representaciones idénticas, es decir, se pueden usar de manera intercambiable sin conversión, es decir, la misma dirección se denota con el mismo patrón de bits (que no tiene que ser cierto para otros tipos de punteros) - 6.3.2.3 §1 indica que cualquier puntero a un tipo de objeto o incompleto se puede convertir a (y desde)
void*
y viceversa, y seguir siendo válido; ¡Esto no incluye punteros de función! - 6.3.2.3 §6 indica que
void*
se puede convertir a (y desde) enteros y 7.18.1.4 §1 proporciona los tipos apropiadosintptr_t
yuintptr_t
; El problema: estos tipos son opcionales: ¡el estándar menciona explícitamente que no es necesario que haya un tipo entero lo suficientemente grande como para mantener el valor del puntero!
sizeof(char*) != sizeof(void(*)(void)
? - No en x86 en el modo de direccionamiento de 36 bits (compatible con casi todas las CPU Intel desde Pentium 1)
"La representación en memoria de un puntero es lo mismo que un entero de la misma longitud de bits": no hay representación en memoria en ninguna arquitectura moderna; la memoria etiquetada nunca se ha activado y ya estaba obsoleta antes de que C se estandarizara. De hecho, la memoria ni siquiera contiene números enteros, solo bits y posiblemente palabras (no bytes, la mayoría de la memoria física no le permite leer solo 8 bits).
"La multiplicación de punteros es imposible" - familia 68000; los registros de direcciones (los que tienen punteros) no son compatibles con ese IIRC.
"Todos los punteros se pueden convertir en enteros", no en PIC.
"Incrementar un T * es equivalente a agregar sizeof (T) a la dirección de la memoria": verdadero por definición. También equivalente a &pointer[1]
.