[Lenguaje C] ☀️Tipo personalizado (estructura + segmento de bits + enumeración + unión) [colección recomendada]

contenido

1. Estructura

¿Por qué hay una estructura?

declaración de estructura

Conocimientos básicos de estructura.

declaración de estructura

Autorreferencia de estructuras

Definición e inicialización de variables de estructura

acceso a los miembros de la estructura

1. Objetos de estructura Miembros de estructura

 2. Puntero de estructura -> miembro de estructura

parámetro de estructura 

Alineación de memoria de estructura

 Reglas de alineación

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

Modificar la alineación por defecto

En segundo lugar, el segmento

¿Qué es un segmento de bits?

Asignación de memoria para campos de bits

Problemas multiplataforma con campos de bits

Aplicación de segmentos de bits

3. Constantes de enumeración

¿Qué es una enumeración?

Definición de tipos de enumeración

¿Cuál es el valor?

Ventajas de los tipos de enumeración

4. Unión (comunidad)

Definición de tipos de unión

declaración de tipo de unión


1. Estructura

¿Por qué hay una estructura?

Los tipos de datos que aprendimos anteriormente: char, int, double y punteros no son suficientes para representar objetos.Si queremos representar a una persona, ¿podemos usar un número para representarla? Definitivamente no es posible, es necesario saber que las personas son objetos complejos y no pueden ser representados simplemente por un número determinado, para representar a una persona se necesitan muchos aspectos, como el nombre, género, edad, etc. Para representar personas, tenemos que crear un tipo complejo, y hay un tipo de estructura en lenguaje C

La estructura es un punto de conocimiento particularmente importante en el lenguaje C. La estructura permite que el lenguaje C describa tipos complejos.

El tipo de estructura también necesita ser creado por la palabra

declaración de 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

declaración de estructura

Describa a una persona:

Forma 1:

struct Peo     //声明了一个结构体类型
{
	char name[20];
	int age;
};

Esta es la declaración de la estructura, struct es la palabra clave de la estructura, Peo es la etiqueta de la estructura (nombre del tipo de estructura) y las llaves son las variables miembros de la estructura.

Los miembros de la estructura pueden ser escalares, matrices, punteros, estructuras

 Forma 2:

struct Peo
{
	char name[20];
	int age;
}p1, p2   //全局变量

El método 2 consiste en crear dos variables de estructura de tipo struct Peo cuando se declara la estructura, pero debe tenerse en cuenta que p1 y p2 obtenidas por este método de creación son variables globales con un alcance amplio, lo que generalmente no se recomienda.

Modo 3 (tipo de estructura anónima)

struct 
{
	char name[20];
	int age;
}p1, p2;
struct 
{
	char name[20];
	int age;
}p1;
struct 
{
	char name[20];
	int age;
}*p;

El primer código omite directamente la etiqueta de estructura (nombre del tipo de estructura), de modo que las variables de estructura p1 y p2 solo se pueden crear después de crear la estructura y no se pueden crear en la siguiente función principal. Relativamente limitado, no recomendado

El segundo código crea un puntero p más tarde, pero si se basa en el segundo código, es ilegal escribir p = &p1; a continuación, y el compilador tratará las dos declaraciones anteriores como dos tipos completamente diferentes. entonces es ilegal

Autorreferencia de estructuras

Cuando usamos la estructura, ¿podemos escribir la estructura en la estructura al declarar la estructura?

El siguiente código:

struct Peo
{
	char name[20];
	int age;
	struct Peo next;
};

Cabe señalar que esto no es aceptable, porque si está escrito así, ¿cuál debería ser el tamaño de la estructura? seguro que no sale

Ortografía correcta

struct Peo
{
	int data;
	struct Peo* next;
};

Poner un puntero de estructura del mismo tipo en la estructura.

Definición e inicialización de variables de estructura

Cómo definir variables de estructura, consulte el siguiente código:

Código 1:

#include <stdio.h>
#include <string.h>
struct Peo
{
	char name[20];
	int age;
};
int main()
{
	struct Peo p = { "张三",18 };   //通过结构体类型来创建结构体变量并初始化
	printf("%s  %d\n", p.name, p.age);
	//修改里面的内容
	strcpy(p.name, "李四");
	p.age = 20;
	printf("%s  %d\n", p.name, p.age);
	return 0;
}

A través de este código, se crea e inicializa una estructura que describe a una persona, y su contenido se modifica posteriormente.

analogía:

De hecho, usar una estructura es equivalente a construir una casa, el enunciado previo de la estructura es el dibujo del dibujo, y la creación posterior de la variable de estructura es construir la casa según el dibujo.

Entonces, cuando declaramos una estructura, el sistema no le asignará espacio. Solo después de que se haya creado la variable de estructura, el sistema le asignará espacio.

Código 2 (crear una variable de estructura global)

struct Peo
{
	char name[20];
	int age;
}p1,p2,p3;

Cuando se declara la estructura, escriba el nombre/etiqueta de la variable de estructura que se creará directamente después de las llaves, pero debe tenerse en cuenta que la variable global se crea aquí. Se recomienda usar este método menos.

acceso a los miembros de la estructura

1. Objetos de estructura Miembros de estructura

El siguiente código accede a los miembros de la estructura a través del nombre/etiqueta de la variable de estructura

struct Peo
{
	char name[20];
	int age;
};
int main()
{
	struct Peo p = { "张三",18 };
	printf("%s   %d\n", p.name, p.age);
	return 0;
}

resultado de la operación:

 2. Puntero de estructura -> miembro de estructura

 El siguiente código: Acceder a los miembros de la estructura a través de punteros de estructura.

#include <stdio.h>
struct Peo
{
	char name[20];
	int age;
};
int main()
{
	struct Peo p = { "张三",18 };
	struct Peo* ps = &p;
	printf("%s   %d\n", ps->name, ps->age);
	return 0;
}

resultado de la operación:

parámetro de estructura 

 La transferencia de parámetros de estructura se divide en transferencia de valor y transferencia de dirección

1. Pasar por valor

El siguiente código:

struct Peo
{
	char name[20];
	int age;
};
void print1(struct Peo p)
{
	printf("%s   %d\n", p.name, p.age);
}
int main()
{
	struct Peo p = { "张三",18 };
	print1(p);   //传值,传整个结构体过去
	return 0;
}

Esto es para pasar toda la estructura directamente.

2. Dirección

struct Peo
{
	char name[20];
	int age;
};
void print2(struct Peo* ps)
{
	printf("%s   %d\n", ps->name, ps->age);
}
int main()
{
	struct Peo p = { "张三",18 };
	print2(&p);
	return 0;
}

Pasar la dirección de la estructura.

Para estos dos métodos de paso de parámetros, preferimos el segundo método

La razón es:

Al pasar parámetros a una función, los parámetros deben colocarse en la pila. 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 conducirá a una degradación del rendimiento.

Alineación de memoria de estructura

Si queremos calcular el tamaño de memoria de una estructura, ¿cómo lo calculamos? ¿Es para agregar el tamaño de memoria de todas las variables miembro directamente?

Primero mira el siguiente código:

#include <stdio.h>
struct P
{
	char a;
	int b;
};
int main()
{
	struct P p;
	printf("%d\n", sizeof(p));
}

 Después de crear la variable de estructura p aquí, encuentra el tamaño de esta estructura, ¿es 1+4==5?

resultado de la operación:

 El resultado de la impresión es 8, lo que obviamente muestra que el tamaño de la estructura no es una simple suma de variables miembro, entonces, ¿cómo surgió este 8? Esto implica la alineación de la memoria de la estructura.

 Reglas de alineación

  1.  El primer miembro está en la dirección en el desplazamiento 0 de la variable de estructura (desplazamiento 0 desde la posición inicial)
  2. Las otras variables miembro restantes deben alinearse con un múltiplo entero de un número determinado (número de alineación)
  3. Alineación : el tamaño más pequeño de cada miembro y la alineación predeterminada del compilador utilizado
  4. 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)
  5. Si una estructura está anidada, la estructura anidada se alinea con un múltiplo entero de su propio número de alineación máximo y el tamaño total de la estructura es un múltiplo entero de todos los números de alineación máximos (incluido el número de alineación de las estructuras anidadas).

Sugerencia: el número de alineación predeterminado del compilador VS es 8, y el compilador gcc en Linux no tiene un número de alineación predeterminado.

Entonces ahora puede usar las reglas de alineación para encontrar el tamaño de la estructura

En el código anterior, el primero en la estructura es de tipo char, que se coloca directamente en el desplazamiento 0

El segundo tipo es de tipo int, su propio tamaño es 4 y el número de alineación predeterminado es 8, por lo que el número de alineación toma el valor más pequeño de 4

El tamaño total de la estructura es un múltiplo entero del número máximo de alineación

Ver por dibujo

 Entonces el tamaño de esta estructura es 8

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

  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 tipos de datos en ciertas direcciones; de lo contrario, se generará una excepción de hardware.
  2. 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.

Entonces estructurar la alineación de la memoria es intercambiar espacio por tiempo.

Al diseñar la estructura, no solo debemos satisfacer la alineación, sino también ahorrar espacio, por lo que los miembros que ocupan el pequeño espacio deben concentrarse tanto como sea posible.

Modificar la alineación por defecto

Justo ahora dijimos que el número de alineación predeterminado del compilador VS es 8, pero de hecho, este número de alineación predeterminado se puede modificar

Cuando la alineación de la estructura no es adecuada, podemos cambiar la alineación predeterminada por nosotros mismos

#pragma pack()      //括号内放想达到的默认对齐数

El siguiente código:

#include <stdio.h>
#pragma pack(4)    //修改默认对齐数为4
struct P
{
	char a;
	int b;
};
#pragma pack()    //取消设置的默认对齐数,还原为默认值
int main()
{
	struct P p;
	printf("%d\n", sizeof(p));
}

Nota: este número de alineación predeterminado no se puede modificar a voluntad, y el valor modificado debe ser 2^n (n==0,1,2,3...)

En segundo lugar, el segmento

¿Qué es un segmento de bits?

El lenguaje C permite especificar la longitud de memoria ocupada por sus miembros en unidades de bits en una estructura . Tales miembros en unidades de bits se denominan "campos de bits" o " campos de bits ". Use campos de bits para almacenar datos con menos bits

como sigue:

struct A
{
	int a : 2;
	int b : 3;
	int c : 5;
	int d : 10;
};

Se puede ver en el código que la declaración del campo de bits es muy similar a la estructura. El número después de los dos puntos indica el tamaño del miembro (en bits). Hay dos diferencias:

  1. Los miembros del campo de bits deben ser int, int sin signo o int con signo 
  2. Los campos de bits tienen dos puntos y un número después del nombre del miembro

Entonces, ¿cuál es el tamaño de este segmento de bits? ¿Son 2+3+5+10 bits? Definitivamente no, pruébalo con código

struct A
{
	int a : 2;
	int b : 3;
	int c : 30;
	int d : 10;
};
int main()
{
	printf("%d\n", sizeof(struct A));
	return 0;
}

resultado de la operación:

  Entonces, ¿cómo provienen estos 12 bytes? A continuación, mire la asignación de memoria del segmento de bits

Asignación de memoria para campos de bits

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

Hay 4 variables de tipo int en la declaración del segmento de bits en el código anterior, que deberían haber ocupado 16 bytes de espacio, pero solo se ocupan 12 bytes después de que se usa el segmento de bits.El método de cálculo es el siguiente:

 Cuando la memoria solicitada no es suficiente y el miembro del segundo segmento de bits es demasiado grande para caber en los bits restantes del primer segmento de bits, no se sabe si descartar los bits restantes o usarlos, lo que depende del compilador.

Problemas multiplataforma con campos de bits

  1. Es indeterminado si el campo de bit int se trata como firmado o sin firmar
  2. No se puede determinar el número máximo de bits en el campo de bits. (La máquina de 16 bits tiene hasta 16, la máquina de 32 bits tiene hasta 32, escrito como 27, habrá problemas en las máquinas de 16 bits
  3. No está definido si los miembros en un campo de bits se asignan en la memoria de izquierda a derecha o de derecha a izquierda.
  4. Cuando una estructura contiene dos segmentos de bits, y el miembro del segundo segmento de bits es demasiado grande para caber en los bits restantes del primer segmento de bits, no se sabe si descartar los bits restantes o usarlos.

Aplicación de segmentos de bits

Cuando normalmente queremos enviar un mensaje de saludo a un amigo, no solo enviamos saludo, sino que también enviamos saludo y otros datos juntos, como quién envió el mensaje, a quién y la información de la dirección IP de destino, etc., pero algunos de esta información puede necesitar solo unos pocos bits para guardar, sería demasiado derrochador usar todos los int, lo que también afectará la condición de la red, en este caso, use el segmento de bits

3. Constantes de enumeración

¿Qué es una enumeración?

Enumeración significa enumerar uno por uno, enumerando todos los valores posibles.

Por ejemplo, se pueden enumerar colores, semanas y meses. En lenguaje C, los valores de un determinado tipo que queremos se definen como tipos de enumeración, y sus valores también se pueden enumerar uno por uno.

Definición de tipos de enumeración

Por ejemplo, ahora enumere los tres colores primarios

enum Color
{
	RED,
	GREEN,
	BLUE
};

 El contenido en {} es el valor posible del tipo de enumeración, también llamado constante de enumeración

¿Cuál es el valor?

Entonces, ¿cuáles son los valores aquí? El siguiente código:

enum Color
{
	RED = 3,
	GREEN = 7,
	BLUE = 5
};
int main()
{
	printf("%d\n", RED);
	printf("%d\n", GREEN);
	printf("%d\n", BLUE);
}

imprimir resultado:

Todos estos valores posibles tienen valores, comenzando desde 0 por defecto y aumentando de 1 en 1. Por supuesto, el valor inicial también se puede asignar al definir . como sigue:

enum Color
{
	RED = 3,
	GREEN = 7,
	BLUE = 5
};

Se puede usar así :

enum Color
{
	RED ,
	GREEN,
	BLUE
};
int main()
{
	enum Color c = RED;
}

Ventajas de los tipos de enumeración

  1. Aumente la legibilidad y el mantenimiento del código
  2. Las enumeraciones tienen verificación de tipo y son más rigurosas en comparación con los identificadores definidos por #define .
  3. Previene la contaminación de nombres (encapsulación)
  4. fácil de depurar
  5. Fácil de usar, puede definir múltiples constantes a la vez

4. Unión (comunidad)

Definición de tipos de unión

La unión también es un tipo personalizado especial. Las variables definidas por este tipo también contienen una serie de miembros, caracterizados por el hecho de que estos miembros comparten el mismo espacio (por lo que la unión también se denomina unión)

declaración de tipo de unión

El siguiente código:

union U
{
	char i;
	int a;
};

Entonces, ¿cuál es el tamaño de la unión aquí ?

También es el principio de alineación, ligeramente diferente

Los miembros de la unión comparten el mismo espacio de memoria, por lo que el tamaño de dicha variable de unión es al menos el tamaño del miembro más grande (porque la unión debe al menos poder guardar el miembro más grande)

El tamaño total es un múltiplo entero del número máximo de alineaciones

-------------------------------------------------- ---------------

-------------Se completó el almacenamiento de números de punto flotante en lenguaje C---------

Con respecto al lenguaje C, cada punto de conocimiento se escribirá en un blog separado para una introducción más detallada.

¡Bienvenidos todos a prestar atención! ! !

¡Aprender a comunicarnos juntos! ! !

¡Vamos a programar hasta el final! ! !

-------------- No es fácil de organizar, apoye tres consecutivos -------------------

Supongo que te gusta

Origin blog.csdn.net/weixin_46531416/article/details/120379878
Recomendado
Clasificación