07- puntero de lenguaje c (lenguaje C)

introducción de un puntero

1. En general, un byte en la memoria se denomina unidad de memoria.
2. Para acceder correctamente a estas unidades de memoria, cada unidad de memoria debe estar numerada. De acuerdo con el número de serie de una unidad de memoria, la unidad de memoria se puede encontrar con precisión. El número de una unidad de memoria también se denomina dirección , y esta dirección suele denominarse puntero .
3. Si se define una variable en el programa, cuando se compila o ejecuta el programa, el sistema asignará una unidad de memoria a la variable y determinará su dirección de memoria (número).
4. La dirección de la variable es el puntero de la variable, y la variable que almacena la dirección de la variable es una variable de puntero .
5. El puntero de la unidad de memoria y el contenido de la unidad de memoria son dos conceptos diferentes. Se puede usar un ejemplo popular para ilustrar la relación entre ellos. Cuando vayamos al banco a depositar y retirar, el personal del banco encontrará nuestro comprobante de depósito de acuerdo con nuestro número de cuenta y, después de encontrarlo, escribirá el monto del depósito y retiro en el comprobante de depósito. Aquí, el número de cuenta es el indicador del certificado de depósito y el monto del depósito es el contenido del certificado de depósito. Para una unidad de memoria, la dirección de la unidad es el puntero y los datos almacenados en él son el contenido de la unidad.

Definición y uso de dos variables de puntero

2.1 Sintaxis de definición de variable de puntero

Tipo de datos  *nombre de variable de puntero , nota: 1. El tipo de datos son todos los tipos de datos
compatibles con el lenguaje C. 2. El nombre de la variable de puntero sigue las reglas de nomenclatura de las variables del lenguaje C.

Ejemplo:

int *p; //定义了一个指针变量p,简称指针p,p是变量, int *是类型
char* p2;

También consideramos que los punteros son un tipo de datos .

2.2 Asignación de variables de puntero

1) El valor de la variable puntero significa que el puntero apunta al espacio de memoria cuya primera dirección es este valor .

2) La asignación de variable de puntero es la dirección de otras variables: &: operador de dirección de toma .

//指针变量 = &变量名;
&:取地址运算符
int a = 10;
int *p = &a;

3) El espacio de memoria apuntado por la operación de variable de puntero se
puede acceder y modificar a través de la variable de puntero en el espacio de memoria apuntado .

*:指针运算符(或称“间接访问” 运算)。
int a = 10;
int *p;
p = &a;
printf("*p: %d\n", *p);
*p = 100;
printf("*p: %d\n", *p);

4) La variable de puntero es la misma que la variable ordinaria, si no se inicializa después de la definición, el valor de la variable de puntero es incierto .

5) Puntero salvaje
Debido a que el valor de la variable puntero es incierto, llamamos a este puntero "puntero salvaje".
Daño de los punteros salvajes: debido a que el espacio al que apunta el puntero es incierto, el puntero puede operar en un espacio de memoria ilegal, lo que hace que el programa se bloquee. 

int a = 100;
int *p;
*p = 1000;
/*因为p没有初始化/赋值,所以p的值是不确定的,如果此时p的值恰好等于a的地址(p == &a), 那么*p=1000将a的值修改为如果p的值恰好是内存上一块只读的内存空间,*p = 1000将导致程序异常退出,你可能会看到程序运行报错(段错误/核心内容*/

6) Puntero nulo
Para marcar que la variable puntero no apunta a ninguna variable (libre y disponible), en lenguaje C se puede asignar NULL a este puntero, marcando así este puntero como puntero nulo.

int *p = NULL;

NULL es una macro constante con valor 0:

#define NULL ((void *)0)

Nota: La función del puntero nulo es evitar que la variable del puntero se convierta en un puntero salvaje . Si usa * para acceder al espacio de memoria señalado por el puntero nulo, el programa también informará de un error.

7) Preguntas de prueba escritas: los sistemas integrados a menudo se caracterizan por requerir que los programadores accedan a una ubicación de memoria específica . En un proyecto, se requiere establecer el valor de una variable entera cuya dirección absoluta es 0x67a9 a 0xaa66

//方法1:
int *ptr;
ptr = (int *)0x67a9; //在内存地址编号的前面加上(int *)将地址编号这个无符号整型数据强制转换为
//(int*)指针类型,这样赋值符号左值和右值的数据类型一致
*ptr = 0xaa66;
//方法2:
*(int *)(0x67a9) = 0xaa55;

Nota: en el trabajo real, generalmente rara vez asignamos una determinada dirección de memoria a una variable de puntero , porque los programadores generalmente no saben qué dirección de memoria está disponible. ! !

2.3 Diferencias entre diferentes tipos de variables de puntero

1. ¿Cuáles son las similitudes entre int *p1 y char *p2?

int x = 100;
int *p1 = &x;
char y = 'A';
char *p2 = &y;

Mismo punto:

  • son variables de puntero
  • se utilizan para almacenar un número de dirección de memoria
  • El espacio de memoria ocupado es el mismo
int *p1;
char *p2;
printf("%d %d\n", sizeof(p1), sizeof(p2));

Encontramos que el espacio de memoria ocupado por p1 y p2 es 4/8, el resultado es 4 en la máquina de 32 bits y el resultado es 8 en la máquina de 64 bits.
pensar:

¿Por qué el espacio de memoria está ocupado por variables de puntero de 4 u 8 bytes?
¡Porque la variable puntero contiene el número de una dirección de memoria!
El valor máximo del número de dirección de memoria de una máquina de 32 bits es 2^32-1, que se puede almacenar en una variable de 4 bytes.
El número máximo de direcciones de memoria de una máquina de 64 bits es 2^64-1, que se puede almacenar en una variable de 8 bytes.

2. ¿Cuál es la diferencia entre int *p1 y char *p2?
Antes que nada, debemos saber: lo que se almacena en la memoria es solo binario.
La razón por la que existen tipos de datos como int float char es que los programadores esperan tratar el binario almacenado en la memoria como un determinado tipo de datos.

int x = 65;
printf("%c\n", x);
  • La función de int *p1 es que la variable puntero p1 trata el binario en el espacio de memoria al que apunta como un tipo int .
  • La función de char *p2 es que la variable puntero p2 trata el binario en el espacio de memoria al que apunta como un tipo char .

3. La diferencia entre p1++ y p2++

#include <iostream>

int main() {
    int x = 10;
    int *p1 = &x;
    char y = 'A';
    char *p2 = &y;
    printf("p1: %p, p2: %p\n", p1, p2);
    p1++;
    p2++;
    printf("p1: %p, p2: %p\n", p1, p2);
    return 0;
}

La diferencia entre el valor de p1 después del autoincremento y antes del autoincremento es 4 .
La diferencia entre el valor de p2 después del autoincremento y antes del autoincremento es 1 .
La variable de puntero + n significa que el puntero no se compensa con n bytes, pero la variable de puntero se compensa con n tipos de datos, por ejemplo: p1+3, lo que significa que el puntero p1 se compensa con 3 datos de tipo int
, y el puntero El valor de la variable p1 +12 ( 3*sizeof(int) ).

Tres punteros y matrices

3.1 Punteros de matriz

1. Una variable tiene una dirección y una matriz contiene varios elementos, cada elemento de la matriz ocupa una unidad de almacenamiento en la memoria y todos tienen direcciones correspondientes. El llamado puntero de matriz se refiere a la dirección inicial de la matriz .
2. El nombre de la matriz indica la primera dirección de la matriz, por lo que el nombre de la matriz también es un puntero .
3. Acceda a los elementos de la matriz a través del nombre de la matriz.

int ch[] = {1,2,3,4};
//假如我们想访问ch中的第3个元素:ch[3] == 4
//我们也可以通过指针法引用数组中的元素:比如 *(ch + 3)
//那么,如果想访问第n个元素呢?
*(ch + n) 注意:n<=sizeof(ch)/sizeof(ch[0])-1

4. Ejercicio: Si hay un arreglo int a[4], escribe código para lograr las siguientes funciones:

  • Asigne un valor a cada elemento de la matriz a ingresando un número desde el teclado
  • Imprime la dirección de cada elemento en la matriz a
  • Imprima el valor de cada elemento en la matriz a por método de puntero
#include <iostream>

int main() {
    int a[4];
    int i;
    for (i = 0; i < 4; i++)
        scanf("%d", &a[i]);
    for (i = 0; i < 4; i++) {
        printf("%p %d\\n", &a[i], *(a+i));
    }
    }

5. Acceso indirecto a matrices a través de variables de puntero

#include <iostream>

int main() {
    int a[4] = {1,2,3,4};
    int *p;
    p = a;
    *(p + 2) = 100;
    char ch[] = {'a', 'b', 'c'};
    char *p2;
    p2 = ch;
    *(p2+1) = 'A';
    }
#include <iostream>

int main() {
    int ch[] = {1, 2, 3, 4};
    printf("%d\n", ch[4]);
    printf("%p %p\n", &ch[3], &ch[4]);
    //数组名:数组的首地址
    printf("ch: %p\n", ch);
    //数组中的第0个元素的地址:数组的首地址
    printf("&ch[0]: %p\n", &ch[0]);
    printf("%d %d\n", ch[3], *(ch+3));
    int a[4];
    int i;
    for (i = 0; i < 4; i++)
    {
        scanf("%d", &a[i]); //a+i
        getchar();
    }
    //打印数组中每个元素的地址
    for (i = 0; i < 4; i++)
        printf("%p\n", &a[i]);
    //通过指针法将数组a中的每一个元素的值打印出来
    for (i = 0; i < 4; i++)
        printf("%d\n", *(a+i));
    int *p;
    p = a; //指针p指向了数组a

    //指针指向了一个数组,可以将指针当数组看待
    for (i = 0; i < 4; i++)
        printf("%d\n", p[i]); //通过下标发访问数组中的元素
        // printf("%d\n", *(p+i));
    }

6. Puntero de matriz fuera de los límites

#include <iostream>

int main() {
    int b[4] = {10, 20, 30, 40};
    int a[4];
    a[4] = 100;
    printf("a[4]: %d\n", a[4]);
    printf("b[0]: %d\n", b[0]);
    printf("a: %u, b: %u\n", &a, &b); //打印数组a和b的首地址
    }

3.2 Matriz de punteros

1. Como su nombre lo indica, la matriz de punteros es: una matriz que almacena punteros, que es esencialmente una matriz, y cada elemento de la matriz es un puntero 

#include <stdio.h>

int main()
{
    int a = 10, b = 20, c = 30;
    int *p[3];
    p[0] = &a;
    p[1] = &b;
    p[2] = &c;
    return 0;
}

2. Nota: int *p[3], es equivalente a (int *) p[3], porque [] tiene una prioridad más alta que * y coincide con p primero.
3. Razonamiento: ¿Cómo almacenar los nombres de 10 personas en una matriz?
char *name[10] = {"zhangsan", "lisi", "wangwu", "zhaoliu, "tianqi"}; Las primeras direcciones
de 10 constantes de cadena se guardan en la matriz de nombres (nota: las constantes de cadena no se guardan pero es la primera dirección de la constante).

3.3 La dirección de la variable puntero

1. Cuando definimos una variable de puntero , el compilador asignará un espacio para almacenar el valor de la variable de puntero. El espacio de memoria asignado debe tener un código de dirección, por lo que el código de dirección debe ser la dirección de la variable de puntero. .

#include <stdio.h>

int main()
{
    int a = 10;
    int *p;
    p = &a;
    //将指针变量p的值以及变量a的地址打印出来(结果应该是两者相等)
    printf("p: %p, &a: %p\n", p, &a);
    //打印指针变量p的地址(存储指针变量p的内存空间的首地址)
    printf("&p: %p\n", &p);
    return 0;
}

2. Énfasis: el valor de la variable puntero p guarda la dirección 0x300800 de otra variable a, y la dirección de la variable puntero es la primera dirección del espacio de memoria que almacena el valor de la variable puntero p: 0x3007F8, el valor guardado en este espacio es 0x300800 

3.4 Punteros de nivel 1 como parámetros formales de funciones

1. El parámetro formal de la función es una matriz
Si ​​el parámetro formal de la función es una matriz, el método de definición del parámetro formal es el siguiente:

void func(int a[], int n)
{}

También podemos definir parámetros formales como tipos de puntero :
 

void func(int *a, int n)
{}

¡En el trabajo real generalmente usamos el segundo método!

2. Al llamar a una función, debe pasar una cadena, puede diseñar el parámetro formal como un tipo char *

#include <stdio.h>

void func(char *p) //调用函数时将字符串的地址赋值给指针变量p
{
    printf("%c\n", p[0]);   //返回值为 'h'
}
int main()
{
    func("hello");
    return 0;
}

3. Cuando el parámetro formal es una matriz, ¿cómo obtener la longitud de la matriz? //tamaño de( )

¿Por qué los valores de sizeof(a) y sizeof(b) son ambos 8 ? Razón: ¡El compilador trata a y b como punteros
durante la compilación ! !

4. Nota: si el parámetro formal de la función es un puntero , el valor del puntero generalmente se juzga primero en el cuerpo de la función para determinar si el valor del puntero es NULL

3.5 Puntero secundario

1. Utilice una variable de puntero para guardar la dirección de una variable de puntero de primer nivel . Llamamos a este puntero puntero de segundo nivel
. 2. La definición de un puntero de segundo nivel:

  • tipo de datos **nombre de variable ;

3. Aplicación del puntero secundario 

void func_1(){
    //二级指针的使用
    int a = 10;
    int *p = &a;
    int **p2 = &p; //二级指针p2保存了一级指针p的地址(p2指向了p )
    int ***p3 = &p2; //三级指针
    //*p2 == p == &a
    printf("%p %p %p\n", *p2, p, &a);  //000000000061FDE4 000000000061FDE4 
    //**p2 == *p == *(&a) == a
    printf("%d %d %d %d\n", **p2, *p, *(&a), a);   //10 10 10 10

    **p2 = 100;
    printf("%d %d %d %d\n", **p2, *p, *(&a), a);  //100 100 100 100
    //*p3 == p2 = &p
    //**p3 == *p2 == p == &a
    //***p3 == **p2 == *p == *(&a) == a
    printf("***p3: %d\n", ***p3);   //***p3: 100
}

3.6 Asignación de memoria

1. En el trabajo real, si necesitamos almacenar varios datos, muchos estudiantes primero piensan en usar matrices, pero debido a que la longitud de la matriz se fija después de la definición, a menudo no es lo suficientemente flexible .
2. Primero podemos definir una variable de puntero de acuerdo con el tipo de datos que se almacenarán , por ejemplo: int *p, y luego usar
la función malloc
para asignar el espacio de acuerdo con las necesidades reales.

3. función malloc :

#include <stdlib.h>
void *malloc(size_t size);

Función: la función malloc se aplica a un espacio de memoria de bytes de tamaño como el sistema y devuelve un puntero, que apunta a la primera dirección del espacio de memoria asignado, y el espacio de memoria solicitado está en el "montón". ¡El espacio en el montón debe aplicarse y liberarse manualmente ! ! De lo contrario, provocará una pérdida de memoria.

int *p;
//假如我们需要存储10个int类型的数据
p = (int *)malloc(10*sizeof(int));

Nota: El espacio asignado es 10*sizeof(int), porque el espacio asignado por malloc está en bytes.

int *p;
//通过指针变量p操作一块空间,可以存储4个int数据
//向系统申请 4*sizeof(int)字节的内存空间
p = (int *)malloc(4*sizeof(int)); //在堆上申请了4*sizeof(int)字节的内存空间
//p的值:申请到的堆上的内存空间的首地址 (指针p指向申请到的堆上的空间)
printf("p: %p\n", p);
//通过指针变量p 来操作申请到的堆空间
p[0] = 100;
p[1] = 200;
*(p+2) = 300;
*(p+3) = 400;

4. Liberación de memoria: función libre 

#include <stdlib.h>
void free(void *ptr);

Función: liberar el espacio de memoria señalado por ptr

Nota: ¡
La función gratuita no modifica el valor de la variable puntero! Pero una vez que se completa la ejecución de free, ¡el contenido del espacio de direcciones original señalado por el puntero es incierto! !
Pregunta: ¿Qué hace exactamente
liberar espacio ?
Lo más importante es: ¡ dígale al sistema que este espacio de memoria puede ser utilizado por otros ! ! !

  • El archivo fuente donde se encuentra malloc : #include <stdlib.h>
  • El archivo fuente donde se encuentra strcpy : #include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[]){
    char *p;
    //malloc分配的空间是在堆上的,需要手动释放
    p = (char *)malloc(10);
    strcpy(p, "hello");
    printf("p所指向的空间的内容: %s\n", p); //结果是hello

    //将p所指向的地址空间的首地址打印出来(就是将指针变量p的值打印出来)
    printf("p的值: %p\n", p);
    free(p);
    //将p所指向的地址空间的首地址打印出来
    printf("p的值: %p\n", p);

    //仔细观察,下面这条打印语句的结果
    printf("p所指向的空间的内容: %s\n", p);  //结果不是hello

    strcpy(p, "world");
    //再仔细观察,下面这条打印语句的结果
    printf("p所指向的空间的内容: %s\n", p);  //结果是world

    return 0;
}

Use habilidades después de que se llame a la función gratuita: 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[]){
    char *p;
    //malloc分配的空间是在堆上的,需要手动释放
    p = (char *)malloc(10);
    strcpy(p, "hello");
    
    //释放申请的内容
    free(p);
    p = NULL;
    return 0;
}

5. ¿Qué pasa si el espacio previamente asignado no es suficiente?
Podemos usar la función realloc

#include <stdlib.h>
void *realloc(void *ptr, size_t size);

Función: asigne un nuevo espacio de memoria especificado por tamaño en el montón, la unidad de tamaño de espacio es byte, y también copie el contenido en el espacio señalado por ptr al nuevo espacio de memoria, y finalmente devuelva la nueva dirección de frente de espacio de memoria .
Código de muestra: 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
    char *p;
    p = (char *)malloc(10);
    strcpy(p, "hello"); //向分配的空间中拷贝字符串
    printf("p所指向空间的首地址: %p\n", p);
    printf("p所指向空间的内容: %s\n", p);
    p = (char *)realloc(p, 20); //重新分配新的空间
    printf("p所指向新的空间的首地址: %p\n", p);
    printf("p所指向新的空间的内容: %s\n", p);
    //注意:分配的新的空间的首地址有可能有之前分配的空间首地址一样,也有可能不一样
    strcat(p, " world"); //追加字符串
    printf("p所指向新的空间的内容: %s\n", p);
    return 0;
}

6. Piense en un escenario, char *dest, *src, copie el contenido del espacio de direcciones apuntado por src al espacio de direcciones apuntado por dest a través de una función, pero suponga que no conocemos la longitud de src antes de llamar al función, en este momento necesitamos diseñar el parámetro formal de la función como un puntero secundario !

void test2(char **dest, char *src){
    //通过二级指针dest给形参一级指针dest分配内存空间!
    *dest = (char *)malloc(strlen(src) + 1);
    if (NULL == *dest || NULL == src)
        return ;
    strcpy(*dest, src);
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void func4(char **dst)
{
    //*dst == p
    *dst = (char *)malloc(10); //在堆上申请了10个字节
    strcpy(*dst, "hello");
}

int main()
{
    char *p; //指针指向某个函数调用结束后 在函数体中申请的堆空间的首地址
    /*
    * 既然我希望让p指向一块堆空间,其实就是希望对p进行赋值,赋值为在函数中申请的堆空间的首地址
    * 如何在函数中对p进行赋值呢?必须在调用函数的时候传递p的地址!!!!
    * */
    func4(&p);
    printf("%s\n", p);
    free(p);
    return 0;
}

Supongo que te gusta

Origin blog.csdn.net/March_A/article/details/131333982
Recomendado
Clasificación