Lenguaje C - 5 de Agosto - Estructuras y Variables

Tabla de contenido

Estructura:

El espacio ocupado por la estructura (independientemente de la alineación de la memoria):

Acceso a miembros de la estructura:

 La combinación de estructura y matriz:

La primera:

El segundo tipo:

Combinación de palabras clave de estructura y typedef:

La combinación de estructura y puntero:

Por operador de puntero "*" y accesor de miembro ".":

A través del puntero "->": 

El tamaño de la memoria de la estructura:

Los miembros de la estructura abren espacio en la memoria y el proceso completo de alineación de la memoria:

Anidamiento de estructuras:

Combinación de estructura y memoria dinámica:

variable: 

variable local:

Ciclo de vida de las variables locales:

Variables globales:

El ciclo de declaración de variables globales:

Ejercicios con estructuras:


Estructura:

La mayoría de los tipos de datos con los que hemos entrado en contacto antes son tipos de datos básicos, como tipo int y tipo doble, y la estructura es un tipo de datos personalizado, que podemos diseñar nosotros mismos de acuerdo con nuestras necesidades. tipos de datos.

Hay dos tipos de datos en lenguaje C llamados tipos de datos agregados (agregate data type), son estructuras y arreglos. Como se mencionó anteriormente, una matriz es una colección de los mismos elementos, y se puede acceder a cada elemento de la matriz a través de un subíndice.

Pero la estructura es diferente. Aunque la estructura también es una colección de algunos valores, estos valores se denominan miembros de la estructura. Los miembros de una estructura pueden tener diferentes tipos de datos. Por ejemplo, queremos crear una estructura de estudiante Los miembros del interior incluyen el nombre del estudiante, la edad, la altura, el sexo y los resultados de las pruebas. A diferencia de las matrices, cada miembro de una estructura tiene su propio nombre y también se accede a los miembros de una estructura por nombre.

La forma de la estructura de diseño es la siguiente:

struct 类型名
{
成员(变量的声明);
};//分号是必须要有的

Aquí usamos un ejemplo para entender gradualmente la estructura:

#include<stdio.h>
struct student
{
    const char *name;
    int age;
    int score;
};
int main()
{
    struct student stu1 = {"stu1",9,99};//花括号里面的内容一一对应结构体里面的成员列表
    return 0;
}

Cabe señalar aquí que si nuestro archivo es código c puro, es decir, un archivo en forma de .c, se debe agregar struct delante de estudiante, pero si es un archivo cpp, podemos usar directamente estudiante

Por ejemplo, aquí defino una estructura llamada estudiante, que consta de tres miembros, a saber, nombre, edad y grado. Defino una variable de estructura de stu1 en el programa principal.

El espacio ocupado por la estructura (independientemente de la alineación de la memoria):

A continuación, echemos un vistazo a cómo se ve la estructura en la memoria:

El diseño del tipo no ocupa memoria , por ejemplo, si uso int type, int no ocupa espacio de memoria cuando no uso int para definir variables, pero si uso int para definir variables, como int a = 0 ; entonces el tipo int ocupará 4 bytes de espacio de memoria, y también podemos mapearlo a una estructura.Solo cuando usamos el tipo de estructura para definir variables ocupará espacio.

Bajo la premisa de que no consideramos la alineación de la memoria, analicemos el espacio ocupado por una estructura:

Doy dos estructuras aquí:

uno:

struct Student
{
    char name[10];
    int age;
    int score;
}

Su estructura en la memoria debería verse así:

 

dos:

struct Student
{
    const char* name;
    int age;
    int score;
};

El tamaño de la primera estructura es de 10 bytes en la matriz de caracteres más dos tipos int para un total de 18 bytes.

En la segunda estructura, el nombre se genera como la dirección de la cadena de nombre en la estructura de almacenamiento del puntero.En el sistema de 32 bits, el tamaño del puntero es de 4 bytes, por lo que el tamaño total de la estructura es de 12 bytes. 

Acceso a miembros de la estructura:

Entonces, ¿cómo debemos acceder a los miembros de la estructura?

Seguimos usando la estructura anterior y definimos una persona llamada stu1 en el programa principal para que corresponda a los miembros de la estructura, y accedemos a los miembros a través de variables de estructura:

#include<stdio.h>
struct Student
{
    const char* name;
    int age;
    int score;
};
int main()
{
    struct Student stu1 = {"zs",12,100};
    printf("%s ",stu1.name);//通过结构体变量来进行成员的访问
    return 0;
}

resultado de la operación: 

Del mismo modo, ahora accedemos a todos los miembros de la estructura:

printf("%s,%d,%d\n",stu1.name,stu1.age,stu1.score);

Nota: El "." aquí se llama miembro de acceso. 

 La combinación de estructura y matriz:

Lo que escribimos anteriormente es el caso de un solo estudiante, stu 1. Si necesitamos almacenar la información de varios estudiantes cuando se ha definido la estructura del estudiante, será problemático para nosotros definir uno por uno. Podemos usar la matriz en esta vez Para almacenar la información del estudiante:

La primera:

Use llaves para ingresar información directamente en la matriz, acceda a los elementos a través del subíndice de la matriz y luego use el descriptor de acceso de miembros para realizar el acceso direccional a los miembros en el elemento:

#include<stdio.h>
struct Student
{
    const char *name;
    int age;
    int score;
};
int main()
{
    struct Student ar[] = {
   
   {"zs",10,100},{"lisi",9,99},{"ww",8,88}};
    int len = sizeof(ar) / sizeof(ar[0]);
    for(int i = 0;i < len;i++){
        printf("第%d个学生,姓名:%s,年龄:%d,成绩:%d\n",i,ar[i].name,ar[i].age,ar[i].score);
    }
    return 0;
}

El resultado de la operación es:

El segundo tipo:

Si configuré los estudiantes antes de definir la matriz de estructura, también es posible almacenar directamente el nombre de la variable de estructura en la matriz:

struct Student stu1 = {"zs",10,100};
    struct Student stu2 = {"lisi",9,99};
    struct Student stu3 = {"ww",8,88};
    struct Student ar[] = {stu1,stu2,stu3};
    int len = sizeof(ar) / sizeof(ar[0]);
    for(int i = 0;i < len;i++){
        printf("第%d个学生,姓名:%s,年龄:%d,成绩:%d\n",i,ar[i].name,ar[i].age,ar[i].score);
    }

El resultado de la operación es:

Ambos métodos pueden cumplir con los requisitos para acceder a matrices de estructura.El segundo método tiene un poco más de código, pero es relativamente ordenado.

Combinación de palabras clave de estructura y typedef:

Cuando definimos la estructura, no es difícil encontrar que es engorroso reescribir la estructura Student cada vez que queremos usar la estructura, por lo que podemos usar el cambio de nombre de tipo:

Por ejemplo, cambiamos directamente el nombre de struct Student a student aquí:

struct Student
{
    const char* name;
    int age;
    int score;
};
typedef struct Student student;

Simplifica el enunciado:

typedef struct Student
{
    const char* name;
    int age;
    int score;
}student;//将新类型名称添加到分号的前面

 Después de cambiar el nombre del tipo de estructura, podemos usar el nuevo nombre de tipo para definir la variable. Al escribir la declaración, también se puede omitir la estructura, que es más concisa.

La combinación de estructura y puntero:

Por operador de puntero "*" y accesor de miembro ".":

Podemos realizar un acceso direccional a los miembros de la estructura usando un puntero a la estructura que define el tipo de estructura:

    student ar[] = {
   
   {"zs",10,100},{"lisi",9,99},{"ww",8,88}};
    student *ptr = ar;
    printf("%s",(*ptr).name);

Aquí, la prioridad del operador de acceso a miembros "." es más alta que la del operador de puntero "*" , por lo que debemos agregar paréntesis al usar punteros para acceder a la estructura. De manera similar, si queremos acceder a la matriz de estructura El nombre del segundo miembro, también podemos usar la función de sumar 1 al puntero para escribir:

(*(ptr + 1)).name);//第二个成员的名字
(*(ptr + 2)).name);//第三个成员的名字

resultado de la operación:

A través del puntero "->": 

Cabe señalar aquí que el puntero también está desreferenciado.

Por ejemplo, aquí necesito acceder al miembro de nombre del primer elemento en la matriz de estructura a través de un puntero:

    student ar[] = {
   
   {"zs",10,100},{"lisi",9,99},{"ww",8,88}};
    student *ptr = ar;
    printf("%s",ptr->name);

Entonces, ¿qué necesito para usar punteros para acceder a los miembros de nombre en el segundo y tercer elemento respectivamente?

Sigue siendo el conocimiento de agregar 1 al puntero.También debido al problema de prioridad, debemos agregar paréntesis al realizar el acceso direccional:

printf("%s",(ptr+1)->name);//第二个元素中name成员的访问
printf("%s",(ptr+2)->name);//第三个元素中name成员的访问

Generemos los tres resultados y observemos los resultados en ejecución:

Como se muestra en la figura, el acceso es exitoso.

Luego usamos la palabra clave typedef para cambiar el nombre del tipo de puntero de la estructura:

student ar[] = {
   
   {"zs",10,100},{"lisi",9,99},{"ww",8,88}};
typedef struct Student *Pstu;
Pstu ptr = ar;
printf("%s\n%s\n%s\n",ptr->name,(ptr+1)->name,(ptr+2)->name);

Como se muestra en la figura, todavía muestra que la compilación es exitosa:

Podemos escribir el cambio de nombre del puntero en la estructura de redefinir el nombre del tipo directamente antes del programa principal y simplificar el código:

typedef struct Student
{
    const char *name;
    int age;
    int score;
}student,*Pstu;

Este método todavía es posible. 

Nota: Cuando usamos el método del puntero para acceder a los miembros de la estructura, se prefiere el uso del puntero al uso de la desreferencia. 

El tamaño de la memoria de la estructura:

Como se mencionó anteriormente, independientemente de la alineación de la memoria, el tamaño total de una estructura es la suma de los tamaños de letra de todos los miembros de la estructura, pero si se considera la alineación de la memoria, el resultado es diferente.

Primero echemos un vistazo a lo que es la alineación de la memoria:

struct A
{
    char a;
    int b;
    short c;
    
};
struct B
{
    short c;
    char a;
    int b;
};

En las dos estructuras de A y B, los tipos de datos de los miembros internos son los mismos, pero el orden de los miembros es diferente. Mostramos el tamaño de las dos estructuras de A y B:

De hecho, la memoria que ocupan las dos estructuras es diferente ¿A qué se debe esto?

La respuesta es que la alineación de la memoria hace que las estructuras que declaran variables en diferentes órdenes tengan diferentes tamaños.El compilador asigna un espacio de memoria adecuado para cada programa en la computadora, y todo esto se puede rastrear hasta la CPU.

Todos sabemos que la memoria juega el papel de un puente en la computadora. Es la memoria que establece la conexión entre la CPU y la memoria externa. La memoria también almacena temporalmente la información de cómputo en la CPU. Aquí nos enfocamos en la lectura de la CPU a la memoria Take method:

La forma en que la CPU lee la memoria es en bloques, el tamaño del bloque puede ser de 2, 4, 8, 16 bytes, que generalmente son múltiplos enteros de 2. Por lo tanto, cuando la CPU lee la memoria, lee uno por uno. , el tamaño del bloque se denomina (granularidad de memoria) granularidad de lectura de memoria. Dado que la memoria de lectura se lee en bloques, debe haber desperdicio de memoria.

La distribución de los miembros de la estructura en la memoria tiene las tres reglas siguientes:

La primera dirección de la variable de estructura debe ser un múltiplo entero del número de bytes ocupados por los miembros de MIN (el tipo de datos básico más grande en la variable de estructura, que especifica la alineación de la memoria).

El desplazamiento de cada miembro en la variable de estructura en relación con la primera dirección de la estructura es MIN (el tipo de datos básico del miembro ocupa un múltiplo entero del árbol de bytes y se especifica la alineación de la memoria).

El tamaño total de la estructura es un múltiplo entero de la estructura MIN (bytes ocupados por el tipo de datos básico más grande o alineación de memoria en la variable).

Nota: MIN aquí se refiere al valor mínimo entre los dos.

El primer miembro de la variable de estructura se encuentra en la dirección 0. Tomemos esta estructura como ejemplo:

struct A
{
    int a;
    char b;
    short c;
    long long d;
};

En la estructura A, el tipo de datos básico más grande es long long, que ocupa 8 bytes, y el resto son 4 bytes, 1 byte y 2 bytes, como se muestra en la figura:

En este momento, nuestra alineación de tipos de datos predeterminados es de 8 bytes y la dirección inicial es 0. La estructura comienza desde int a y ocupa cuatro bytes, seguida de char b. Como se mencionó anteriormente, la CPU lee la memoria una por una. entonces char b ocupa un byte, debido a la alineación de la memoria, debemos continuar agregando un byte de espacio detrás, este espacio no almacena datos, solo se genera porque la computadora necesita espacio de alineación de la memoria, por lo que en este momento más el anterior int type space es un total de 6 bytes, que es un múltiplo entero de 2, por lo que asignaremos directamente espacio de memoria para short, que ahora es de 8 bytes, y continuaremos agregando long long type más tarde, el tamaño total es 16 bytes. Como se mencionó anteriormente, el tamaño total de la estructura debe ser un múltiplo entero del tipo de datos básico más grande en la variable de estructura. El tipo más grande de la variable de estructura es el tipo largo largo, que es de 8 bytes. Nuestro tamaño total es 16 bytes, que es un múltiplo entero de 8, por lo que el tamaño total de esta estructura debe ser de 16 bytes.

Echemos un vistazo a los resultados de ejecución:

El resultado es correcto.

Luego, si agregamos un miembro de tipo entero a la estructura, el espacio asignado al sumar todos los tipos y contar la alineación de la memoria debe ser 20, pero 20 no es un múltiplo entero del tipo de datos básico más grande en la estructura, por lo que deberíamos Posteriormente se asignan 4 bytes más de espacio, para un tamaño total de 24 bytes. Verificamos a través del programa:

El resultado es correcto.

Como mencionamos anteriormente, la lectura de la memoria es en bloques, y hay 2, 4, 8 y otras formas de alineación de la memoria, y la alineación de la memoria se puede personalizar, entonces, ¿cómo formular la alineación de la memoria?

#progma pack(1)//对齐方式开始

Rellene los corchetes y necesita especificar unos pocos bytes como la alineación de la memoria 

Por ejemplo, usamos la estructura A anterior:

struct A
{
    int a;
    char b;
    short c;
    long long d;
}

Cuando no usamos la declaración personalizada de alineación de memoria, el programa determinará el tamaño total de la estructura final por un múltiplo entero del tipo de datos básico más grande, que es 24.

Si usamos una declaración de alineación de memoria personalizada, la establecemos en 4 y 1 aquí:

En este momento, siempre que el miembro más la alineación de la memoria sea un múltiplo entero de 4, es decir, 20. 

Si la alineación de la memoria es 1, no necesitamos asignar espacio detrás del tipo char, porque el tipo de memoria es originalmente 1, por lo que es la suma del tamaño de todos los tipos de miembros de la estructura, 19.

 Cuando terminemos de usar la alineación de memoria personalizada, naturalmente debemos finalizarla. La declaración final es:

#pragma pack()//内存对齐方式结束

#pragma pack(), como #define, es una directiva de preprocesamiento.

Los miembros de la estructura abren espacio en la memoria y el proceso completo de alineación de la memoria:

Estructura:

struct student
{
    int a;
    char b;
    short c;
    int e;
    long long d;
};

  

Como se muestra en la figura: el siguiente es el proceso de desarrollo de la memoria para cada miembro de la estructura estudiante:

1: Primero, abra espacio para el miembro a del tipo int, con un tamaño de 4.

2: debido a que el tamaño del tipo int es 4, que es un múltiplo entero del tipo char con un tamaño de 1, el espacio se abre directamente después del tipo int

3: El tamaño del tipo corto es 2, y el tamaño del tipo int anterior más el tipo char es 5, que no es un múltiplo entero de 2, por lo que una vez más abrimos 1 byte de espacio para la alineación de la memoria. esta vez, el tamaño total es 6, que es exactamente un múltiplo entero de 2, el miembro c se abre después de un byte recién abierto

4: En este momento, el tamaño total del frente suma 8, que es exactamente un múltiplo entero del tamaño del miembro e del tipo int, por lo que se desarrolla directamente en la parte posterior.

5, el tamaño anterior suma 12, que no es un múltiplo entero del miembro e del tipo long long, por lo que abrimos 4 bytes de espacio para llenar la memoria, en este momento el tamaño es 16, que es exactamente un múltiplo entero de 8, por lo que el miembro d se desarrollará más adelante.

6: El tamaño total de los miembros de la estructura es 24, usamos el programa para verificar:

el resultado es correcto 

Anidamiento de estructuras:

struct address
{
    const char *city;
    const char *street;
};

struct student
{
    const char *name;
    int age;
    struct address;
};

Del mismo modo, analicemos el proceso de asignación de memoria, incluida la alineación de memoria:

1: Los dos tipos de miembros en la estructura de dirección son ambos tipos de puntero, y el tamaño del tipo de puntero es 4, por lo que el tamaño total de la estructura de dirección es 8

2: En la estructura del estudiante, el miembro char *name se desarrolla primero en la memoria, el tamaño es de 4 bytes, el miembro de edad es de tipo int, también 4 bytes, 4 es un múltiplo entero de 4, por lo que está directamente conectado para desarrollar , la estructura de dirección El tamaño total del cuerpo es de 8 bytes, y la suma del tamaño de los miembros anteriores es de 8 bytes, que es un múltiplo entero. La memoria se abre directamente en la parte posterior y no hay alineación de memoria durante el período, por lo que el tamaño total de la estructura de estudiantes es 16.

Usamos el programa para verificar:

Como se muestra en la figura, el resultado es correcto. 

Combinación de estructura y memoria dinámica:

typedef struct student
{
    const char *name;
    int age;
};
int main()
{
    student s = {"zs",10};
    student* p = (student*)malloc(sizeof(student));
    assert(p != NULL);
    memset(p,0,sizeof(student));
    printf("%s\n",p -> name);
    free(p);
    p = NULL;
    return 0;
}

Utilice la función malloc para solicitar un espacio de almacenamiento dinámico del tamaño de un estudiante e inicialice esta área con 0. Imprimiremos el espacio inicializado que pertenece al nombre del miembro en forma de cadena, y el resultado debería estar vacío. 

resultado de la operación:

variable: 

En programación, debemos distinguir la conexión entre cada variable y descubrir la diferencia entre las variables para usar las variables correctamente. Las variables se dividen en dos tipos:

variable local:

Variables definidas dentro de funciones:

ejemplo:

En este momento, a se define en el programa principal bajo el archivo cpp testarray, por lo que a es una variable local en este momento.

Y partiendo de la premisa de que a la variable local no se le asigna un valor cuando se define, el sistema asignará un valor aleatorio a la variable local.

Ciclo de vida de las variables locales:

Definido en la función -> donde ocurre la definición -> la variable termina cuando termina la función

                                 Pila -> El desarrollo y liberación de memoria en la pila

Variables globales:

Variables definidas fuera de la función:

ejemplo:

En este momento, la variable a está definida fuera de la función principal, y a está orientada a todas las funciones en el archivo testarray.En este momento, a es una variable global.

Si a la variable global no se le asigna un valor cuando se define, el sistema asignará automáticamente el valor predeterminado (es decir, 0) a la variable global.

El ciclo de declaración de variables globales:

Cuando se inicia el programa, se generan las variables globales; cuando finaliza el programa, finalizan las variables globales.

Cuando definimos variables globales en otros archivos y modificamos el valor de las variables globales en el programa principal, el programa informará un error, pero podemos usar una palabra clave extern en el lenguaje C para realizar variables globales en diferentes archivos Enlace.

A partir de esto introducimos la palabra clave extern:

Como se muestra en la figura, definí la variable global a en el archivo my_struct y luego traté de asignar un valor a la variable a en el archivo testarray, y el programa informó un error:

​​​​​​​

Pero cuando agrego esta declaración antes del programa principal, el programa se puede compilar normalmente:

extern int a;

La función de la palabra clave extern es vincular variables globales en archivos externos.

Nota: si la variable global en este momento es una variable global estática, es decir, la palabra clave estática se agrega antes de la definición de la variable global, entonces la variable global no tiene ningún atributo vinculable y solo es visible en este archivo. la palabra clave extern todavía se usa para vincular, el programa informará un error.

static int a;

Ejercicios con estructuras:

Acceso dirigido a los miembros de la estructura y combinación con clasificación:

Escriba un programa que use el cuerpo de la institución para almacenar información de los estudiantes y use sort para ordenar las calificaciones de los estudiantes en orden ascendente. Si las calificaciones son iguales, se ordenan en orden descendente por nombre:

#include<stdio.h>
#include<assert.h>
#include<string.h>
typedef struct Student//重新定义结构体类型名
{
    const char *name;
    int age;
    int score;
}student;
void Swap(student *p,student *q)//交换函数
{
    assert(p != NULL && q != NULL);
    student temp = *p;//因为我们需要交换的是结构体中的成员,所以临时值temp也应是我们重新定义的类型
    *p = *q;
    *q = temp;
}
void Bubble_sort(student *ar,int len)//冒泡排序(针对结构体中的成员,所以形参类型为结构体类型)
{
    assert(ar != NULL && len >= 0);
    while(len--){
        int flag = 0;
        for(int i = 0;i < len;i++){
            if(ar[i].score > ar[i + 1].score){
                flag = 1;
                Swap(&ar[i],&ar[i + 1]);
            }
            else if(ar[i].score == ar[i + 1].score){//如果成绩相同的情况下
                if(strcmp(ar[i].name,ar[i + 1].name) < 0){//使用strcmp函数,如果前一个字符串返回的整数值是小于后一个字符串的则直接进行交换
                    Swap(&ar[i],&ar[i + 1]);
                }
            }
        }
        if(!flag){
            break;
        }
    }
}
int main()
{
    student ar[] = {
   
   {"zs",10,100},{"lisi",9,100},{"ww",8,100}};
    int len = sizeof(ar) / sizeof(ar[0]);
    Bubble_sort(ar,len);
    for(int i = 0;i < len;i++){
        printf("姓名:%s,成绩:%d\n",ar[i].name,ar[i].score);
    }
    return 0;
} 

resultado de la operación:

Supongo que te gusta

Origin blog.csdn.net/weixin_45571585/article/details/126176226
Recomendado
Clasificación