El tutorial de lenguaje C más sólido de la historia: punteros (principal)

contenido

1. ¿Qué es un puntero?

2. Punteros y tipos de punteros

2.1 Puntero + - Entero

2.2 Desreferencia de punteros

3. Puntero salvaje

3.1 Causas de los punteros salvajes

3.2 Cómo evitar los punteros salvajes

4. Aritmética de punteros

4.1 Puntero + - Entero

4.2 Puntero-Puntero

4.3 Operaciones relacionales sobre punteros

5. Punteros y matrices

 6. Puntero secundario

7. Matriz de punteros


1. ¿Qué es un puntero?

¿Qué son los punteros?

2 puntos para la comprensión del puntero:

  • 1. Un puntero es el número de una unidad más pequeña en la memoria, es decir, una dirección, que es lo que a menudo llamamos un puntero o una dirección.
  • 2. El puntero que suele hablarse en el lenguaje hablado suele referirse a una variable puntero, que es una variable que se utiliza para almacenar direcciones de memoria, es decir, un puntero es una variable

Resumen: un puntero es una dirección, y un puntero en el lenguaje coloquial generalmente se refiere a una variable de puntero.

Entonces podemos entenderlo así:

RAM

variable puntero

Podemos usar el & (operador de dirección) para sacar la dirección real de la memoria de la variable y almacenar la dirección en una variable, que es una variable de puntero.

#include <stdio.h>
int main()
{
	int a = 10;//在内存中开辟一块空间
	int* p = &a;//这里我们对变量a,取出它的地址,可以使用&操作符。
	   //a变量占用4个字节的空间,这里是将a的4个字节的第一个字节的地址存放在p变量
	中,p就是一个之指针变量。
		return 0;
}

Resumir:

Una variable de puntero, una variable utilizada para almacenar una dirección. (El valor almacenado en el puntero se trata como una dirección).

El problema aquí es:

  • ¿Qué tan grande es una unidad pequeña? (1 byte)
  • ¿Cómo dirigirse?

Después de un cuidadoso cálculo y pesaje, encontramos que es más apropiado dar un byte a una dirección correspondiente.

Para una máquina de 32 bits, suponiendo que hay 32 líneas de dirección, luego suponiendo que cada línea de dirección genera un nivel alto (alto voltaje) y un nivel bajo (bajo voltaje) al direccionar, es (1 o 0);

Entonces la dirección generada por las 32 líneas de dirección será:

00000000 00000000 00000000 00000000

00000000 00000000 00000000 00000001

...

11111111 11111111 11111111 11111111

Hay de 2 a 32 direcciones aquí.

Cada dirección identifica un byte, luego podemos dar (2^32Byte == 2^32/1024KB == 2^32/1024/1024MB==2^32/1024/1024/1024GB == 4GB) 4G gratis para el direccionamiento.

De la misma manera, para una máquina de 64 bits, si proporciona 64 líneas de dirección, cuánto espacio se puede direccionar y puede calcularlo usted mismo. El método de cálculo es como se muestra arriba y no se calculará aquí.

Aquí entendemos: En una máquina de 32 bits, la dirección es una secuencia binaria compuesta por 32 0 o 1, y la dirección debe almacenarse en 4 bytes de espacio, por lo que el tamaño de una variable de puntero debe ser de 4 bytes. . Luego, en una máquina de 64 bits, si hay 64 líneas de dirección, el tamaño de una variable de puntero es de 8 bytes para almacenar una dirección.

Resumen: los punteros se utilizan para almacenar direcciones y las direcciones marcan de forma única un espacio de direcciones. El tamaño de un puntero es de 4 bytes en plataformas de 32 bits y de 8 bytes en plataformas de 64 bits.

2. Punteros y tipos de punteros

Aquí estamos discutiendo: todos conocemos el tipo de puntero, las variables tienen diferentes tipos, enteros, punto flotante, etc.

¿El puntero tiene un tipo? Para ser precisos: sí. Cuando hay un código como este:

int num = 10;
p = &num;

Para guardar &num (la dirección de num) en p, sabemos que p es una variable de puntero, entonces, ¿cuál es su tipo? Damos a la variable puntero el tipo correspondiente.

char  *pc = NULL;
int   *pi = NULL;
short *ps = NULL;
long  *pl = NULL;
float *pf = NULL;
double *pd = NULL;

Como puede ver aquí, los punteros se definen como: type + * .

De hecho: el puntero de tipo char* es para almacenar la dirección de la variable de tipo char.

Un puntero de tipo short* se utiliza para almacenar la dirección de una variable de tipo short.

Se utiliza un puntero de tipo int* para almacenar la dirección de una variable de tipo int. ¿Cuál es el punto de ese tipo de puntero? ¡Te lo explicamos a continuación!

2.1 Puntero + - Entero

#include <stdio.h>
int main()
{
 int n = 10;
 char *pc = (char*)&n;
 int *pi = &n;
 
 printf("%p\n", &n);
 printf("%p\n", pc);
 printf("%p\n", pc+1);
 printf("%p\n", pi);
 printf("%p\n", pi+1);
 return  0;
}

Resumen: El tipo del puntero determina qué tan lejos (distancia) avanza o retrocede el puntero.

2.2 Desreferencia de punteros

#include <stdio.h>
int main()
{
	int n = 0x11223344;
	char* pc = (char*)&n;
	int* pi = &n;
	*pc = 0;   //重点在调试的过程中观察内存的变化。
	*pi = 0;   //重点在调试的过程中观察内存的变化。
	return 0;
}

La siguiente figura muestra el espacio de memoria de n después de ejecutar *pc = 0

Como se puede ver en la figura, después de ejecutar la declaración, ¡solo se ha cambiado el valor correspondiente al espacio de un byte!

 La siguiente figura muestra el espacio de memoria después de ejecutar *pi = 0, lo cual es consistente con el tipo de puntero que definimos, porque la pc que definimos es una variable de puntero de tipo char, y char solo ocupa cuatro bytes de espacio de memoria.

 Como se puede ver en la figura, esta vez cambiamos el valor correspondiente al espacio de cuatro bytes, lo cual es consistente con el tipo de puntero que definimos, porque pi es una variable de puntero de tipo int, e int ocupa cuatro bytes en la memoria Espacio .

Resumir:

 El tipo del puntero determina cuánta autoridad (cuántos bytes se pueden manipular) para desreferenciar el puntero.

Por ejemplo: una desreferencia de puntero char* solo puede acceder a un byte, y una desreferencia de puntero int* puede acceder a cuatro bytes.

3. Puntero salvaje

Concepto: Un puntero salvaje es donde el puntero apunta es incognoscible (aleatorio, incorrecto, no especificado)

3.1 Causas de los punteros salvajes

1. El puntero no está inicializado

#include <stdio.h>
int main()
{
    int* p;//局部变量指针未初始化,默认为随机值
    *p = 20;
    return 0;
}

2. Acceso fuera de los límites del puntero

#include <stdio.h>
int main()
{
    int arr[10] = { 0 };
    int* p = arr;
    int i = 0;
    for (i = 0; i <= 11; i++)
    {
        //当指针指向的范围超出数组arr的范围时,p就是野指针
        *(p++) = i;
    }
    return 0;
}

3. El espacio señalado por el puntero se libera (se discutirá más adelante, pero no se discutirá aquí por el momento)

3.2 Cómo evitar los punteros salvajes

  • 1. Inicialización del puntero
  • 2. Cuidado con el puntero fuera de los límites
  • 3. El puntero que apunta al espacio se suelta incluso si se establece en NULL
  • 4. Evita devolver la dirección de una variable local
  • 5. Verifique la validez del puntero antes de usar
#include <stdio.h>
int main()
{
    int* p = NULL;
    //....
    int a = 10;
    p = &a;
    if (p != NULL)
    {
        *p = 20;
    }
    return 0;
}

4. Aritmética de punteros

4.1 Puntero + - Entero

#include<stdio.h>
int main()
{
	int x = 0;
	int* p = &x;
	printf("%p\n", p);
	printf("%p\n", p + 1);
	char* p2 = (char*)&x;
	printf("%p\n", p2);
	printf("%p\n", p2+1);
	return 0;
}

 Puntero+-entero es en realidad el número de bytes que abarca el tipo de variable señalado por el puntero multiplicado por el número que sumamos. Por ejemplo, en el ejemplo anterior, primero realizamos una operación +1 en el puntero entero p, y luego p agregada Después de que +1 se imprime en forma de dirección, en comparación con la p original, se agregan 4 bytes. De manera similar, la última p2 es un puntero de tipo char. Después de realizar la operación +1 en ella, en comparación con la original dirección p2, aumentada en 1 byte.

4.2 Puntero-Puntero

#include<stdio.h>
int main()
{
	int arr[5] = { 1,2,3,4,5 };
	int* p1 = &arr[4];
	int* p2 = &arr[0];
	printf("%d", p1 - p2);
	return 0;
}

El resultado de la resta anterior es 4. ¿Por qué el resultado es 4? Debido a que la diferencia entre p1 y p2 son 4 elementos enteros, ¿por qué enfatizamos el elemento entero en este lugar? Debido a que los tipos de puntero en ambos lados del operador son punteros enteros, de hecho , la resta de las variables de tipo puntero es la resta de la dirección representada por el puntero, y el valor resultante se divide por el espacio ocupado por el puntero. número de bytes no es fácil de entender para todos. Es un poco abstracto. Le daré un ejemplo simple. Por ejemplo, en el ejemplo anterior, el valor de dirección almacenado en p1 es 16, y el valor almacenado en p2 es 0 ., luego dividimos los 16 obtenidos al restar p1 de p2 por el tipo de dato al que apuntan p1 y p2, es decir, el tipo entero.El número de bytes que ocupa cada elemento entero es 4, por lo que el resultado después de dividir 16 por 4 es 4, es decir, el resultado de salida final en la pantalla es 4, por lo que muchos socios pequeños preguntaron, en el ejemplo anterior, si convertimos el tipo de los dos punteros en tipo char, ¿el resultado será el mismo? a 16, porque de acuerdo al método de razonamiento que di arriba, debería ser así, porque el número de bytes que ocupa el tipo char es 1, y el resultado de dividir 16 entre 1 sigue siendo 16. A continuación, mostremos el código ¿Es como nuestro razonamiento que salió como 16?

 Al igual que nuestra derivación, ¡esto confirma que nuestra derivación es correcta!

4.3 Operaciones relacionales sobre punteros

Dado que la dirección almacenada en la variable de puntero es esencialmente solo una cadena de datos, ¡el tamaño también se puede comparar! No les mostraré un ejemplo aquí, porque no es difícil de entender, ¡pero enfatizaré un punto clave para todos a continuación!

regulación estándar:

Se permite la comparación de un puntero a un elemento de matriz con un puntero a una ubicación de memoria posterior al último elemento de la matriz, pero no se permite la comparación con un puntero a una ubicación de memoria anterior al primer elemento.

5. Punteros y matrices

Veamos un ejemplo:

#include <stdio.h>
int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
    printf("%p\n", arr);
    printf("%p\n", &arr[0]);
    return 0;
}

resultado de la operación:

Se puede ver que el nombre del arreglo y la dirección del primer elemento del arreglo son iguales.

Conclusión: el nombre del arreglo representa la dirección del primer elemento del arreglo. (excepto 2 casos, que se explican en el capítulo de matrices)

Entonces es posible escribir código como este:

int arr[10] = {1,2,3,4,5,6,7,8,9,0};
int *p = arr;//p存放的是数组首元素的地址

Dado que el nombre de la matriz se puede almacenar en un puntero como una dirección, es posible que usemos el puntero para acceder a uno.

P.ej:

#include <stdio.h>
int main()
{
    int arr[] = { 1,2,3,4,5,6,7,8,9,0 };
    int* p = arr; //指针存放数组首元素的地址
    int sz = sizeof(arr) / sizeof(arr[0]);
    for (int i = 0; i < sz; i++)
    {
        printf("&arr[%d] = %p   <====> p+%d = %p\n", i, &arr[i], i, p + i);
    }
    return 0;
}

Entonces p+i en realidad calcula la dirección del subíndice i de la matriz arr.

Entonces podemos acceder a la matriz directamente a través del puntero.

como sigue:

int main()
{
	int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
	int* p = arr; //指针存放数组首元素的地址
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i));
	}
	return 0;
}

 6. Puntero secundario

Una variable de puntero también es una variable, y una variable tiene una dirección.¿Dónde se almacena la dirección de la variable de puntero? Este es el puntero secundario.

Las operaciones para punteros secundarios son:

  • *ppa elimina la referencia de la dirección en ppa para encontrar pa, y *ppa realmente accede a pa.
int b = 20;
*ppa = &b;//等价于 pa = &b;
  • **ppa primero encuentra pa a través de *ppa, y luego desreferencia pa: *pa, que encuentra a .
**ppa = 30;
//等价于*pa = 30;
//等价于a = 30;

7. Matriz de punteros

¿Una matriz de punteros es un puntero o una matriz?

Respuesta: Es una matriz. es una matriz de punteros.

Matrices Ya conocemos las matrices de enteros, las matrices de caracteres.

int arr1[5];
char arr2[6];

 ¿Qué pasa con una matriz de punteros?

int* arr3[5];//是什么?

arr3 es una matriz con cinco elementos, cada elemento es un puntero entero.

Supongo que te gusta

Origin blog.csdn.net/m0_57304511/article/details/122259605
Recomendado
Clasificación