Recientemente tuve el placer de explicarle los consejos a un principiante de programación en C y me topé con la siguiente dificultad. Puede que no parezca un problema en absoluto si ya sabe cómo usar punteros, pero intente ver el siguiente ejemplo con una mente clara:
int foo = 1;
int *bar = &foo;
printf("%p\n", (void *)&foo);
printf("%i\n", *bar);
Para el principiante absoluto, la salida puede ser sorprendente. En la línea 2 él / ella acababa de declarar * bar como be & foo, pero en la línea 4 resulta que * bar es realmente foo en lugar de & foo!
La confusión, se podría decir, proviene de la ambigüedad del símbolo *: en la línea 2 se usa para declarar un puntero. En la línea 4 se usa como operador unario que obtiene el valor al que apunta el puntero. Dos cosas diferentes, ¿verdad?
Sin embargo, esta "explicación" no ayuda en absoluto a un principiante. Introduce un nuevo concepto al señalar una discrepancia sutil. Esta no puede ser la forma correcta de enseñarlo.
Entonces, ¿cómo lo explicaron Kernighan y Ritchie?
El operador unario * es el operador de indirección o desreferenciación; cuando se aplica a un puntero, accede al objeto al que apunta el puntero. [...]
La declaración del puntero ip,
int *ip
pretende ser un mnemotécnico; dice que la expresión*ip
es un int. La sintaxis de la declaración para una variable imita la sintaxis de las expresiones en las que puede aparecer la variable .
int *ip
debe leerse como " *ip
devolverá un int
"? Pero, ¿por qué la asignación después de la declaración no sigue ese patrón? ¿Qué pasa si un principiante quiere inicializar la variable? int *ip = 1
(léase: *ip
devolverá un int
y el int
is 1
) no funcionará como se esperaba. El modelo conceptual simplemente no parece coherente. ¿Me estoy perdiendo de algo?
Editar: Intentó resumir las respuestas aquí .
*
en una declaración es un token que significa "declarar un puntero", en expresiones es el operador de desreferencia, y que estos dos representan cosas diferentes que tienen el mismo símbolo (igual que el operador de multiplicación - mismo símbolo, significado diferente). Es confuso, pero cualquier cosa diferente al estado actual de las cosas será aún peor.int* bar
hace que sea más obvio que la estrella es en realidad parte del tipo, no parte del identificador. Por supuesto, esto te lleva a diferentes problemas con cosas poco intuitivas comoint* a, b
.*
puede tener dos significados diferentes según el contexto. Al igual que la misma letra se puede pronunciar de manera diferente según la palabra en la que se encuentre, lo que dificulta aprender a hablar muchos idiomas. Si cada concepto / operación tuviera su propio símbolo, necesitaríamos teclados mucho más grandes, por lo que los símbolos se reciclan cuando tiene sentido hacerlo.int* p
), al tiempo que advierte a su estudiante que no use declaraciones múltiples en la misma línea cuando hay punteros involucrados. Cuando el alumno haya entendido completamente el concepto de punteros, explíquele que laint *p
sintaxis es equivalente y luego explique el problema con varias declaraciones.Respuestas:
Para que su alumno entienda el significado del
*
símbolo en diferentes contextos, primero debe comprender que los contextos son realmente diferentes. Una vez que comprenden que los contextos son diferentes (es decir, la diferencia entre el lado izquierdo de una tarea y una expresión general), no es un gran salto cognitivo comprender cuáles son las diferencias.Primero explique que la declaración de una variable no puede contener operadores (demuestre esto mostrando que poner un símbolo
-
o+
en una declaración de variable simplemente causa un error). Luego continúe para mostrar que una expresión (es decir, en el lado derecho de una asignación) puede contener operadores. Asegúrese de que el alumno comprenda que una expresión y una declaración de variable son dos contextos completamente diferentes.Cuando entienden que los contextos son diferentes, puede continuar explicando que cuando el
*
símbolo está en una declaración de variable frente al identificador de variable, significa 'declarar esta variable como un puntero'. Luego puede explicar que cuando se usa en una expresión (como operador unario) el*
símbolo es el 'operador de desreferencia' y significa 'el valor en la dirección de' en lugar de su significado anterior.Para convencer verdaderamente a su estudiante, explique que los creadores de C podrían haber usado cualquier símbolo para referirse al operador de desreferenciación (es decir, podrían haberlo usado en su
@
lugar) pero por cualquier razón tomaron la decisión de diseño*
.Con todo, no hay forma de explicar que los contextos son diferentes. Si el estudiante no entiende que los contextos son diferentes, no puede entender por qué el
*
símbolo puede significar cosas diferentes.fuente
La razón por la taquigrafía:
en su ejemplo puede ser confuso es que es fácil interpretarlo mal como equivalente a:
cuando en realidad significa:
Escrito de esta manera, con la declaración variable y la asignación separadas, no existe tal posibilidad de confusión, y el uso del paralelismo de declaración ↔ descrito en su cita de K&R funciona perfectamente:
La primera línea declara una variable
bar
, tal que*bar
es unint
.La segunda línea asigna la dirección de
foo
abar
, haciendo que*bar
(anint
) sea un alias parafoo
(también unint
).Al presentar la sintaxis del puntero C a los principiantes, puede ser útil mantener inicialmente este estilo de separar las declaraciones del puntero de las asignaciones, y solo introducir la sintaxis abreviada combinada (con advertencias apropiadas sobre su potencial de confusión) una vez que los conceptos básicos del puntero se utilizan en C se ha internalizado adecuadamente.
fuente
typedef
.typedef int *p_int;
significa que una variable de tipop_int
tiene la propiedad que*p_int
es unint
. Entonces tenemosp_int bar = &foo;
. Animar a cualquiera a crear datos no inicializados y luego asignarlos como un hábito predeterminado parece ... una mala idea.int a[2] = {47,11};
, eso no es una inicialización del elemento (inexistente)a[2]
eiher.*
debería ser parte del tipo, no estar vinculado a la variable, y luego podría escribirint* foo_ptr, bar_ptr
para declarar dos punteros. Pero en realidad declara un puntero y un número entero.Corto en declaraciones
Es bueno saber la diferencia entre declaración e inicialización. Declaramos variables como tipos y las inicializamos con valores. Si hacemos ambas cosas al mismo tiempo, a menudo lo llamamos definición.
1.
int a; a = 42;
Nosotros declaramos una
int
llamada de una . Luego lo inicializamos dándole un valor42
.2.
int a = 42;
nos declaramos y
int
nombramos una y le damos el valor 42. Se inicializa con42
. Una definicion.3.
a = 43;
Cuando usamos las variables, decimos que las operamos .
a = 43
Es una operación de asignación. Asignamos el número 43 a la variable a.Diciendo
declaramos que bar es un puntero a un int. Diciendo
declaramos bar e inicializamos con la dirección de foo .
Después de haber inicializado barra , podemos usar el mismo operador, el asterisco, para acceder y operar con el valor de foo . Sin el operador, accedemos y operamos en la dirección a la que apunta el puntero.
Además de eso, dejé que la imagen hablara.
Qué
Una ASCIIMACIÓN simplificada sobre lo que está sucediendo. (Y aquí una versión del reproductor si quieres pausar, etc.)
fuente
La segunda declaración
int *bar = &foo;
se puede ver gráficamente en la memoria como,Ahora
bar
es un puntero de tipo queint
contiene la dirección&
defoo
. Usando el operador unario*
, deferencia para recuperar el valor contenido en 'foo' usando el punterobar
.EDITAR : Mi enfoque con los principiantes es explicar el
memory address
de una variable, es decirMemory Address:
Cada variable tiene una dirección asociada proporcionada por el sistema operativo. Enint a;
,&a
es la dirección de la variablea
.Continúe explicando los tipos básicos de variables en
C
as,Types of variables:
Las variables pueden contener valores de tipos respectivos pero no direcciones.Introducing pointers:
Como se dijo anteriormente, las variables, por ejemploEs posible asignar
b = a
pero nob = &a
, ya que la variableb
puede contener valor pero no dirección, por lo tanto, necesitamos punteros .Pointer or Pointer variables :
Si una variable contiene una dirección, se conoce como variable puntero. Use*
en la declaración para informar que es un puntero.fuente
int *ip
como "ip es un puntero (*) de tipo int" se mete en problemas al leer algo asíx = (int) *ip
.x = (int) *ip;
, obtenga el valor desreferenciando el punteroip
y convierta el valor enint
cualquier tipoip
.int* bar = &foo;
hace que las cargas más sentido. Sí, sé que causa problemas cuando declaras múltiples punteros en una sola declaración. No, no creo que eso importe en absoluto.Mirando las respuestas y los comentarios aquí, parece haber un acuerdo general de que la sintaxis en cuestión puede ser confusa para un principiante. La mayoría de ellos proponen algo en este sentido:
Puede escribir en
int* bar
lugar deint *bar
resaltar la diferencia. Esto significa que no seguirá el enfoque de K&R "declaración imita el uso", pero el enfoque Stroustrup C ++ :No declaramos
*bar
ser un número entero. Declaramosbar
ser unint*
. Si queremos inicializar una variable recién creada en la misma línea, está claro que estamos tratandobar
, no*bar
.int* bar = &foo;
Los inconvenientes:
int* foo, bar
vsint *foo, *bar
).Editar: Un enfoque diferente que se ha sugerido es ir a la forma de "imitación" de K&R, pero sin la sintaxis de "taquigrafía" (ver aquí ). Tan pronto como omita hacer una declaración y una asignación en la misma línea , todo se verá mucho más coherente.
Sin embargo, tarde o temprano el alumno tendrá que lidiar con punteros como argumentos de función. Y punteros como tipos de retorno. Y punteros a funciones. Tendrás que explicar la diferencia entre
int *func();
yint (*func)();
. Creo que tarde o temprano las cosas se vendrán abajo. Y quizás más pronto es mejor que más tarde.fuente
Hay una razón por la cual los favores de estilo K&R
int *p
y los favores de estilo Stroustrupint* p
; ambos son válidos (y significan lo mismo) en cada idioma, pero como dijo Stroustrup:Ahora, ya que está tratando de enseñar C aquí, eso sugeriría que debería enfatizar expresiones más que tipos, pero algunas personas pueden asimilar más fácilmente un énfasis más rápido que el otro, y eso se trata de ellos en lugar del lenguaje.
Por lo tanto, a algunas personas les resultará más fácil comenzar con la idea de que un
int*
es algo diferente de unint
y partir de allí.Si alguien asimila rápidamente la forma de mirarlo que usa
int* bar
que tienenbar
como una cosa que no es un entero, sino un puntero paraint
, a continuación, van a ver rápidamente que*bar
está haciendo algo abar
, y el resto seguirá. Una vez que hayas hecho eso, puedes explicar por qué los codificadores C tienden a preferirint *bar
.O no. Si hubiera una manera de que todos entendieran por primera vez el concepto, no habría tenido ningún problema en primer lugar, y la mejor manera de explicárselo a una persona no será necesariamente la mejor manera de explicárselo a otra.
fuente
int* p = &a
entonces podemos hacerint* r = *p
. Estoy bastante seguro de que lo cubrió en The Design and Evolution of C ++ , pero ha pasado mucho tiempo desde que lo leí, y tontamente le presté mi copia a alguien.int& r = *p
. Y apuesto a que el prestatario todavía está tratando de digerir el libro.Var A, B: ^Integer;
deja en claro que el tipo "puntero al entero" se aplica a ambosA
yB
. Usar unK&R
estiloint *a, *b
también es viable; sino una declaración comoint* a,b;
, sin embargo, parece como sia
yb
están ambas están declaradas comoint*
, pero en realidad se declaraa
como unaint*
yb
comoint
.tl; dr:
A: no lo hagas. Explique los punteros al principiante y muéstreles cómo representar sus conceptos de puntero en la sintaxis C después.
OMI, la sintaxis de C no es horrible, pero tampoco es maravillosa: no es un gran obstáculo si ya entiendes los punteros, ni ninguna ayuda para aprenderlos.
Por lo tanto: comience explicando los punteros y asegúrese de que realmente los entiendan:
Explíquelos con diagramas de caja y flecha. Puede hacerlo sin direcciones hexadecimales, si no son relevantes, solo muestre las flechas que apuntan a otro cuadro o a algún símbolo nulo.
Explique con pseudocódigo: simplemente escriba la dirección de foo y el valor almacenado en la barra .
Luego, cuando su novato comprenda qué son los punteros, por qué y cómo usarlos; luego muestre el mapeo en la sintaxis de C.
Sospecho que la razón por la cual el texto de K&R no proporciona un modelo conceptual es que ya entendieron los punteros , y probablemente asumieron que cualquier otro programador competente en ese momento también lo hizo. El mnemotécnico es solo un recordatorio del mapeo desde el concepto bien entendido hasta la sintaxis.
fuente
Este problema es algo confuso al comenzar a aprender C.
Estos son los principios básicos que pueden ayudarlo a comenzar:
Solo hay unos pocos tipos básicos en C:
char
: un valor entero con el tamaño de 1 byte.short
: un valor entero con el tamaño de 2 bytes.long
: un valor entero con un tamaño de 4 bytes.long long
: un valor entero con un tamaño de 8 bytes.float
: un valor no entero con un tamaño de 4 bytes.double
: un valor no entero con un tamaño de 8 bytes.Tenga en cuenta que el tamaño de cada tipo generalmente está definido por el compilador y no por el estándar.
Los tipos enteros
short
,long
ylong long
generalmente son seguidos porint
.Sin embargo, no es imprescindible y puede usarlos sin el
int
.Alternativamente, puede simplemente indicar
int
, pero eso podría ser interpretado de manera diferente por diferentes compiladores.Para resumir esto:
short
es lo mismoshort int
pero no necesariamente lo mismo queint
.long
es lo mismolong int
pero no necesariamente lo mismo queint
.long long
es lo mismolong long int
pero no necesariamente lo mismo queint
.En un compilador dado,
int
es unoshort int
olong int
olong long int
.Si declara una variable de algún tipo, también puede declarar otra variable apuntando a ella.
Por ejemplo:
int a;
int* b = &a;
Entonces, en esencia, para cada tipo básico, también tenemos un tipo de puntero correspondiente.
Por ejemplo:
short
yshort*
.Hay dos formas de "mirar" la variable
b
(eso es lo que probablemente confunde a la mayoría de los principiantes) :Se puede considerar
b
como una variable de tipoint*
.Se puede considerar
*b
como una variable de tipoint
.Por lo tanto, algunas personas declararían
int* b
, mientras que otras declararíanint *b
.Pero el hecho es que estas dos declaraciones son idénticas (los espacios no tienen sentido).
Puede usarlo
b
como puntero a un valor entero o*b
como el valor entero puntiagudo real.Puede obtener (leer) el valor señalado:
int c = *b
.Y se puede establecer (escribir) el valor en punta:
*b = 5
.Un puntero puede apuntar a cualquier dirección de memoria, y no solo a la dirección de alguna variable que haya declarado previamente. Sin embargo, debe tener cuidado al usar punteros para obtener o establecer el valor ubicado en la dirección de memoria puntiaguda.
Por ejemplo:
int* a = (int*)0x8000000;
Aquí, tenemos una variable que
a
apunta a la dirección de memoria 0x8000000.Si esta dirección de memoria no está asignada dentro del espacio de memoria de su programa, entonces cualquier operación de lectura o escritura que use
*a
probablemente hará que su programa se bloquee, debido a una violación del acceso a la memoria.Puede cambiar el valor de forma segura
a
, pero debe tener mucho cuidado al cambiar el valor de*a
.El tipo
void*
es excepcional en el hecho de que no tiene un "tipo de valor" correspondiente que se pueda usar (es decir, no se puede declararvoid a
). Este tipo se usa solo como un puntero general a una dirección de memoria, sin especificar el tipo de datos que reside en esa dirección.fuente
Quizás recorrerlo un poco más lo hace más fácil:
Pídales que le digan qué esperan que sea la salida en cada línea, luego pídales que ejecuten el programa y vean qué aparece. Explique sus preguntas (la versión desnuda allí seguramente provocará algunas, pero puede preocuparse por el estilo, la rigidez y la portabilidad más adelante). Luego, antes de que su mente se vuelva loca por pensar demasiado o se conviertan en un zombi después del almuerzo, escriba una función que tome un valor y la misma que tome un puntero.
En mi experiencia, está superando ese "¿por qué esto se imprime de esa manera?" joroba, y luego muestra de inmediato por qué esto es útil en los parámetros de la función mediante el juego práctico (como preludio de algún material básico de K&R como el análisis de cadenas / procesamiento de matriz) que hace que la lección no solo tenga sentido sino que se mantenga.
El siguiente paso es conseguir que explicar a usted cómo
i[0]
se relaciona con&i
. Si pueden hacer eso, no lo olvidarán y puede comenzar a hablar sobre estructuras, incluso un poco antes de tiempo, solo para que se asimile.Las recomendaciones anteriores sobre cuadros y flechas también son buenas, pero también pueden terminar divagando en una discusión completa sobre cómo funciona la memoria, que es una charla que debe suceder en algún momento, pero que puede distraerlo inmediatamente. : cómo interpretar la notación de puntero en C.
fuente
int foo = 1;
. Ahora bien, esto está bien:int *bar; *bar = foo;
. Esto no está bien:int *bar = foo;
El tipo de la expresión
*bar
esint
; por lo tanto, el tipo de la variable (y expresión)bar
esint *
. Como la variable tiene un tipo de puntero, su inicializador también debe tener un tipo de puntero.Existe una inconsistencia entre la inicialización y asignación de la variable de puntero; Eso es algo que hay que aprender por las malas.
fuente
Prefiero leerlo como el primero se
*
aplica aint
más debar
.fuente
int* a, b
no hace lo que ellos piensan que hace.int* a,b
deba usarse en absoluto. Para una mejor lisibilidad, actualización, etc., solo debe haber una declaración de variable por línea y nunca más. También es algo para explicar a los principiantes, incluso si el compilador puede manejarlo.*
como parte del tipo y simplemente desalentarloint* a, b
. A menos que prefiera decir que*a
es de tipoint
más quea
un puntero aint
...int *a, b;
no debe usarse. Declarar dos variables con diferentes tipos en la misma declaración es una práctica bastante pobre y un buen candidato para problemas de mantenimiento en el futuro. Quizás sea diferente para aquellos de nosotros que trabajamos en el campo incrustado, donde anint*
y aint
menudo son de diferentes tamaños y, a veces, se almacenan en ubicaciones de memoria completamente diferentes. Es uno de los muchos aspectos del lenguaje C que se enseñaría mejor como 'está permitido, pero no lo hagas'.Question 1
: ¿Qué esbar
?Ans
: Es una variable de puntero (para escribirint
). Un puntero debe apuntar a una ubicación de memoria válida y luego debe desreferenciarse (* barra) utilizando un operador unario*
para leer el valor almacenado en esa ubicación.Question 2
: ¿Qué es&foo
?Ans
: foo es una variable de tipoint
.que se almacena en una ubicación de memoria válida y esa ubicación la obtenemos del operador,&
por lo que ahora tenemos una ubicación de memoria válida&foo
.Entonces, ambos juntos, es decir, lo que el puntero necesitaba era una ubicación de memoria válida y eso se consigue
&foo
para que la inicialización sea buena.Ahora el puntero
bar
apunta a una ubicación de memoria válida y el valor almacenado en él se puede obtener haciendo referencia a ella, es decir*bar
fuente
Debe señalar a un principiante que * tiene un significado diferente en la declaración y la expresión. Como sabe, * en la expresión es un operador unario, y * En la declaración no es un operador y solo un tipo de sintaxis que se combina con el tipo para que el compilador sepa que es un tipo de puntero. es mejor decir un principiante, "* tiene un significado diferente. Para comprender el significado de *, debe encontrar dónde se usa *"
fuente
Creo que el diablo está en el espacio.
Escribiría (no solo para el principiante, sino también para mí): int * bar = & foo; en lugar de int * bar = & foo;
Esto debería hacer evidente cuál es la relación entre la sintaxis y la semántica
fuente
Ya se señaló que * tiene múltiples roles.
Hay otra idea simple que puede ayudar a un principiante a comprender las cosas:
Piensa que "=" también tiene múltiples roles.
Cuando la asignación se usa en la misma línea con la declaración, piense en ella como una llamada de constructor, no como una asignación arbitraria.
Cuando veas:
Piensa que es casi equivalente a:
Los paréntesis tienen prioridad sobre el asterisco, por lo que "& foo" se atribuye intuitivamente mucho más fácilmente a "bar" que a "* bar".
fuente
Vi esta pregunta hace unos días, y luego leí la explicación de la declaración de tipo de Go en el Blog de Go . Comienza dando una cuenta de las declaraciones de tipo C, lo que parece un recurso útil para agregar a este hilo, aunque creo que ya hay respuestas más completas.
(Continúa describiendo cómo extender esta comprensión a los punteros de función, etc.)
Esta es una forma en que no lo había pensado antes, pero parece una forma bastante directa de explicar la sobrecarga de la sintaxis.
fuente
Si el problema es la sintaxis, puede ser útil mostrar un código equivalente con plantilla / uso.
Esto se puede usar como
Después de eso, compare la sintaxis normal / C con este enfoque de C ++ solamente. Esto también es útil para explicar los punteros constantes.
fuente
La fuente de confusión surge del hecho de que el
*
símbolo puede tener diferentes significados en C, dependiendo del hecho en el que se usa. Para explicar el puntero a un principiante, se*
debe explicar el significado del símbolo en un contexto diferente.En la declaración
El
*
símbolo no es el operador de indirección . En cambio, ayuda a especificar el tipo debar
información al compilador quebar
es un puntero a unint
. Por otro lado, cuando aparece en una declaración, el*
símbolo (cuando se usa como operador unario ) realiza una indirecta. Por lo tanto, la declaraciónestaría mal ya que asigna la dirección de
foo
al objeto quebar
apunta, no abar
sí mismo.fuente
"tal vez escribirlo como int * bar hace que sea más obvio que la estrella es en realidad parte del tipo, no parte del identificador". Así que hago. Y digo que es algo así como Type, pero solo para un nombre de puntero.
"Por supuesto, esto te lleva a diferentes problemas con cosas poco intuitivas como int * a, b".
fuente
Aquí debe usar, comprender y explicar la lógica del compilador, no la lógica humana (lo sé, usted es un humano, pero aquí debe imitar la computadora ...).
Cuando escribes
el compilador agrupa eso como
Es decir: aquí hay una nueva variable, su nombre es
bar
, su tipo apunta a int y su valor inicial es&foo
.Y debe agregar: los
=
denota anteriores no una inicialización una afectación, mientras que en las expresiones siguientes*bar = 2;
que es una afectaciónEditar por comentario:
Cuidado: en caso de declaración múltiple,
*
solo está relacionado con la siguiente variable:bar es un puntero a int inicializado por la dirección de foo, b es un int inicializado a 2, y en
barra en puntero fijo a int, y p es un puntero a un puntero a un int inicializado a la dirección o barra.
fuente
int* a, b;
declara que a es un puntero a anint
, pero b es un anint
. El*
símbolo solo tiene dos significados distintos: en una declaración, indica un tipo de puntero, y en una expresión es el operador de desreferencia unario.*
ajusta al tipo, de modo que el puntero se inicializa mientras que en una afectación se ve afectado el valor en punta. Pero al menos me diste un buen sombrero :-)Básicamente, el puntero no es una indicación de matriz. El principiante piensa fácilmente que el puntero parece una matriz. la mayoría de los ejemplos de cadenas que usan el
"char * pstr" es similar parece
"char str [80]"
Pero, cosas importantes, el puntero se trata como un número entero en el nivel inferior del compilador.
Veamos ejemplos ::
A los resultados les gustará esto 0x2a6b7ed0 es la dirección de str []
Entonces, básicamente, tenga en cuenta que Pointer es una especie de número entero. presentando la dirección.
fuente
Explicaría que los ints son objetos, como flotadores, etc. Un puntero es un tipo de objeto cuyo valor representa una dirección en la memoria (de ahí que el puntero por defecto sea NULL).
Cuando declara un puntero por primera vez, usa la sintaxis de tipo-puntero-nombre. Se lee como un "nombre llamado puntero entero que puede apuntar a la dirección de cualquier objeto entero". Solo usamos esta sintaxis durante la declinación, de forma similar a cómo declaramos un int como 'int num1' pero solo usamos 'num1' cuando queremos usar esa variable, no 'int num1'.
int x = 5; // un objeto entero con un valor de 5
int * ptr; // un entero con un valor de NULL por defecto
Para hacer que un puntero apunte a la dirección de un objeto, usamos el símbolo '&' que se puede leer como "la dirección de".
ptr = & x; // ahora el valor es la dirección de 'x'
Como el puntero es solo la dirección del objeto, para obtener el valor real contenido en esa dirección debemos usar el símbolo '*' que cuando se usa antes de un puntero significa "el valor en la dirección señalada por".
std :: cout << * ptr; // imprime el valor en la dirección
Puede explicar brevemente que ' ' es un 'operador' que devuelve resultados diferentes con diferentes tipos de objetos. Cuando se usa con un puntero, el operador ' ' ya no significa "multiplicado por".
Ayuda a dibujar un diagrama que muestre cómo una variable tiene un nombre y un valor y un puntero tiene una dirección (el nombre) y un valor y muestra que el valor del puntero será la dirección del int.
fuente
Un puntero es solo una variable utilizada para almacenar direcciones.
La memoria en una computadora está compuesta de bytes (un byte consta de 8 bits) dispuestos de forma secuencial. Cada byte tiene un número asociado, al igual que el índice o el subíndice en una matriz, que se llama la dirección del byte. La dirección del byte comienza de 0 a uno menos que el tamaño de la memoria. Por ejemplo, digamos en 64 MB de RAM, hay 64 * 2 ^ 20 = 67108864 bytes. Por lo tanto, la dirección de estos bytes comenzará de 0 a 67108863.
Veamos qué sucede cuando declaras una variable.
marcas int;
Como sabemos, un int ocupa 4 bytes de datos (suponiendo que estamos usando un compilador de 32 bits), por lo que el compilador reserva 4 bytes consecutivos de la memoria para almacenar un valor entero. La dirección del primer byte de los 4 bytes asignados se conoce como la dirección de las marcas variables. Digamos que la dirección de 4 bytes consecutivos son 5004, 5005, 5006 y 5007, entonces la dirección de las marcas variables será 5004.
Declarar variables de puntero
Como ya se dijo, un puntero es una variable que almacena una dirección de memoria. Al igual que cualquier otra variable, primero debe declarar una variable de puntero antes de poder usarla. Así es como puede declarar una variable de puntero.
Sintaxis:
data_type *pointer_name;
data_type es el tipo de puntero (también conocido como el tipo base del puntero). pointer_name es el nombre de la variable, que puede ser cualquier identificador C válido.
Tomemos algunos ejemplos:
int * ip significa que ip es una variable de puntero capaz de apuntar a variables de tipo int. En otras palabras, una variable de puntero ip puede almacenar la dirección de variables de tipo int solamente. Del mismo modo, la variable de puntero fp solo puede almacenar la dirección de una variable de tipo flotante. El tipo de variable (también conocido como tipo base) ip es un puntero a int y el tipo de fp es un puntero a flotante. Una variable de puntero de tipo puntero a int puede representarse simbólicamente como (int *). Del mismo modo, una variable de puntero de tipo puntero para flotar se puede representar como (float *)
Después de declarar una variable de puntero, el siguiente paso es asignarle una dirección de memoria válida. Nunca debe usar una variable de puntero sin asignarle una dirección de memoria válida, porque justo después de la declaración contiene un valor de basura y puede estar apuntando a cualquier parte de la memoria. El uso de un puntero no asignado puede dar un resultado impredecible. Incluso puede hacer que el programa se bloquee.
Fuente: thecguru es, con mucho, la explicación más simple pero detallada que he encontrado.
fuente