Había estado escribiendo cosas como
char *x=NULL;
asumiendo que
char *x=2;
crearía un charpuntero a la dirección 2.
Pero, en The GNU C Programming Tutorial dice que int *my_int_ptr = 2;almacena el valor entero 2en cualquier dirección aleatoria en la my_int_ptrque se encuentre cuando se asigna.
Esto parecería implicar que el mío char *x=NULLestá asignando cualquier valor de NULLcast a a chares 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_ptres una variable (de tipo puntero aint), tiene una dirección propia (tipo: dirección de puntero a entero), está almacenando un valor de2en esa dirección.Ahora, al
my_int_ptrser 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
xaNULL, 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_ptres una variable de puntero, tipoint *2tiene 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
2es unint, la tarea es un problema. Pero es más que eso.NULLtambié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 .0Es una constante entera )char *x = (void *)0;, ser conforme? ¿O es solo con otras expresiones lo que da el valor0?0son 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 = somePtrExpressionmi humilde opinión, la sintaxis es bastante horrible, ya que parece que está estableciendo el valor de*ppero 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 entero2en 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úmero5en 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_texplí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)bno es un tipo de puntero compatible, ni avoid*, ni una constante de puntero nulo, yvoid* ano 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 = 2Esto está completamente mal. Si esto está realmente escrito, consiga un mejor libro o tutorial.
int *my_int_ptr = 2define 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 elinten la línea, almacena el valor dos en cualquier dirección aleatoria a lamy_int_ptrque apunte. Habiendo dicho esto, puede asignarNULLa 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 valorNULLse 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 = expressionconduce apointer-type variable = pointer-expressionydereferenced-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-variablelugar 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, someotherPtrdeclare dos punteros, de hecho, solía escribir,int* somePtrpero eso conduce al error que describe.createlugar 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
NULLestá 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
freees 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.constpunteros declarados en medias res , pero incluso cuando un puntero necesita ser mutable (como uno que se usa en un bucle o porrealloc()), configurándolo paraNULLdetectar errores donde se usa antes está configurado con su valor real. En la mayoría de los sistemas, la desreferenciaciónNULLcausa 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.