[Notas de estudio avanzado del lenguaje C] 4. Tipo personalizado (1) (estructura + segmento de bits)


estructura

Conocimientos básicos de estructura:

Una estructura es una colección de valores llamados variables miembro. Cada miembro de la estructura puede ser una variable de un tipo diferente.
Una matriz es una colección de elementos del mismo tipo. Una estructura también es una colección de valores, y cada miembro de una estructura puede ser de un tipo diferente.


declaración de estructura

struct tag
{
    
    
	member_list;
}variable_list;

Palabra clave de estructura: struct
estructura etiqueta: etiqueta
tipo de estructura: struct etiqueta estructura lista de miembros: lista de miembros estructura lista de variables
: lista_variable

Por ejemplo, describa a un estudiante: incluya el nombre del estudiante, la identificación del estudiante, la edad, el sexo

struct Student
{
    
    
	char name[20];//姓名
	char id[20];//学号
	int age;//年龄
	char sex[5];//性别
};//分号不能丢弃

El método de declaración de estructura anterior es una declaración completa. Por supuesto, también hay declaraciones incompletas, como omitir la etiqueta de la estructura. Por
ejemplo:

//匿名结构体类型
struct
{
    
    
	int a;
	char b;
	float c;
}x;

struct
{
    
    
	int a;
	char b;
	float c;
}*p;

Pensando: ¿Este código p = &x; es legal? ¿Funcionará correctamente en el compilador?
El compilador emitirá una advertencia: el compilador tratará las dos declaraciones anteriores como dos tipos completamente diferentes, por lo que es ilegal.
inserte la descripción de la imagen aquí
typedef -- - Redefinición de tipo Reflexión
: ¿Se puede usar typedef para redefinir tipos de estructura anónimos?
P.ej:

typedef struct
{
    
    
	int data;
	Node* next;
}Node;
//这样写代码,可以吗?

¡Este método no puede ejecutarse bajo el compilador!

Solución:

typedef struct Node
{
    
    
	int data;
	struct Node* next;
}Node;

Pensando: ¿Se puede estructurar Node* next; ser reemplazado por Node* next?

Respuesta: No, typedef redefine el tipo de estructura, siempre que el tipo de estructura se cree primero, y luego redefine un nuevo nombre de tipo para su nombre de tipo.¿Si se dice que la redefinición se usa dentro del nombre de tipo de estructura creado? ¿Empezaste a usarlo antes de que se creara? ¡Este lugar no puede viajar en el tiempo de esta manera!


Autorreferencia de estructuras

Reflexión: ¿Está bien incluir un miembro cuyo tipo es la estructura misma en una estructura?

//代码1
struct Node
{
    
    
	int data;
	struct Node next;
};
//可行否?

En caso afirmativo, ¿cuál es sizeof (struct Node)?

Entendimiento: Suponiendo que el método en el Código 1 se puede ejecutar, luego en el proceso de creación de la estructura, el
tamaño del Nodo de estructura siguiente es desconocido porque el tipo de Nodo de estructura de la estructura aún no se ha creado, y si el Nodo de estructura El tipo puede ser La creación exitosa a su vez depende de
la certeza del tamaño de la siguiente tipo. Así que los dos se contradicen. ¡Entonces el método anterior no funciona!

Corregir la autorreferencia de las estructuras:

//代码2
struct Node
{
    
    
	int data;//数据域
	struct Node* next;//指针域
};

¿Por qué es exitoso este enfoque?

Comprensión: el método de autoaplicación de la estructura aquí no es usar directamente la estructura para crear variables, sino crear un puntero al tipo de estructura. Sabemos que el tamaño del puntero no tiene nada que ver con el tipo que tiene. apunta a, solo el entorno de la plataforma., el tamaño del puntero de la plataforma de 32 bits es de 4 bytes, la plataforma de 64 bits, el tamaño del puntero es de 8 bytes. Debido a la certeza del tamaño del puntero, el tamaño total del tipo de estructura también se puede determinar cuando se hace referencia a sí mismo.


Definición e inicialización de variables de estructura

Con el tipo de estructura, ¿cómo definir variables de estructura e inicializar variables?
Ejemplo 1:

struct Point
{
    
    
	int x;
	int y;
}p1;//声明结构体类型的同时定义变量p1
struct Point p2;//定义结构体变量p2
struct Point p3 = {
    
     1,1 };//初始化:定义变量的同时给变量赋值

Ejemplo 2:

struct Stu//类型声明
{
    
    
	char name[20];//姓名
	int age;//年龄
};
struct Stu s = {
    
     "Student_zhang",20 };//初始化

Ejemplo 3:

struct Point
{
    
    
	int x;
	int y;
}p1;//声明结构体类型的同时定义变量p1
struct Node
{
    
    
	int data;
	struct Point p;
	struct Node* next;
}n1 = {
    
     10,{
    
    4,5},NULL };//结构体嵌套初始化
struct Node n2 = {
    
     20,{
    
    1,2},NULL };//结构体嵌套初始化

Alineación de memoria de estructura

Hemos dominado el uso básico de las estructuras, así que analicemos un problema más profundo ahora;
cualquier tipo de datos debe tener su tamaño de espacio de memoria correspondiente, como el tamaño de caracteres es de 1 byte, el tipo int es de 4 bytes, el tipo doble es de 8 bytes, etc. Sin un tamaño determinado, es imposible saber cuánto espacio de memoria se debe asignar a una variable de este tipo en el momento de su creación. Entonces, ¿cuál es el tamaño de la estructura? ¿Cómo calcularlo?
Esto implica un sitio de prueba popular: ¡alineación de memoria estructural!

Reglas de alineación de memoria de estructura:
1. El primer miembro está en la dirección cuyo desplazamiento es O desde la variable de estructura.
⒉ Otras variables miembro deben alinearse con la dirección de un múltiplo entero de un número determinado (número de alineación).
①Número de alineación = El valor más pequeño del número de alineación predeterminado del compilador y el tamaño del miembro.
②El valor predeterminado en VS es 8. No existe un valor predeterminado en Linux, y su propio tamaño es el número de alineación
3. El tamaño total de la estructura es un múltiplo entero del número máximo de alineación (cada variable miembro tiene un número de alineación ).
4. Si una estructura está anidada, la estructura anidada se alinea con un múltiplo entero de su propio número máximo de alineación y el tamaño total de la estructura es un número entero de todos los números máximos de alineación (incluido el número de alineación de las estructuras anidadas). .

Atención especial: En el punto 4, la estructura anidada no está alineada con un múltiplo entero del tamaño total de la estructura, sino con un múltiplo entero del número máximo de alineación de la estructura misma.


Ejercicio uno:

#include<stdio.h>
	struct s1
{
    
    
	char c1;
	int i;
	char c2;
};
int main()
{
    
    
	printf("%d\n", sizeof(struct s1));
	return 0;
}

Análisis:
inserte la descripción de la imagen aquí
Los resultados muestran:
inserte la descripción de la imagen aquí


Ejercicio 2:

#include<stdio.h>
struct s2
{
    
    
	char c1;
	char c2;
	int i;
};
int main()
{
    
    
	printf("%d\n", sizeof(struct s2));
	return 0;
}

analizar:
inserte la descripción de la imagen aquí

Los resultados muestran:
inserte la descripción de la imagen aquí


Ejercicio 3:

#include<stdio.h>
struct s3
{
    
    
	double d;
	char c;
	int i;
};
int main()
{
    
    
	printf("%d\n", sizeof(struct s3));
	return 0;
}

Análisis:
inserte la descripción de la imagen aquí
Los resultados muestran:
inserte la descripción de la imagen aquí


Ejercicio 4:
Problema de anidamiento de estructuras

#include<stdio.h>
struct s3
{
    
    
	double d;
	char c;
	int i;
};
struct s4
{
    
    
	char c1;
	struct s3 s3;
	double d;
};
int main()
{
    
    
	printf("%d\n", sizeof(struct s4));
	return 0;
}

analizar:
inserte la descripción de la imagen aquí

Los resultados muestran:
inserte la descripción de la imagen aquí


Pensando: ¿Por qué existe la alineación de la memoria?

La mayoría de los materiales de referencia dicen:
1. Motivo de la plataforma (motivo del trasplante): no todas las plataformas de hardware pueden acceder a ningún dato en ninguna dirección, algunas plataformas de hardware solo pueden obtener ciertos datos en ciertas direcciones Datos de un tipo específico, de lo contrario, se produce una excepción de hardware arrojado
⒉ Razones de rendimiento: las estructuras de datos (especialmente las pilas) deben alinearse en los límites naturales tanto como sea posible. La razón es que para acceder a la memoria no alineada, el procesador necesita hacer dos accesos a la memoria; los accesos a la memoria alineada requieren solo un acceso.
En general: la alineación de la memoria de las estructuras es la práctica de intercambiar espacio por tiempo.

A la hora de diseñar la estructura, no solo debemos satisfacer la alineación, sino también ahorrar espacio ¿Cómo hacerlo?

Mantenga juntos a los miembros que ocupan menos espacio tanto como sea posible.

Por ejemplo, cambie la forma del ejercicio 1 a la forma del ejercicio 2:

	struct s1
{
    
    
	char c1;
	int i;
	char c2;
};
struct s2
{
    
    
	char c1;
	char c2;
	int i;
};

Los miembros de los tipos s1 y s2 son exactamente iguales, pero el tamaño del espacio ocupado por s1 y s2 es diferente. El tamaño del primero es de 12 bytes y el tamaño del segundo es de 8 bytes. Obviamente, la utilización del espacio la eficiencia del último método es mayor.


Modificar la alineación por defecto

#pragma establece la alineación predeterminada

Usando #pragma, se puede usar para cambiar nuestro número de alineación predeterminado.
Ejemplo:

#include <stdio.h>
#pragma pack(8)//设置默认对齐数为8
struct s1
{
    
    
	char c1;
	int i;
	char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认

#pragma pack(1)//设置默认对齐数为1
struct s2
{
    
    
	char c1;
	int i;
	char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
int main()
{
    
    
	printf("%d\n", sizeof(struct s1));
	printf("%d\n", sizeof(struct s2));
	return 0;
}

¿Cuál es la salida del código anterior?

Análisis: El tamaño de la estructura s1 es 12. Esto se explica en detalle en el Ejercicio 1. La clave es ¿cuál es el tamaño de s2?
De acuerdo con las reglas de alineación de la memoria de estructura, el número de alineación predeterminado es 1, luego:

inserte la descripción de la imagen aquí

Los resultados muestran:
inserte la descripción de la imagen aquí

Conclusión: cuando la alineación de la estructura no es adecuada, podemos cambiar el número de alineación predeterminado para satisfacer las necesidades.


compensación de

Esta es una macro que calcula el desplazamiento de un miembro de estructura en relación con el inicio de la estructura

Pregunta de prueba escrita de cierto grado:
Contenido: Escribir una macro para calcular el desplazamiento de una variable en la estructura relativa a la primera dirección, y dar una explicación.
Investigación: Implementación de la macro de desplazamiento

#include<stdio.h>
#include<stdlib.h>
struct s2
{
    
    
	char c1;
	int i;
	char c2;
};
int main()
{
    
    
	printf("%d\n", offsetof(struct s2, c1));
	printf("%d\n", offsetof(struct s2, i));
	printf("%d\n", offsetof(struct s2, c2));
	return 0;
}

inserte la descripción de la imagen aquí


parámetro de estructura

Ejemplo:

#include <stdio.h>
struct S
{
    
    
	int data[1000];
	int num;
};
struct S s = {
    
     {
    
    1, 2, 3, 4}, 1000 };
//结构体传参
void print1(struct S s)
{
    
    
	printf("%d \n", s.num);
}
// 结构体地址传参
void print2(struct S* ps)
{
    
    
	printf("%d \n", ps->num);
}
int main()
{
    
    
	print1(s);//传结构体
	print2(&s);//传结构体地址
	return 0;
}

Hay dos formas de pasar parámetros en una estructura:
una es pasar un objeto de estructura (pasar por valor), que corresponde a la forma de la función print1, y la
otra es pasar la dirección de la estructura (por dirección), que corresponde a la forma de la función print2.

Pensando: ¿Cuál de las funciones print1 e print2 anteriores es mejor?

La respuesta es: se prefiere la función print2.

razón︰

Al pasar parámetros a una función, los parámetros deben colocarse en la pila, lo que dará como resultado una sobrecarga del sistema en términos de tiempo y espacio.
Si se pasa un objeto de estructura, la estructura es demasiado grande y la sobrecarga del sistema del apilamiento de parámetros es relativamente grande, por lo que se degradará el rendimiento.

Conclusión: al pasar parámetros en una estructura, debe pasar la dirección de la estructura.


segmento de bits

Qué es un campo de bits
La declaración y la estructura de un campo de bits son similares, con dos diferencias:
1. Los miembros de un campo de bits deben ser int, int sin signo o int con signo.
2. El nombre del miembro del campo de bits va seguido de dos puntos y un número.
Ejemplo:

#include<stdio.h>
struct A
{
    
    
	int _a : 2;
	int _b : 5;
	int _c : 10;
	int _d : 30;
};
int main()
{
    
    
	printf("%d\n", sizeof(struct A));
	return 0;
}

La estructura A es un tipo de segmento de bits, entonces, ¿cuál es el tamaño de A?
Para responder a la pregunta anterior, primero debemos comprender el significado de dos puntos y un número después del nombre del miembro del segmento de bits. De hecho, los números después de los dos puntos representan bits binarios.El análisis específico es el siguiente:
inserte la descripción de la imagen aquí

Extensión:
①En la prueba real, se puede usar el tipo entero.
②Solo aparece un tipo en el mismo segmento de bit. Por ejemplo, el
tipo int es diferente del tipo int sin signo y no puede estar en el mismo segmento de bit al mismo tiempo.


Asignación de memoria para campos de bits

1. Los miembros del campo de bits pueden ser int unsigned intsigned int o char (perteneciente a la familia de enteros) tipo
2. El espacio del campo de bits es de 4 bytes ([int) o 1 byte (char) según sea necesario. arriba.
3. El segmento de bits involucra muchos factores inciertos. El segmento de bits no es multiplataforma. Los programas que se enfocan en la portabilidad deben evitar usar el segmento de bits.

Estos factores inciertos se reflejan en:
①¿Se va a desperdiciar el espacio?
② ¿Se usa el espacio de izquierda a derecha o de derecha a izquierda?
Por ejemplo, VS2019: ① abra 1/4 bytes primero, ② use de derecha a izquierda, el espacio se desperdiciará

Ejemplo:

#include<stdio.h>
		struct S
{
    
    
	char a : 3;
	char b : 4;
	char c : 5;
	char d : 4;
};
int main()
{
    
    
	struct S s = {
    
     0 };
	s.a = 10;
	s.b = 12;
	s.c = 3;
	s.d = 4;
	return 0;
}

Antes de la ejecución:
inserte la descripción de la imagen aquí

Después de la ejecución:
inserte la descripción de la imagen aquí

analizar:
inserte la descripción de la imagen aquí


Problemas multiplataforma con campos de bits

1. No está claro si el campo de bit int se trata como un número con signo o sin signo.
2. No se puede determinar el número máximo de bits en el segmento de bits. (máquina de 16 bits máximo 16, máquina de 32 bits máximo 32), escrito como 27, habrá problemas en la máquina de 16 bits.
3. No está definido si los miembros de un segmento de bits se asignan en la memoria de izquierda a derecha o de derecha a izquierda.
4. Cuando una estructura contiene dos campos de bits, y los miembros del segundo campo de bits son relativamente grandes y no pueden acomodar los bits restantes del primer campo de bits
, no está claro si descartar los bits restantes o usarlos.

Resumen:
en comparación con la estructura, el segmento de bits puede lograr el mismo efecto, pero puede ahorrar espacio (ventaja), pero el segmento de bits tiene problemas multiplataforma (desventaja).


Escenarios de aplicación para segmentos de bits

Paquete de protocolo de transferencia de red (se mencionará el paquete de protocolo de transferencia de datos en la red informática)
inserte la descripción de la imagen aquí

Versión china:

inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/QIYICat/article/details/118251907
Recomendado
Clasificación