sirven que punteros puntero para operaciones los ejemplo dev declaracion con cadenas aritmetica apuntadores c pointers memory-address

que - ¿Qué es exactamente un puntero C si no una dirección de memoria?



punteros a cadenas (25)

Así es como lo he explicado a algunas personas confundidas en el pasado: un puntero tiene dos atributos que afectan su comportamiento. Tiene un valor , que es (en entornos típicos) una dirección de memoria, y un tipo , que le indica el tipo y tamaño del objeto al que apunta.

Por ejemplo, dado:

union { int i; char c; } u;

Puedes tener tres punteros diferentes, todos apuntando a este mismo objeto:

void *v = &u; int *i = &u.i; char *c = &u.c;

Si comparas los valores de estos punteros, todos son iguales:

v==i && i==c

Sin embargo, si incrementa cada puntero, verá que el tipo al que apuntan se vuelve relevante.

i++; c++; // You can''t perform arithmetic on a void pointer, so no v++ i != c

Las variables i y c tendrán valores diferentes en este punto, porque i++ hace que contenga la dirección del siguiente entero accesible, y c++ hace que c apunte al siguiente carácter direccionable. Normalmente, los enteros ocupan más memoria que caracteres, por lo que terminaré con un valor mayor que c después de que ambos se incrementen.

En una fuente acreditada sobre C, se proporciona la siguiente información después de analizar el operador & :

... Es un poco desafortunado que la terminología [dirección de] se mantenga, porque confunde a los que no saben de qué se tratan las direcciones, y confunde a los que sí lo hacen: pensar en los punteros como si fueran direcciones generalmente lleva al dolor. .

Otros materiales que he leído (de fuentes igualmente reputadas, diría) siempre se han referido sin reparos a los punteros y al operador & como direcciones de memoria. Me encantaría seguir buscando la actualidad del asunto, pero es un poco difícil cuando las fuentes de buena reputación están en desacuerdo.

Ahora estoy un poco confundido: ¿ qué es exactamente un puntero, entonces, si no es una dirección de memoria?

PD

El autor luego dice: ... continuaré usando el término ''dirección de'', porque inventar uno diferente [término] sería aún peor.


Bueno, un puntero es una abstracción que representa una ubicación de memoria. Tenga en cuenta que la cita no dice que pensar en los punteros como si fueran direcciones de memoria es incorrecto, simplemente dice que "generalmente lleva al dolor". En otras palabras, te lleva a tener expectativas incorrectas.

La fuente más probable de dolor es ciertamente la aritmética de punteros, que en realidad es una de las fortalezas de C. Si un puntero fuera una dirección, se esperaría que la aritmética de punteros sea aritmética de direcciones; pero no lo es. Por ejemplo, agregar 10 a una dirección debería darle una dirección que sea mayor en 10 unidades de direccionamiento; pero agregar 10 a un puntero lo incrementa en 10 veces el tamaño del tipo de objeto al que apunta (y ni siquiera el tamaño real, sino redondeado a un límite de alineación). Con un int * en una arquitectura normal con enteros de 32 bits, agregarle 10 incrementos en 40 unidades de direccionamiento (bytes). Los experimentados programadores de C son conscientes de esto y viven con ello, pero su autor evidentemente no es un fanático de las metáforas descuidadas.

Existe la pregunta adicional de cómo el contenido del puntero representa la ubicación de la memoria: como explican muchas de las respuestas, una dirección no siempre es una int (o larga). En algunas arquitecturas, una dirección es un "segmento" más un desplazamiento. Un puntero puede incluso contener solo el desplazamiento en el segmento actual (puntero "cercano"), que por sí solo no es una dirección de memoria única. Y el contenido del puntero puede tener solo una relación indirecta con una dirección de memoria a medida que el hardware la entiende. Pero el autor de la cita citada ni siquiera menciona la representación, por lo que creo que fue la equivalencia conceptual, en lugar de la representación, lo que tenían en mente.


El estándar C no define qué es un puntero internamente y cómo funciona internamente. Esto es intencional para no limitar el número de plataformas, donde C puede implementarse como un lenguaje compilado o interpretado.

Un valor de puntero puede ser algún tipo de ID o identificador o una combinación de varios ID (por ejemplo, hola a segmentos y desplazamientos x86) y no necesariamente una dirección de memoria real. Esta ID podría ser cualquier cosa, incluso una cadena de texto de tamaño fijo. Las representaciones sin dirección pueden ser especialmente útiles para un intérprete de C.


Es difícil decir exactamente qué significan exactamente los autores de esos libros. Si un puntero contiene una dirección o no, depende de cómo defina una dirección y cómo defina un puntero.

A juzgar por todas las respuestas que están escritas, algunas personas asumen que (1) una dirección debe ser un número entero y (2) no es necesario que un puntero sea virtual para que no se indique en la especificación. Con estas suposiciones, entonces los indicadores claramente no necesariamente contienen direcciones.

Sin embargo, vemos que si bien (2) es probablemente cierto, (1) probablemente no tiene que ser cierto. ¿Y qué hacer con el hecho de que la & se llama la dirección del operador según la respuesta de @ CornStalks? ¿Significa esto que los autores de la especificación pretenden que un puntero contenga una dirección?

Entonces, ¿podemos decir que el puntero contiene una dirección, pero una dirección no tiene que ser un número entero? Tal vez.

Creo que todo esto es una charla semántica pediátrica jibberish. Es totalmente inútil prácticamente hablando. ¿Puedes pensar en un compilador que genere código de tal manera que el valor de un puntero no sea una dirección? ¿Entonces qué? Es lo que pensaba...

Creo que el autor del libro (el primer extracto que afirma que los punteros no son necesariamente direcciones) probablemente se refiere al hecho de que un puntero viene con la información de tipo inherente.

Por ejemplo,

int x; int* y = &x; char* z = &x;

ambos y y z son punteros, pero y + 1 y z + 1 son diferentes. Si son direcciones de memoria, ¿esas expresiones no le darían el mismo valor?

Y aquí, en las mentiras, el pensar en los indicadores como si fueran direcciones generalmente lleva al dolor . Se han escrito errores porque la gente piensa en los indicadores como si fueran direcciones , y esto generalmente lleva al dolor .

55555 probablemente no sea un puntero, aunque puede ser una dirección, pero (int *) 55555 es un puntero. 55555 + 1 = 55556, pero (int *) 55555 + 1 es 55559 (+/- diferencia en términos de tamaño de (int)).


Mark Bessey ya lo dijo, pero esto debe volver a enfatizarse hasta que se entienda.

El puntero tiene tanto que ver con una variable como un literal 3.

El puntero es una tupla de un valor (de una dirección) y un tipo (con propiedades adicionales, como solo lectura). El tipo (y los parámetros adicionales si los hay) pueden definir o restringir aún más el contexto; p.ej. __far ptr, __near ptr : cuál es el contexto de la dirección: pila, montón, dirección lineal, desplazamiento desde algún lugar, memoria física o qué.

Es la propiedad de tipo que hace que la aritmética de punteros sea un poco diferente a la aritmética de enteros.

Los ejemplos de contador de un puntero de no ser una variable son demasiados para ignorarlos

  • Fopen devolviendo un puntero de ARCHIVO. (donde esta la variable)
  • el puntero de pila o el puntero de marco son registros típicamente no direccionables

    *(int *)0x1231330 = 13; - Convertir un valor entero arbitrario a un tipo puntero_de_integer y escribir / leer un entero sin nunca haber introducido una variable

Durante la vida útil de un programa C, habrá muchas otras instancias de punteros temporales que no tienen direcciones, y por lo tanto no son variables, sino expresiones / valores con un tipo asociado al tiempo de compilación.


No estoy seguro de su fuente, pero el tipo de lenguaje que describe proviene del estándar C:

6.5.3.2 Operadores de direccionamiento e indirección
[...]
3. El operador unario y produce la dirección de su operando. [...]

Entonces ... sí, los punteros apuntan a direcciones de memoria. Al menos así es como el estándar C sugiere que significa.

Para decirlo un poco más claro, un puntero es una variable que contiene el valor de alguna dirección . La dirección de un objeto (que se puede almacenar en un puntero) se devuelve con el operador unario & .

Puedo almacenar la dirección "42 Wallaby Way, Sydney" en una variable (y esa variable sería una especie de "puntero", pero como esa no es una dirección de memoria, no es algo que llamaríamos correctamente "puntero"). Su computadora tiene direcciones para sus cubos de memoria. Los punteros almacenan el valor de una dirección (es decir, un puntero almacena el valor "42 Wallaby Way, Sydney", que es una dirección).

Edit: Quiero ampliar el comentario de Alexey Frunze.

¿Qué es exactamente un puntero? Veamos el estándar C:

6.2.5 tipos
[...]
20. [...]
Un tipo de puntero se puede derivar de un tipo de función o un tipo de objeto, denominado tipo referenciado . Un tipo de puntero describe un objeto cuyo valor proporciona una referencia a una entidad del tipo referenciado. Un tipo de puntero derivado del tipo de referencia T a veces se llama "puntero a T". La construcción de un tipo de puntero a partir de un tipo referenciado se denomina "derivación de tipo de puntero". Un tipo de puntero es un tipo de objeto completo.

Esencialmente, los punteros almacenan un valor que proporciona una referencia a algún objeto o función. Mas o menos. Los punteros están diseñados para almacenar un valor que proporciona una referencia a algún objeto o función, pero no siempre es así:

6.3.2.3 Punteros
[...]
5. Un entero se puede convertir a cualquier tipo de puntero. Excepto como se especificó anteriormente, el resultado está definido por la implementación, podría no estar correctamente alineado, podría no apuntar a una entidad del tipo referenciado y podría ser una representación de captura.

La cita anterior dice que podemos convertir un entero en un puntero. Si hacemos eso (es decir, si metemos un valor entero en un puntero en lugar de una referencia específica a un objeto o función), entonces el puntero "podría no apuntar a una entidad de tipo de referencia" (es decir, puede no proporcionar un referencia a un objeto o función). Podría proporcionarnos algo más. Y este es un lugar donde puede pegar algún tipo de identificador o ID en un puntero (es decir, el puntero no está apuntando a un objeto; está almacenando un valor que representa algo, pero ese valor puede no ser una dirección).

Así que sí, como dice Alexey Frunze, es posible que un puntero no almacene una dirección en un objeto o función. Es posible que un puntero en su lugar almacene algún tipo de "identificador" o ID, y puede hacer esto asignando algún valor entero arbitrario a un puntero. Lo que representa este identificador o ID depende del sistema / entorno / contexto. Mientras su sistema / implementación pueda dar sentido al valor, usted está en buena forma (pero eso depende del valor específico y del sistema / implementación específico).

Normalmente , un puntero almacena una dirección en un objeto o función. Si no está almacenando una dirección real (para un objeto o función), el resultado es una implementación definida (lo que significa que exactamente lo que sucede y lo que representa el puntero ahora depende de su sistema e implementación, por lo que podría ser un identificador o ID en un sistema en particular, pero usar el mismo código / valor en otro sistema podría bloquear su programa).

Eso terminó siendo más largo de lo que pensé que sería ...


Pensar en un puntero como una dirección es una aproximación . Como todas las aproximaciones, es lo suficientemente bueno como para ser útil a veces, pero tampoco es exacto, lo que significa que confiar en ello causa problemas.

Un puntero es como una dirección en que indica dónde encontrar un objeto. Una limitación inmediata de esta analogía es que no todos los punteros contienen realmente una dirección. NULL es un puntero que no es una dirección. El contenido de una variable de puntero puede, de hecho, ser de uno de tres tipos:

  • la dirección de un objeto, que puede ser anulada (si p contiene la dirección de x entonces la expresión *p tiene el mismo valor que x );
  • un puntero nulo , de los cuales NULL es un ejemplo;
  • contenido no válido , que no apunta a un objeto (si p no tiene un valor válido, entonces *p podría hacer cualquier cosa ("comportamiento indefinido"), con la posibilidad de bloquear el programa como una posibilidad bastante común).

Además, sería más exacto decir que un puntero (si es válido y no nulo) contiene una dirección: un puntero indica dónde encontrar un objeto, pero hay más información relacionada con él.

En particular, un puntero tiene un tipo. En la mayoría de las plataformas, el tipo de puntero no tiene influencia en el tiempo de ejecución, pero tiene una influencia que va más allá del tipo en tiempo de compilación. Si p es un puntero a int ( int *p; ), entonces p + 1 apunta a un entero que es sizeof(int) bytes después de p (suponiendo que p + 1 sigue siendo un puntero válido). Si q es un puntero a char que apunta a la misma dirección que p ( char *q = p; ), entonces q + 1 no es la misma dirección que p + 1 . Si piensa que el puntero es una dirección, no es muy intuitivo que la "siguiente dirección" sea diferente para diferentes punteros a la misma ubicación.

En algunos entornos, es posible tener varios valores de puntero con diferentes representaciones (diferentes patrones de bits en la memoria) que apuntan a la misma ubicación en la memoria. Puede pensar en estos como diferentes punteros que sostienen la misma dirección, o como direcciones diferentes para la misma ubicación; la metáfora no está clara en este caso. El operador == siempre le dice si los dos operandos apuntan a la misma ubicación, por lo que en estos entornos puede tener p == q aunque p y q tengan diferentes patrones de bits.

Incluso hay entornos en los que los punteros llevan otra información más allá de la dirección, como información de tipo o permiso. Puedes pasar fácilmente tu vida como programador sin encontrarlos.

Hay entornos donde diferentes tipos de punteros tienen diferentes representaciones. Puedes considerarlo como diferentes tipos de direcciones que tienen diferentes representaciones. Por ejemplo, algunas arquitecturas tienen punteros de byte y de palabra, o punteros de objeto y punteros de función.

En general, pensar en los punteros como direcciones no es tan malo siempre que se tenga en cuenta que

  • solo son punteros válidos, no nulos, que son direcciones;
  • puede tener varias direcciones para la misma ubicación;
  • no puedes hacer aritmética en direcciones, y no hay orden en ellas;
  • El puntero también lleva información de tipo.

Ir al revés es mucho más problemático. No todo lo que parece una dirección puede ser un puntero . En algún lugar en el fondo, cualquier puntero se representa como un patrón de bits que puede leerse como un entero, y puede decir que este entero es una dirección. Pero si vamos por el otro lado, no todos los enteros son punteros.

Primero hay algunas limitaciones bien conocidas; por ejemplo, un entero que designa una ubicación fuera del espacio de direcciones de su programa no puede ser un puntero válido. Una dirección desalineada no hace un puntero válido para un tipo de datos que requiere alineación; por ejemplo, en una plataforma donde int requiere una alineación de 4 bytes, 0x7654321 no puede ser un valor int* válido.

Sin embargo, va mucho más allá de eso, porque cuando se convierte un puntero en un entero, se encuentra en un mundo de problemas. Una gran parte de este problema es que la optimización de los compiladores es mucho mejor en la microoptimización de lo que la mayoría de los programadores esperan, por lo que su modelo mental de cómo funciona un programa es profundamente erróneo. El hecho de que tenga punteros con la misma dirección no significa que sean equivalentes. Por ejemplo, considere el siguiente fragmento de código:

unsigned int x = 0; unsigned short *p = (unsigned short*)&x; p[0] = 1; printf("%u = %u/n", x, *p);

Podría esperar que en una máquina de ejecución sizeof(int)==4 donde sizeof(int)==4 y sizeof(short)==2 , esto imprima 1 = 1? (little-endian) o 65536 = 1? (Big Endian). Pero en mi PC Linux de 64 bits con GCC 4.4:

$ c99 -O2 -Wall a.c && ./a.out a.c: In function ‘main’: a.c:6: warning: dereferencing pointer ‘p’ does break strict-aliasing rules a.c:5: note: initialized from here 0 = 1?

GCC es lo suficientemente amable como para advertirnos qué está mal en este simple ejemplo; en ejemplos más complejos, el compilador podría no darse cuenta. Como p tiene un tipo diferente de &x , cambiar los puntos a los que no puede afectar a lo que &x apunta (fuera de algunas excepciones bien definidas). Por lo tanto, el compilador tiene la libertad de mantener el valor de x en un registro y no actualizar este registro a medida que cambia *p . El programa hace referencia a dos punteros a la misma dirección y obtiene dos valores diferentes.

La moraleja de este ejemplo es que pensar en un puntero (que no sea nulo válido) como una dirección está bien, siempre y cuando se mantenga dentro de las reglas precisas del lenguaje C. La otra cara de la moneda es que las reglas del lenguaje C son intrincadas y difíciles de obtener para un sentimiento intuitivo a menos que se sepa lo que ocurre debajo del capó. Y lo que sucede debajo del capó es que el vínculo entre los punteros y las direcciones es un tanto flojo, tanto para admitir arquitecturas de procesadores "exóticas" como para permitir la optimización de compiladores.

Así que piense que los punteros son direcciones como un primer paso en su comprensión, pero no siga esa intuición demasiado lejos.


Tienes razón y estás sano. Normalmente, un puntero es solo una dirección, por lo que puede convertirlo en entero y hacer cualquier aritmética.

Pero a veces los punteros son solo una parte de una dirección. En algunas arquitecturas, un puntero se convierte en una dirección con la adición de una base o se utiliza otro registro de CPU .

Pero en estos días, en la arquitectura de PC y ARM con un modelo de memoria plano y lenguaje C compilado de forma nativa, está bien pensar que un puntero es una dirección de entero en algún lugar de la RAM direccionable unidimensional.


Un puntero es una variable que oculta la dirección de memoria, no la dirección en sí. Sin embargo, puede eliminar la referencia de un puntero y obtener acceso a la ubicación de la memoria.

Por ejemplo:

int q = 10; /*say q is at address 0x10203040*/ int *p = &q; /*means let p contain the address of q, which is 0x10203040*/ *p = 20; /*set whatever is at the address pointed by "p" as 20*/

Eso es. Es así de simple.

Un programa para demostrar lo que estoy diciendo y su salida está aquí:

http://ideone.com/rcSUsb

El programa:

#include <stdio.h> int main(int argc, char *argv[]) { /* POINTER AS AN ADDRESS */ int q = 10; int *p = &q; printf("address of q is %p/n", (void *)&q); printf("p contains %p/n", (void *)p); p = NULL; printf("NULL p now contains %p/n", (void *)p); return 0; }


Un puntero, como cualquier otra variable en C, es fundamentalmente una colección de bits que pueden estar representados por uno o más valores unsigned char concatenados (como con cualquier otro tipo de cariable, sizeof(some_variable) indicará el número de valores unsigned char ) . Lo que hace que un puntero sea diferente de otras variables es que un compilador de C interpretará los bits en un puntero como identificando, de alguna manera, un lugar donde se puede almacenar una variable. En C, a diferencia de otros idiomas, es posible solicitar espacio para múltiples variables y luego convertir un puntero a cualquier valor en ese conjunto en un puntero a cualquier otra variable dentro de ese conjunto.

Muchos compiladores implementan punteros utilizando sus bits almacenan las direcciones reales de la máquina, pero esa no es la única implementación posible.Una implementación podría mantener una matriz, no accesible para el código de usuario, que incluya la dirección del hardware y el tamaño asignado de todos los objetos de memoria (conjuntos de variables) que un programa estaba usando, y que cada puntero contenga un índice en una matriz a lo largo con un desplazamiento de ese índice. Un diseño de este tipo permitiría a un sistema no solo restringir el código a operar solo en la memoria que poseía, sino que también garantizaría que un puntero a un elemento de memoria no pudiera convertirse accidentalmente en un puntero a otro elemento de memoria (en un sistema que usa hardware las direcciones, si fooy barson matrices de 10 elementos que se almacenan consecutivamente en la memoria, un puntero al elemento "undécimo" foopodría apuntar al primer elemento debar, pero en un sistema donde cada "puntero" es un ID de objeto y un desplazamiento, el sistema podría interceptarse si el código intentara indexar un puntero foomás allá de su rango asignado. También sería posible que un sistema de este tipo elimine los problemas de fragmentación de la memoria, ya que las direcciones físicas asociadas con cualquier puntero podrían moverse.

Tenga en cuenta que si bien los punteros son un tanto abstractos, no son lo suficientemente abstractos como para permitir que un compilador de C que cumpla con todas las normas implemente un recolector de basura. El compilador de C especifica que cada variable, incluidos los punteros, se representa como una secuencia de unsigned charvalores. Dada cualquier variable, uno puede descomponerlo en una secuencia de números y luego convertir esa secuencia de números nuevamente en una variable del tipo original. En consecuencia, sería posible que un programacallocalgún almacenamiento (que recibe un puntero), almacena algo allí, descompone el puntero en una serie de bytes, los muestra en la pantalla y luego borra todas las referencias a ellos. Si el programa luego aceptó algunos números del teclado, los reconstituyó en un puntero y luego trató de leer los datos desde ese puntero, y si el usuario ingresó los mismos números que el programa había mostrado anteriormente, el programa deberá enviar los datos. que había sido almacenado en la callocmemoria ''ed. Como no es posible que la computadora pueda saber si el usuario ha hecho una copia de los números que se muestran, no sería posible que la computadora pueda saber si se puede acceder a la memoria mencionada en el futuro.


Breve resumen (que también pondré en la parte superior):

(0) Pensar en los punteros como direcciones es a menudo una buena herramienta de aprendizaje y, a menudo, es la implementación real de los punteros a los tipos de datos ordinarios.

(1) Pero en muchos, tal vez en la mayoría de los compiladores, los punteros a las funciones no son direcciones, sino que son más grandes que una dirección (generalmente 2x, a veces más), o en realidad son punteros a una estructura en la memoria que contienen las direcciones de funciones y cosas como una piscina constante.

(2) Los punteros a los miembros de datos y los punteros a los métodos a menudo son incluso más extraños.

(3) Código x86 heredado con problemas de puntero FAR y NEAR

(4) Varios ejemplos, sobre todo el IBM AS / 400, con "punteros de grasa" seguros.

Estoy seguro de que puedes encontrar más.

DETALLE:

UMMPPHHH !!!!! Muchas de las respuestas hasta ahora son respuestas bastante típicas del "programador weenie", pero no del compilador o del hardware. Ya que pretendo ser un weenie de hardware, y a menudo trabajo con los weenies del compilador, permítame agregar mis dos centavos:

En muchos, probablemente la mayoría de los compiladores de C, un puntero a los datos de tipo Tes, de hecho, la dirección de T.

Multa.

Pero, incluso en muchos de estos compiladores, ciertos punteros NO son direcciones. Usted puede decir esto mirando sizeof(ThePointer).

Por ejemplo, los punteros a las funciones a veces son mucho más grandes que las direcciones normales. O bien, pueden implicar un nivel de direccionamiento indirecto. Este artículoproporciona una descripción, que involucra el procesador Intel Itanium, pero he visto otros. Por lo general, para llamar a una función, debe conocer no solo la dirección del código de la función, sino también la dirección del conjunto constante de la función, una región de la memoria desde la cual las constantes se cargan con una única instrucción de carga, en lugar de que el compilador tenga que generar una constante de 64 bits de varias instrucciones de carga inmediata y Shift y OR. Entonces, en lugar de una sola dirección de 64 bits, necesita 2 direcciones de 64 bits. Algunos ABI (Interfaces binarias de aplicación) se mueven alrededor de 128 bits, mientras que otros utilizan un nivel de direccionamiento indirecto, siendo el puntero a la función la dirección de un descriptor de función que contiene las 2 direcciones reales que se acaban de mencionar. ¿Cual es mejor?Depende de su punto de vista: rendimiento, tamaño del código y algunos problemas de compatibilidad: a menudo, el código supone que un puntero se puede convertir en largo o largo, pero también puede suponer que el largo es exactamente de 64 bits. Este código puede no ser compatible con las normas, pero sin embargo, los clientes pueden querer que funcione.

Muchos de nosotros tenemos recuerdos dolorosos de la antigua arquitectura segmentada Intel x86, con PUNTOS CERCANOS y PUNTOS FUERA. Afortunadamente, ya están casi extintos, por lo que solo es un breve resumen: en el modo real de 16 bits, la dirección lineal real era

LinearAddress = SegmentRegister[SegNum].base << 4 + Offset

Mientras que en modo protegido, podría ser

LinearAddress = SegmentRegister[SegNum].base + offset

con la dirección resultante verificada contra un límite establecido en el segmento. Algunos programas no utilizaron realmente las declaraciones de puntero C / C ++ FAR y NEAR, pero muchos simplemente lo dijeron, *Tpero hubo compiladores y conmutadores de vinculador, por ejemplo, los punteros de código podrían estar cerca de los punteros, solo un desplazamiento de 32 bits contra lo que esté en el registro CS (segmento de código), mientras que los punteros de datos pueden ser punteros FAR, que especifican un número de segmento de 16 bits y un desplazamiento de 32 bits para un valor de 48 bits. Ahora, ambas cantidades están ciertamente relacionadas con la dirección, pero como no son del mismo tamaño, ¿cuál de ellas es la dirección? Además, los segmentos también tenían permisos (solo lectura, lectura-escritura, ejecutable) además de cosas relacionadas con la dirección real.

Un ejemplo más interesante, IMHO, es (o, quizás, fue) la familia IBM AS / 400. Esta computadora fue una de las primeras en implementar un sistema operativo en C ++. Los punteros en este tiempo de ejecución eran típicamente 2 veces el tamaño real de la dirección, por ejemplo, como en esta presentacióndice, punteros de 128 bits, pero las direcciones reales eran 48-64 bits, y, nuevamente, algo de información adicional, lo que se llama una capacidad, que proporcionaba permisos como lectura, escritura, así como un límite para evitar el desbordamiento del búfer. Sí: puedes hacer esto de manera compatible con C / C ++, y si esto fuera ubicuo, el PLA chino y la mafia eslava no estarían pirateando muchos sistemas informáticos occidentales. Pero históricamente, la mayoría de la programación en C / C ++ ha descuidado la seguridad para el rendimiento. Lo más interesante es que la familia AS400 permitió al sistema operativo crear punteros seguros, que podrían asignarse a códigos no privilegiados, pero que el código no privilegiado no pudo falsificar ni manipular. Nuevamente, la seguridad, y si bien cumple con los estándares, un código C / C ++ demasiado descuidado que no cumpla con los estándares no funcionará en un sistema tan seguro. Una vez más, hay normas oficiales,y hay normas de facto.

Ahora, saldré de mi jabonera de seguridad y mencionaré algunas otras formas en que los punteros (de varios tipos) a menudo no son direcciones: los punteros a miembros de datos, los punteros a métodos de funciones de miembros y las versiones estáticas de los mismos son más grandes direccion ordinaria Como dice esta publicación :

Hay muchas maneras de resolver esto [problemas relacionados con la herencia simple o múltiple y la herencia virtual]. Así es como el compilador de Visual Studio decide manejarlo: un puntero a una función miembro de una clase de herencia múltiple es realmente una estructura ". Y continúan diciendo" ¡El puntero de una función puede cambiar su tamaño! ".

Como probablemente pueda adivinar por mi compromiso con la seguridad (in), he estado involucrado en proyectos de hardware / software C / C ++ donde un puntero fue tratado más como una capacidad que como una dirección en bruto.

Podría seguir, pero espero que se te ocurra.

Breve resumen (que también pondré en la parte superior):

(0) pensar en los punteros como direcciones suele ser una buena herramienta de aprendizaje y, a menudo, es la implementación real de los punteros a los tipos de datos comunes.

(1) Pero en muchos, tal vez en la mayoría de los compiladores, los punteros a las funciones no son direcciones, sino que son más grandes que una dirección (generalmente 2X, a veces más), o en realidad son punteros a una estructura en la memoria que contienen las direcciones de funciones y cosas como una piscina constante.

(2) Los punteros a los miembros de datos y los punteros a los métodos a menudo son incluso más extraños.

(3) Código x86 heredado con problemas de puntero FAR y NEAR

(4) Varios ejemplos, sobre todo el IBM AS / 400, con "punteros de grasa" seguros.

Estoy seguro de que puedes encontrar más.


El puntero de CA es muy similar a una dirección de memoria pero con detalles dependientes de la máquina extraídos, así como algunas características que no se encuentran en el conjunto de instrucciones de nivel inferior.

Por ejemplo, un puntero C es relativamente rico. Si incrementa un puntero a través de una matriz de estructuras, salta muy bien de una estructura a la otra.

Los punteros están sujetos a las reglas de conversión y proporcionan la verificación del tipo de tiempo de compilación.

Existe un valor especial de "puntero nulo" que es portátil en el nivel del código fuente, pero cuya representación puede diferir. Si asigna una constante entera cuyo valor es cero a un puntero, ese puntero toma el valor de puntero nulo. Lo mismo ocurre si inicializa un puntero de esa manera.

Un puntero se puede utilizar como una variable booleana: prueba verdadero si es distinto de nulo, y falso si es nulo.

En un lenguaje de máquina, si el puntero nulo es una dirección divertida como 0xFFFFFFFF, es posible que tenga que realizar pruebas explícitas para ese valor. C oculta eso de ti. Incluso si el puntero nulo es 0xFFFFFFFF, puede probarlo usando if (ptr != 0) { /* not null! */}.

Los usos de los punteros que subvierten el sistema de tipos conducen a un comportamiento indefinido, mientras que un código similar en lenguaje de máquina podría estar bien definido. Los ensambladores ensamblarán las instrucciones que ha escrito, pero los compiladores de C se optimizarán basándose en la suposición de que no ha hecho nada malo. Si un float *ppuntero apunta a una long nvariable y *p = 0.0se ejecuta, no se requiere que el compilador maneje esto. Un uso posterior de nno necesariamente leerá el patrón de bits del valor flotante, pero quizás sea un acceso optimizado que se basa en el supuesto de "alias estricto" que nno se ha tocado. Es decir, la suposición de que el programa se comporta bien y, por plo tanto , no debe señalarse n.

En C, los punteros a código y los punteros a datos son diferentes, pero en muchas arquitecturas, las direcciones son las mismas. Se pueden desarrollar compiladores de C que tengan punteros "gruesos", aunque la arquitectura de destino no los tenga. Los punteros de red significan que los punteros no son solo direcciones de máquinas, sino que contienen otra información, como información sobre el tamaño del objeto al que se apunta, para la verificación de límites. Los programas escritos con portabilidad fácilmente se trasladarán a tales compiladores

Como puede ver, hay muchas diferencias semánticas entre las direcciones de las máquinas y los punteros C.


Resumen rápido: la dirección de CA es un valor, típicamente representado como una dirección de memoria a nivel de máquina, con un tipo específico.

La palabra no calificada "puntero" es ambigua. C tiene objetos de puntero (variables), tipos de puntero , expresiones de puntero y valores de puntero .

Es muy común usar la palabra "puntero" para significar "objeto puntero", y eso puede llevar a cierta confusión, por lo que trato de usar "puntero" como adjetivo en lugar de como un sustantivo.

El estándar C, al menos en algunos casos, usa la palabra "puntero" para significar "valor de puntero". Por ejemplo, la descripción de malloc dice que "devuelve un puntero nulo o un puntero al espacio asignado".

Entonces, ¿qué es una dirección en C? Es un valor de puntero, es decir, un valor de algún tipo de puntero en particular. (Excepto que un valor de puntero nulo no se refiere necesariamente como una "dirección", ya que no es la dirección de nada).

La descripción de la norma del &operador unario dice que "proporciona la dirección de su operando". Fuera de la norma C, la palabra "dirección" se usa comúnmente para referirse a una dirección de memoria (física o virtual), generalmente una palabra en tamaño (cualquiera que sea la "palabra" en un sistema determinado).

La "dirección" de CA se implementa normalmente como una dirección de máquina, al igual que un intvalor de C se implementa normalmente como una palabra de máquina. Pero una dirección C (valor de puntero) es más que una dirección de máquina. Es un valor típicamente representado como una dirección de máquina, y es un valor con algún tipo específico .


Un puntero es solo otra variable que se usa para mantener la dirección de una ubicación de memoria (generalmente la dirección de memoria de otra variable).


En esta imagen,

pointer_p es un puntero que se encuentra en 0x12345 y apunta a una variable variable_v en 0x34567.


Ahora que lo pienso, creo que es una cuestión de semántica. No creo que el autor tenga razón, ya que el estándar C se refiere a un puntero que contiene una dirección al objeto referenciado como otros ya han mencionado aquí. Sin embargo, dirección! = Dirección de memoria. Una dirección puede ser realmente cualquier cosa según el estándar C, aunque eventualmente conducirá a una dirección de memoria, el puntero en sí mismo puede ser una identificación, un selector de desplazamiento + (x86), realmente cualquier cosa siempre que pueda describir (después del mapeo) cualquier memoria Dirección en el espacio direccionable.


Antes de entender los punteros necesitamos entender los objetos. Los objetos son entidades que existen y tienen un especificador de ubicación llamado dirección. Un puntero es solo una variable como cualquier otra variable Ccon un tipo llamado pointercuyo contenido se interpreta como la dirección de un objeto que admite la siguiente operación.

+ : A variable of type integer (usually called offset) can be added to yield a new pointer - : A variable of type integer (usually called offset) can be subtracted to yield a new pointer : A variable of type pointer can be subtracted to yield an integer (usually called offset) * : De-referencing. Retrieve the value of the variable (called address) and map to the object the address refers to. ++: It''s just `+= 1` --: It''s just `-= 1`

Un puntero se clasifica según el tipo de objeto al que se refiere actualmente. La única parte de la información que importa es el tamaño del objeto.

Cualquier objeto admite una operación, &(dirección de), que recupera el especificador de ubicación (dirección) del objeto como un tipo de objeto puntero. Esto debería disminuir la confusión que rodea la nomenclatura, ya que tendría sentido llamar &como una operación de un objeto en lugar de un puntero cuyo tipo resultante es un puntero del tipo de objeto.

Nota A lo largo de esta explicación, he dejado de lado el concepto de memoria.


Dice "porque confunde a aquellos que no saben de qué se tratan las direcciones", también es cierto: si aprendes de qué se tratan, no estarás confundido. Teóricamente, el puntero es una variable que apunta a otra, prácticamente contiene una dirección, que es la dirección de la variable a la que apunta. No sé por qué debería ocultar este hecho, no es una ciencia espacial. Si entiendes los punteros, estarás un paso más cerca para entender cómo funcionan las computadoras. ¡Adelante!


Otra forma en la que un puntero C o C ++ difiere de una dirección de memoria simple debido a los diferentes tipos de punteros que no he visto en las otras respuestas (aunque dado su tamaño total, es posible que lo haya pasado por alto). Pero probablemente sea el más importante, porque incluso los programadores experimentados de C / C ++ pueden tropezar con él:

El compilador puede asumir que los punteros de tipos incompatibles no apuntan a la misma dirección, incluso si lo hacen claramente, lo que puede dar un comportamiento que no sería posible con un puntero simple == modelo de dirección. Considere el siguiente código (asumiendo sizeof(int) = 2*sizeof(short)):

unsigned int i = 0; unsigned short* p = (unsigned short*)&i; p[0]=p[1]=1; if (i == 2 + (unsigned short)(-1)) { // you''d expect this to execute, but it need not } if (i == 0) { // you''d expect this not to execute, but it actually may do so }

Tenga en cuenta que existe una excepción char*, por lo que char*es posible manipular los valores (aunque no es muy portátil).


Puedes verlo de esta manera. Un puntero es un valor que representa una dirección en el espacio de memoria direccionable.


Simplemente decir que los punteros son en realidad parte del mecanismo de segmentación que se traduce en Dirección lineal después de la segmentación y luego en Dirección física después de la paginación. Las direcciones físicas se dirigen desde su memoria RAM.

Selector +--------------+ +-----------+ ---------->| | | | | Segmentation | ------->| Paging | Offset | Mechanism | | Mechanism | ---------->| | | | +--------------+ +-----------+ Virtual Linear Physical


Un puntero es solo otra variable que puede contener la dirección de memoria generalmente de otra variable. Un puntero es una variable que también tiene una dirección de memoria.


Un puntero es un tipo de variable que está disponible de forma nativa en C / C ++ y contiene una dirección de memoria. Como cualquier otra variable, tiene una dirección propia y ocupa memoria (la cantidad es específica de la plataforma).

Un problema que verá como resultado de la confusión es tratar de cambiar el referente dentro de una función simplemente pasando el puntero por valor. Esto hará una copia del puntero en el alcance de la función y cualquier cambio en donde este nuevo puntero "puntos" no cambiará el referente del puntero en el alcance que invocó la función. Para modificar el puntero real dentro de una función, normalmente se pasaría un puntero a un puntero.


Un valor de puntero es una dirección. Una variable de puntero es un objeto que puede almacenar una dirección. Esto es cierto porque eso es lo que el estándar define como puntero. Es importante decírselo a los novatos en C porque los novatos en C a menudo no tienen claro la diferencia entre un puntero y la cosa que apunta (es decir, no saben la diferencia entre un sobre y un edificio). La noción de una dirección (cada objeto tiene una dirección y eso es lo que almacena un puntero) es importante porque lo soluciona.

Sin embargo, el estándar habla a un nivel particular de abstracción. Esas personas de las que habla el autor que "saben de qué se tratan las direcciones", pero que son nuevas en C, necesariamente deben haber aprendido sobre las direcciones en un nivel diferente de abstracción, tal vez programando el lenguaje ensamblador. No hay garantía de que la implementación de C use la misma representación para las direcciones que usan los códigos de operación de la CPU (conocida como "la dirección de la tienda" en este pasaje), que estas personas ya conocen.

Continúa hablando sobre "manipulación de dirección perfectamente razonable". En lo que respecta al estándar C, básicamente no existe tal cosa como "manipulación de dirección perfectamente razonable". La adición se define en los punteros y eso es básicamente eso. Claro, puedes convertir un puntero en entero, hacer algunas operaciones de modo bit a bit o aritméticas, y luego convertirlo de nuevo. Esto no garantiza que funcione según el estándar, por lo que antes de escribir ese código, es mejor que sepa cómo su implementación C en particular representa los punteros y realiza esa conversión. Es probable que utiliza la representación dirección esperada, pero no que es su culpa, porque usted no leyó el manual. Eso no es confusión, es un procedimiento de programación incorrecto ;-)

En resumen, C usa un concepto más abstracto de una dirección que el autor.

El concepto del autor de una dirección, por supuesto, tampoco es la palabra de más bajo nivel sobre el tema. Lo que con los mapas de memoria virtual y el direccionamiento RAM físico a través de múltiples chips, el número que le dice a la CPU es "la dirección de la tienda" a la que desea acceder, básicamente no tiene nada que ver con la ubicación de los datos que desea en el hardware. Es todas las capas de direccionamiento indirecto y representación, pero el autor ha elegido uno para privilegiarlo. Si vas a hacer eso cuando hablas de C, ¡ elige el nivel C para privilegiar !

Personalmente, no creo que las observaciones del autor sean tan útiles, excepto en el contexto de la introducción de C a los programadores de ensamblajes. Ciertamente no es útil para aquellos que vienen de lenguajes de nivel superior decir que los valores de puntero no son direcciones. Sería mucho mejor reconocer la complejidad que decir que la CPU tiene el monopolio de decir qué es una dirección y, por lo tanto, que los valores del puntero C "no son" direcciones. Son direcciones, pero pueden estar escritas en un idioma diferente de las direcciones que él quiere decir. Distinguir las dos cosas en el contexto de C como "dirección" y "dirección de la tienda" sería adecuado, creo.


Una dirección se usa para identificar un pedazo de almacenamiento de tamaño fijo, generalmente para cada byte, como un entero. Esto se llama precisamente como dirección de bytes , que también es utilizada por la ISO C. Puede haber otros métodos para construir una dirección, por ejemplo, para cada bit. Sin embargo, solo la dirección de bytes se utiliza con tanta frecuencia, por lo general, omitimos "bytes".

Técnicamente, una dirección nunca es un valor en C, porque la definición del término "valor" en (ISO) C es:

significado preciso de los contenidos de un objeto cuando se interpreta que tiene un tipo específico

(Enfatizado por mí). Sin embargo, no hay tal "tipo de dirección" en C.

El puntero no es lo mismo. El puntero es un tipo de tipo en el lenguaje C. Hay varios tipos de punteros distintos. Ellos no necesariamente obedecen a conjunto idéntico de reglas de la lengua, por ejemplo, el efecto de ++un valor de tipo int*vs char*.

Un valor en C puede ser de un tipo de puntero. Esto se llama un valor de puntero . Para que quede claro, un valor de puntero no es un puntero en el lenguaje C. Pero estamos acostumbrados a mezclarlos juntos, porque en C no es probable que sea ambiguo: si llamamos a una expresión pcomo un "puntero", es simplemente un valor de puntero pero no un tipo, ya que un tipo nombrado en C no es expresado por una expresión , pero por un nombre de tipo o un nombre de typedef .

Algunas otras cosas son sutiles. Como usuario de C, en primer lugar, uno debe saber qué objectsignifica:

Región de almacenamiento de datos en el entorno de ejecución, cuyos contenidos pueden representar valores

Un objeto es una entidad para representar valores, que son de un tipo específico. Un puntero es un tipo de objeto . Entonces, si declaramos int* p;, entonces psignifica "un objeto de tipo puntero", o un "objeto puntero".

Tenga en cuenta que no hay una "variable" definida normativamente por la norma (de hecho, nunca se está utilizando como sustantivo por ISO C en el texto normativo). Sin embargo, informalmente, llamamos variable a un objeto, como lo hace otro lenguaje. (Pero aún no tan exactamente, por ejemplo, en C ++, una variable puede ser de tipo de referencia normativamente, que no es un objeto). Las frases "objeto puntero" o "variable puntero" a veces se tratan como "valor puntero" como anteriormente, con una probable ligera diferencia. (Un conjunto más de ejemplos es "array".)

Como el puntero es un tipo y la dirección es efectivamente "sin tipo de letra" en C, un valor de puntero aproximadamente "contiene" una dirección. Y una expresión de tipo puntero puede producir una dirección, por ejemplo,

ISO C11 6.5.2.3

3 El &operador unario da la dirección de su operando.

Tenga en cuenta que esta frase es introducida por WG14 / N1256, es decir, ISO C99: TC3. En C99 hay

3 El &operador unario devuelve la dirección de su operando.

Refleja la opinión del comité: una dirección no es un valor de puntero devuelto por el &operador unario .

A pesar de la redacción anterior, todavía hay algunos problemas incluso en los estándares.

ISO C11 6.6

9 Una constante de dirección es un puntero nulo, un puntero a un valor l que designa un objeto de duración de almacenamiento estático o un puntero a un designador de función

ISO C ++ 11 5.19

3 ... Una expresión de constante de dirección es una expresión de constante de núcleo de valor nominal de tipo puntero que se evalúa a la dirección de un objeto con una duración de almacenamiento estático, a la dirección de una función, a un valor de puntero nulo, o una expresión de constante de núcleo de valor predeterminado de tipo std::nullptr_t. ...

(El borrador estándar reciente de C ++ usa otra redacción, por lo que no hay este problema).

En realidad, tanto la "constante de dirección" en C como la "expresión de constante de dirección" en C ++ son expresiones constantes de tipos de puntero (o al menos tipos "punteros" desde C ++ 11).

Y el &operador unario incorporado se llama "dirección de" en C y C ++; Del mismo modo, std::addressofse introduce en C ++ 11.

Estos nombres pueden traer la idea falsa. La expresión resultado es de tipo puntero, por lo que había interpretarse como: el resultado contiene / produce una dirección, en lugar de es una dirección.