La cuarta parte del puntero (puntero salvaje, puntero nulo, puntero universal) ---- 2021.2.5

Última charla sobre enlaces de puntero:

La tercera parte del puntero ("*" y "&", la longitud del paso del puntero) ---- 2021.1.31

Puntero salvaje

Primero, eche un vistazo al fragmento de código que se proporciona a continuación. Piense si este código tiene problemas de sintaxis y, ¿puede ejecutarse correctamente al final?

int main()
{
    
    
    int* p;
    *p = 200;
    printf("%d\n", *p);
    system("pause");
    return 0;
}

Primero haga un análisis simple del código anterior:

Primero, definimos una variable de puntero p, y luego asignamos un valor de 200 al contenido apuntado por la variable de puntero p. Finalmente, imprimimos el contenido del espacio de memoria al que apunta p.

Pero intentamos ejecutarlo y finalmente mostramos un error. ¿Por qué es esto? Los resultados de la operación son los siguientes:
Inserte la descripción de la imagen aquíMotivo:

  1. * p: Representa el contenido del espacio de memoria al que apunta la variable de puntero p. Sin embargo, como se muestra en la figura siguiente, el programa no apunta claramente al espacio de memoria específico, por lo que 200 se asignarán aleatoriamente a un espacio de memoria aleatorio, por lo que eventualmente hará que el sistema se bloquee.
    Inserte la descripción de la imagen aquí

  2. En términos sencillos, en este momento, la variable puntero p es equivalente a un perro rabioso. Quien muerda y vea el espacio de la memoria apuntará a ese espacio de la memoria y asignará 200 a ese espacio de la memoria.

Por lo tanto, los punteros salvajes son punteros que no se inicializan. Los punteros apuntan a punteros aleatorios y los punteros salvajes no se pueden manipular .
La operación normal debería ser definir una variable y luego asignar la dirección de la variable a la variable puntero p. Como sigue.

int a = 0;
int *p;
p = &a;
*p = 200;

A través del análisis anterior, también debemos saber que la dirección guardada por el puntero p debe estar definida (es decir, el espacio de memoria al que apunta p debe haber sido aplicado al sistema)

Puntero nulo

Del mismo modo, eche un vistazo al código que se proporciona a continuación. Piense si este código tiene problemas de sintaxis y, ¿puede ejecutarse correctamente al final?

int main()
{
    
    
    int  a;
    int* p = 0x00000000;
    
    *p = 200;
    printf("%d\n", *p);
    system("pause");
    return 0;
}

Primero haga un análisis simple del código anterior:

Definimos una variable entera ay una variable puntero p, y para evitar el problema de los punteros salvajes que mencionamos hace un momento, definimos artificialmente una dirección, que es 0x0000 0000. Luego asignamos 200 al contenido del espacio de memoria apuntado por la variable de puntero p. Finalmente, imprimimos el contenido de ese espacio de memoria a través de * p.

Pero intentamos ejecutarlo y finalmente mostramos un error. ¿Por qué es esto? Los resultados de la operación son los siguientes:
Inserte la descripción de la imagen aquí
Motivo:

  1. Como se muestra en el código anterior, asignamos 0x0000 0000 a la variable de puntero p para evitar punteros salvajes. En este momento, el puntero se convertirá en un puntero nulo. De hecho, otra forma de expresar un puntero nulo es la siguiente:
 int  *p = NULL;
  1. Pero, ¿por qué habría problemas después de que se ejecute el programa? Necesitamos saber que se asigna 0x0000 0000 o NULL a la variable de puntero p, lo que también significa que la variable de puntero p apunta a esta dirección, que es 0x0000 0000. El diagrama esquemático específico se muestra en la siguiente figura.
    Inserte la descripción de la imagen aquí
  2. Aunque esto parece no ser un problema en la superficie, p guarda la dirección de 0x0000 0000, esta dirección no se puede usar, porque a menudo esta dirección se usa para almacenar los archivos de inicio del sistema, lo cual es ilegal, por lo que causa lo anterior Como se muestra en el conflicto de permisos de acceso de escritura, naturalmente, el programa no se ejecutará correctamente.

Entonces, dado que los punteros nulos pueden causar este tipo de problema, ¿en qué aplicaciones se usan los punteros nulos?

El papel del puntero nulo: si el puntero nulo se agota, generalmente asignaremos el puntero a NULL. En el siguiente uso, juzgue si el puntero es NULL, sabrá si se usa el puntero. Si el valor del puntero es NULL, significa que podemos usar el puntero y luego asignarlo. Si el valor del puntero no es NULL, significa que no podemos usar el puntero, lo que significa que el puntero se ha asignado a otro lugar.

Puntero universal

Me lleva a la pregunta;
el artículo anterior mencionado, hay muchos tipos de datos variables puntero, tales como int*, char*, double*etc., entonces es que hay un tipo de variable puntero puede aceptar todo tipo de datos?

A esta variable de puntero la llamamos puntero universal.

Para que todos entendieran mejor, hice un dibujo especial para que todos pudieran verlo, como se muestra en la siguiente figura.

Inserte la descripción de la imagen aquí
La explicación de la figura anterior es la siguiente:

Cuando generalmente definimos una función, a menudo dejamos la interfaz de función correspondiente, pero para lograr la compatibilidad, en realidad no sabemos si los parámetros pasados ​​a la función se ajustan al tipo de datos de la interfaz de función que definimos, por lo que podemos utilice los punteros universales para resolver este problema.

Del mismo modo, eche un vistazo al código que se proporciona a continuación. Piense si este código tiene problemas de sintaxis y, ¿puede ejecutarse correctamente al final?

int main()
{
    
    
    int  a = 10;
    short b = 10;
    void* p = (void*)&a;
    void* q = (void*)&b;
    
    printf("%d\n", *p);
    system("pause");
    return 0;
}

Primero haga un análisis simple del código anterior:

  1. Generalmente, definimos punteros universales de la siguiente manera:
   void *p;//万能指针
  1. Definimos una variable entera ay la asignamos a 10. Del mismo modo, definimos una variable b de tipo corto, a la que también se le asigna un valor de 10.
  2. Defina dos punteros universales pyq para almacenar las direcciones de ay b. Pero de los artículos anteriores, aprendimos que los tipos de datos deben coincidir en ambos lados de la expresión. Repitámoslo aquí para profundizar la impresión de todos. Veamos aun ejemplo. pEl tipo de datos en el lado izquierdo de la expresión es un void*tipo, pero el atipo de datos en el lado derecho de la expresión es un inttipo, pero la &operación de dirección se realiza a partir del valor del artículo anterior . En este momento a, el el tipo de datos se convierte en un int*tipo, pero el tipo de datos aún no coincide, por lo que en este momento necesitamos &aconvertir el tipo de datos general, por lo que la expresión se convierte envoid* p = (void*)&a;
  3. Finalmente, imprimimos el contenido del espacio de memoria a al que apunta la variable puntero p. De acuerdo con la lógica anterior, el resultado final debería ser 10.

Pero intentamos ejecutarlo y finalmente mostramos un error. ¿Por qué es esto? Los resultados de la operación son los siguientes:
Inserte la descripción de la imagen aquíMotivo:

  1. Como se muestra arriba, el programa final no pudo compilarse porque finalmente obtuvimos el contenido del espacio de memoria apuntado por p, pero de los artículos anteriores, el contenido obtenido está relacionado con el tamaño de paso del tipo de datos correspondiente., También puede ser entendido como relacionado con el ancho del tipo de datos.
  2. Debido a que definimos p como un puntero universal, es decir, void * type, podemos ver en los artículos anteriores que el tamaño de paso final del tipo de datos p es sizeof (void), pero como todos sabemos, void significa vacío, lo que significa que pasamos variables de puntero. Cuando p solicita el contenido del espacio de memoria correspondiente, el sistema no puede reconocer el tipo de vacío, por lo que provocará el fenómeno de que la compilación falle.
  3. Por ejemplo, no es void b;posible definir una variable de tipo void, porque el compilador no sabe cuánto espacio se asigna a la variable pero el void *tipo se puede definir , porque de los artículos anteriores, los punteros son todos de 4 bytes.

Luego, cuando usemos el puntero universal, ¿cómo podemos finalmente imprimir el contenido de ese espacio de memoria normalmente? Por favor, venga a analizar conmigo.

 printf("%d\n", *p);

pEl tipo de datos de la variable es void*tipo, lo que significa *pque se toma el contenido del espacio de memoria correspondiente, pero no se permite el resultado. Entonces necesitamos forzar la conversión en este momento.

Entonces la pregunta es, ¿ qué parte debe ser coaccionada?

Todos queremos que se imprima el valor final 10, y 10el tipo de datos es el inttipo. Dado que la pvariable es una variable de puntero, la panterior “*”es solo para extraer el contenido del espacio correspondiente, por lo que la parte de nuestra coerción es p. Por lo tanto, el void*tipo debe convertirse en un int*tipo. Después de la modificación, el código es el siguiente, podemos ver el resultado.

int main()
{
    
    
    int  a = 10;
    short b = 10;
    void* p = (void*)&a;
    void* q = (void*)&b;

    printf("%d\n", *(int*)p);
    system("pause");
    return 0;
}

Inserte la descripción de la imagen aquí

Observaciones finales

Si cree que este artículo es bueno, ¡recuerde darle me gusta y apoyarlo! ! !

Supongo que te gusta

Origin blog.csdn.net/qq_40544107/article/details/113697603
Recomendado
Clasificación