Puntero NULL en C

En un nivel muy alto, podemos pensar en NULL como un puntero nulo que se usa en C para varios propósitos. Algunos de los casos de uso más comunes para NULL son: a) Para inicializar una variable de puntero cuando a esa variable de puntero aún no se le ha asignado ninguna dirección de memoria válida. b) Para verificar si hay un puntero nulo antes de acceder a cualquier variable de puntero. Al hacerlo, podemos realizar el manejo de errores en el código relacionado con el puntero, por ejemplo, desreferenciar una variable de puntero solo si no es NULL. c) Para pasar un puntero nulo a un argumento de función cuando no queremos pasar ninguna dirección de memoria válida.

El ejemplo de a es

C

int * pInt = NULL;

El ejemplo de b es

C

if(pInt != NULL) /*We could use if(pInt) as well*/
{ /*Some code*/}
else
{ /*Some code*/}

El ejemplo de c es

C

int fun(int *ptr)
{
 /*Fun specific stuff is done with ptr here*/
 return 10;
}
fun(NULL);

Cabe señalar que un puntero NULL es diferente de un puntero no inicializado o colgante. En un contexto de programa específico, todos los punteros no inicializados, colgantes o NULL no son válidos, pero NULL es un puntero no válido específico que se menciona en el estándar C y tiene propósitos específicos. Lo que queremos decir es que los punteros no inicializados o colgantes no son válidos, pero pueden apuntar a alguna dirección de memoria a la que se puede acceder a través de un acceso no deseado a la memoria.

C

#include <stdio.h>
int main()
{
 int *i, *j;
 int *ii = NULL, *jj = NULL;
 if(i == j)
 {
  printf("This might get printed if both i and j are same by chance.");
 }
 if(ii == jj)
 {
  printf("This is always printed coz ii and jj are same.");
 }
 return 0;
}

Al mencionar específicamente el puntero NULL, el estándar C brinda un mecanismo mediante el cual un programador de C puede verificar si un puntero determinado es legítimo o no. Pero, ¿qué es exactamente NULL y cómo se define? Estrictamente hablando, NULL se expande a una constante de puntero nulo definida por la implementación que se define en muchos archivos de encabezado como » stdio.h «, » stddef.h «, » stdlib.h «, etc. Veamos qué dicen los estándares C sobre el puntero nulo. El estándar C11, cláusula 6.3.2.3 dice,

Una expresión de constante entera con el valor 0, o una expresión de este tipo convertida al tipo void *, se denomina constante de puntero nulo. Si una constante de puntero nulo se convierte en un tipo de puntero, se garantiza que el puntero resultante, llamado puntero nulo, se comparará con un puntero a cualquier objeto o función.

Antes de continuar con esta discusión NULL :), mencionemos algunas líneas sobre el estándar C en caso de que desee consultarlo para un estudio adicional. Tenga en cuenta que ISO/IEC 9899:2011 es el estándar más reciente del lenguaje C que se publicó en diciembre de 2011. También se denomina estándar C11. Para completar, mencionemos que los estándares C anteriores eran C99, C90 (también conocido como ISO C) y C89 (también conocido como ANSI C). Aunque el estándar C11 real se puede comprar de ISO, hay un borrador de documento que está disponible en dominios públicos de forma gratuita.

Volviendo a nuestra discusión, la macro NULL se define como ((void *)0) en los archivos de encabezado de la mayoría de las implementaciones del compilador C. Pero, el estándar C dice que 0 también es una constante de puntero nulo. Significa que lo siguiente también es perfectamente legal según el estándar:

C

int * ptr = 0;

Tenga en cuenta que 0 en la instrucción C anterior se usa en contexto de puntero y es diferente de 0 como número entero. Esta es una de las razones por las que se prefiere el uso de NULL porque hace explícito en el código que el programador está usando un puntero nulo, no un número entero 0. Otro concepto importante sobre NULL es que » NULL se expande a un nulo definido por la implementación». puntero constante”. Esta declaración también es del estándar C11 (cláusula 7.19). Significa que la representación interna del puntero nulo podría ser un patrón de bits distinto de cero para transmitir el puntero NULL. Es por eso que NULL no siempre necesita representarse internamente como un patrón de bits de ceros. Una implementación del compilador puede optar por representar una «constante de puntero nulo» como un patrón de bits para todos los 1 o cualquier otra cosa. Pero nuevamente, como programadores de C, no debemos preocuparnos mucho por el valor interno del puntero nulo a menos que estemos involucrados en la codificación del compilador o incluso por debajo del nivel de codificación. Dicho esto, NULL normalmente se representa como todos los bits establecidos en 0 solamente. Para saber esto en una plataforma específica, se puede usar lo siguiente:

C

#include<stdio.h>
int main()
{
 printf("%d",NULL);
 return 0;
}

Lo más probable es que imprima 0, que es el valor de puntero nulo interno típico, pero nuevamente, puede variar según el compilador/plataforma de C. Puede probar algunas otras cosas en el programa anterior, como printf(“’%c“,NULL) o printf(“%s”,NULL) e incluso printf(“%f”,NULL) . Los resultados de estos serán diferentes según la plataforma utilizada, pero sería interesante verlos, ¡especialmente debido al uso de %f con NULL!

¿Podemos usar el operador sizeof() en NULL en C? Bueno, el uso de sizeof(NULL) está permitido, pero el tamaño exacto dependerá de la plataforma.

C

#include<stdio.h>
int main()
{
 printf("%lu",sizeof(NULL));
 return 0;
}

Dado que NULL se define como ((void*)0) , podemos pensar en NULL como un puntero especial y su tamaño sería igual a cualquier puntero. Si el tamaño del puntero en una plataforma es de 4 bytes, la salida del programa anterior sería 4. Pero si el tamaño del puntero en una plataforma es de 8 bytes, la salida del programa anterior sería 8.

¿Qué pasa con la desreferenciación de un puntero NULL? ¿Qué va a pasar si usamos el siguiente código C:

C

#include<stdio.h>
int main()
{
 int * ptr = NULL;
 printf("%d",*ptr);
 return 0;
}

En algunas máquinas, lo anterior se compilaría con éxito pero fallaría cuando se ejecuta el programa. Aunque eso no significa que mostraría el mismo comportamiento en todas las máquinas. Una vez más, depende de muchos factores. Pero la idea de mencionar el fragmento anterior es que siempre debemos verificar NULL antes de acceder a un puntero. Como el valor de NULL en las bibliotecas predefinidas es 0 y el puntero (que apunta a NULL) no apunta a ninguna ubicación de memoria, se produce este comportamiento.

podemos ver la salida de este código usando el diagrama:

 

Dado que NULL normalmente se define como ((void*)0) , discutamos un poco sobre el tipo void también. Según el estándar C11, cláusula 6.2.5, “ El tipo void comprende un conjunto vacío de valores; es un tipo de objeto incompleto que no se puede completar ”. Incluso el estándar C11, cláusula 6.5.3.4 menciona que “ El operador sizeof no se aplicará a una expresión que tenga un tipo de función o un tipo incompleto, al nombre entre paréntesis de dicho tipo, o a una expresión que designe un miembro de campo de bits . Básicamente, significa que void es un tipo incompleto cuyo tamaño no tiene ningún sentido en los programas C, pero las implementaciones (como GCC) pueden elegir sizeof (void)como 1 para que la memoria plana a la que apunta un puntero vacío pueda verse como una memoria sin tipo, es decir, una secuencia de bytes. Pero el resultado de lo siguiente no tiene por qué ser el mismo en todas las plataformas:

C

#include<stdio.h>
int main()
{
 printf("%lu",sizeof(void));
 return 0;
}

En GCC, lo anterior generaría 1. ¿Qué pasa con sizeof(void *) ? Aquí, C11 ha mencionado pautas. De la cláusula 6.2.5, » Un puntero a void tendrá los mismos requisitos de representación y alineación que un puntero a un tipo de carácter «. Es por eso que el resultado de lo siguiente sería el mismo que cualquier tamaño de puntero en una máquina:

C

#include<stdio.h>
int main()
{
 printf("%lu",sizeof(void *));
 return 0;
}

A pesar de mencionar las cosas dependientes de la máquina como se mencionó anteriormente, nosotros, como programadores de C, siempre debemos esforzarnos por hacer que nuestro código sea lo más portátil posible. Entonces, podemos concluir en NULL de la siguiente manera:

1. Siempre inicialice las variables de puntero en NULL. 2. Realice siempre una comprobación NULL antes de acceder a cualquier puntero.

Haga clic en Me gusta/Tweet/G+1 si encuentra útil lo anterior. Además, déjenos un comentario para obtener más aclaraciones o información. Nos encantaría ayudar y aprender 🙂

Publicación traducida automáticamente

Artículo escrito por GeeksforGeeks-1 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *