Había estado escribiendo cosas como
char *x=NULL;
asumiendo que
char *x=2;
crearía un char
puntero a la dirección 2.
Pero, en The GNU C Programming Tutorial dice que int *my_int_ptr = 2;
almacena el valor entero 2
en cualquier dirección aleatoria en la my_int_ptr
que se encuentre cuando se asigna.
Esto parecería implicar que el mío char *x=NULL
está asignando cualquier valor de NULL
cast a a char
es a alguna dirección aleatoria en la memoria.
Mientras
#include <stdlib.h>
#include <stdio.h>
int main()
{
char *x=NULL;
if (x==NULL)
printf("is NULL\n");
return EXIT_SUCCESS;
}
de hecho, imprime
es nulo
cuando lo compilo y ejecuto, me preocupa que confíe en un comportamiento indefinido, o al menos un comportamiento subespecificado, y que debería escribir
char *x;
x=NULL;
en lugar.
c
pointers
initialization
fagricipni
fuente
fuente
int *x = whatever;
hace y lo queint *x; *x = whatever;
hace.int *x = whatever;
en realidad se comporta comoint *x; x = whatever;
, no*x = whatever;
.Respuestas:
TL; DR Sí, mucho.
La afirmación real hecha en la guía se lee como
Bueno, están equivocados, tienes razón.
Para la declaración, ( ignorando, por ahora, el hecho de que la conversión de puntero a entero es un comportamiento definido por la implementación )
my_int_ptr
es una variable (de tipo puntero aint
), tiene una dirección propia (tipo: dirección de puntero a entero), está almacenando un valor de2
en esa dirección.Ahora, al
my_int_ptr
ser un tipo de puntero, podemos decir que apunta al valor de "tipo" en la ubicación de memoria apuntada por el valor retenidomy_int_ptr
. Por lo tanto, esencialmente está asignando el valor de la variable de puntero, no el valor de la ubicación de memoria a la que apunta el puntero.Entonces, para concluir
inicializa la variable de puntero
x
aNULL
, no el valor en la dirección de memoria apuntada por el puntero .Esto es lo mismo que
Expansión:
Ahora, siendo estrictamente conforme, una declaración como
es ilegal, ya que implica una violación de la restricción. Para ser claro,
my_int_ptr
es una variable de puntero, tipoint *
2
tiene tipoint
, por definición.y no son tipos "compatibles", por lo que esta inicialización no es válida porque viola las reglas de asignación simple, mencionadas en el capítulo §6.5.16.1 / P1, descritas en la respuesta de Lundin .
En caso de que alguien esté interesado en cómo la inicialización está vinculada a restricciones de asignación simples, citando
C11
, capítulo §6.7.9, P11fuente
2
es unint
, la tarea es un problema. Pero es más que eso.NULL
también puede ser unint
, unint 0
. Es solo quechar *x = 0;
está bien definido ychar *x = 2;
no lo está. 6.3.2.3 Punteros 3 (BTW: C no define un literal entero , sólo el literal de cadena y literal compuesto .0
Es una constante entera )char *x = (void *)0;
, ser conforme? ¿O es solo con otras expresiones lo que da el valor0
?0
son especiales: se convierten implícitamente en punteros nulos por separado de las reglas habituales para convertir explícitamente expresiones enteras generales en tipos de punteros.int *p = somePtrExpression
mi humilde opinión, la sintaxis es bastante horrible, ya que parece que está estableciendo el valor de*p
pero en realidad está estableciendo el valor dep
.El tutorial está mal. En ISO C,
int *my_int_ptr = 2;
es un error. En GNU C, significa lo mismo queint *my_int_ptr = (int *)2;
. Esto convierte el entero2
en una dirección de memoria, de alguna manera según lo determine el compilador.No intenta almacenar nada en la ubicación a la que se dirige esa dirección (si corresponde). Si continuara escribiendo
*my_int_ptr = 5;
, intentaría almacenar el número5
en la ubicación a la que se dirige esa dirección.fuente
Para aclarar por qué el tutorial es incorrecto,
int *my_int_ptr = 2;
es una "violación de restricción", es un código que no se puede compilar y el compilador debe proporcionarle un diagnóstico al encontrarlo.Según 6.5.16.1 Asignación simple:
En este caso, el operando izquierdo es un puntero no calificado. En ninguna parte menciona que el operando derecho puede ser un número entero (tipo aritmético). Entonces el código viola el estándar C.
Se sabe que GCC se comporta mal a menos que le indique explícitamente que sea un compilador C estándar. Si compila el código como
-std=c11 -pedantic-errors
, dará correctamente un diagnóstico como debe hacerlo.fuente
void *
, se llama una constante de puntero nulo". Observe la penúltima viñeta en su cotización. Por tanto,int* p = 0;
es una forma legal de escribirint* p = NULL;
. Aunque este último es más claro y convencional.int m = 1, n = 2 * 2, * p = 1 - 1, q = 2 - 1;
también sea legal.intptr_t
explícitamente a uno de los tipos permitidos en el lado derecho. Es decir,void* a = (void*)(intptr_t)b;
es legal según el punto 4, pero(intptr_t)b
no es un tipo de puntero compatible, ni avoid*
, ni una constante de puntero nulo, yvoid* a
no es un tipo aritmético ni_Bool
. El estándar dice que la conversión es legal, pero no implícita.int *my_int_ptr = 2
Esto está completamente mal. Si esto está realmente escrito, consiga un mejor libro o tutorial.
int *my_int_ptr = 2
define un puntero entero que apunta a la dirección 2. Lo más probable es que se bloquee si intenta acceder a la dirección2
.*my_int_ptr = 2
, es decir, sin elint
en la línea, almacena el valor dos en cualquier dirección aleatoria a lamy_int_ptr
que apunte. Habiendo dicho esto, puede asignarNULL
a un puntero cuando esté definido.char *x=NULL;
es perfectamente válido C.Editar: Mientras escribía esto, no sabía que la conversión de entero a puntero es un comportamiento definido por la implementación. Consulte las buenas respuestas de @MM y @SouravGhosh para obtener más detalles.
fuente
Mucha confusión acerca de los punteros C proviene de una muy mala elección que se hizo originalmente con respecto al estilo de codificación, corroborada por una muy mala elección en la sintaxis del lenguaje.
int *x = NULL;
C es correcta, pero es muy engañosa, incluso diría absurda, y ha dificultado la comprensión del idioma para muchos novatos. Hace pensar que más adelante podríamos hacer lo*x = NULL;
que, por supuesto, es imposible. Verá, el tipo de la variable no lo esint
, y el nombre de la variable no lo es*x
, ni el*
en la declaración juega ningún papel funcional en colaboración con el=
. Es puramente declarativo. Entonces, lo que tiene mucho más sentido es esto:int* x = NULL;
que también es C correcta, aunque no se adhiere al estilo de codificación original de K&R. Deja perfectamente claro que el tipo esint*
, y la variable de puntero lo esx
, por lo que resulta claramente evidente incluso para los no iniciados que el valorNULL
se está almacenando enx
, que es un puntero aint
.Además, hace que sea más fácil derivar una regla: cuando la estrella está lejos del nombre de la variable, entonces es una declaración, mientras que la estrella que se adjunta al nombre es una desreferenciación del puntero.
Entonces, ahora se vuelve mucho más comprensible que más abajo podamos hacer
x = NULL;
o,*x = 2;
en otras palabras, facilita que un novato vea cómovariable = expression
conduce apointer-type variable = pointer-expression
ydereferenced-pointer-variable = expression
. (Para los iniciados, por 'expresión' me refiero a 'rvalue').La desafortunada elección en la sintaxis del lenguaje es que al declarar variables locales puedes decir
int i, *p;
cuál declara un número entero y un puntero a un número entero, por lo que te lleva a creer que*
es una parte útil del nombre. Pero no lo es, y esta sintaxis es solo un caso especial peculiar, agregado por conveniencia y, en mi opinión, nunca debería haber existido, porque invalida la regla que propuse anteriormente. Hasta donde yo sé, en ninguna otra parte del lenguaje esta sintaxis es significativa, pero incluso si lo es, apunta a una discrepancia en la forma en que se definen los tipos de puntero en C.En cualquier otro lugar, en declaraciones de una sola variable, en listas de parámetros, en miembros de estructura, etc., puede declarar sus punteros como entype* pointer-variable
lugar detype *pointer-variable
; es perfectamente legal y tiene más sentido.fuente
int *x = NULL; is correct C, but it is very misleading, I would even say nonsensical,
... Tengo que aceptar estar en desacuerdo.It makes one think
.... deja de pensar, lee un libro en C primero, sin ofender.int* somePtr, someotherPtr
declare dos punteros, de hecho, solía escribir,int* somePtr
pero eso conduce al error que describe.create
lugar decreat
. :) El caso es que es así y tenemos que moldearnos para adaptarnos a eso. Al final del día, todo se reduce a una elección personal, de acuerdo.Me gustaría agregar algo ortogonal a las muchas respuestas excelentes. En realidad, la inicialización en
NULL
está lejos de ser una mala práctica y puede ser útil si ese puntero puede o no usarse para almacenar un bloque de memoria asignado dinámicamente.Dado que de acuerdo con el estándar ISO-IEC 9899
free
es un nop cuando el argumento lo esNULL
, el código anterior (o algo más significativo en la misma línea) es legítimo.fuente
void*
se convierte según sea necesario. Pero tener un código que funcione con un compilador C y C ++ podría tener beneficios.const
punteros declarados en medias res , pero incluso cuando un puntero necesita ser mutable (como uno que se usa en un bucle o porrealloc()
), configurándolo paraNULL
detectar errores donde se usa antes está configurado con su valor real. En la mayoría de los sistemas, la desreferenciaciónNULL
causa una falla de segmentación en el punto de falla (aunque hay excepciones), mientras que un puntero no inicializado contiene basura y escribir en él corrompe la memoria arbitraria.NULL
, pero puede ser muy difícil diferenciar un puntero de basura de uno válido. Por lo tanto, es útil asegurarse de que todos los punteros sean siempre válidos oNULL
, desde el momento de la declaración.este es un puntero nulo
fuente
Esto es correcto.
Esta función es correcta para lo que hace. Asigna la dirección de 0 al puntero char x. Es decir, apunta el puntero x a la dirección de memoria 0.
Alternativa:
Mi conjetura sobre lo que querías es:
fuente
char* x = 0; if (x == 0)
será cierto. Los punteros no son necesariamente números enteros.