Notas de lectura de "C y punteros" (Capítulo 10 Estructura y unión)

0 Introducción

En lenguaje C, si desea juntar elementos de datos del mismo tipo, puede usar matrices. ¿Qué pasa con elementos de diferentes tipos?

Algunos lenguajes de computadora no tienen careeste problema. Por ejemplo python, después pythonde todo, originalmente eran los llamados lenguajes de tipos de datos débiles. CAunque el lenguaje nació antes, ha pasado por miles de velas y todavía puede estar en la cima de Clasificación de lenguajes informáticos durante todo el año. Una de sus ventajas es el control preciso del tipo de datos y la memoria. Estructura y unión son la perfecta interpretación y práctica de esta idea.

En algunos libros de texto y materiales, la estructura también se llama 结构体y la unión también se llama 联合体,, 共同体.共用体

Como dice el refrán: " Una flor que florece por sí sola no es primavera, pero cien flores que florecen juntas llenan el jardín ". Debido a que puede acomodar múltiples tipos de datos, en el desarrollo real, la estructura y la alianza a menudo funcionan juntas, lo que proporciona una gran comodidad. para el desarrollo del proyecto.

Resumen del contenido de este artículo :

Insertar descripción de la imagen aquí

1 Conocimientos básicos de estructura.

Los tipos de datos agregados pueden almacenar más de un dato individual al mismo tiempo. C proporciona dos tipos de datos agregados 数组y 结构. Una estructura es una colección de valores llamados sus miembros .

Se puede acceder a los elementos de una matriz mediante subíndices simplemente porque los elementos de la matriz tienen la misma longitud. Pero en las estructuras esto no es así. Dado que los miembros de una estructura pueden tener diferentes longitudes, no se puede acceder a ellos mediante subíndices.

1.1 Declaración de estructura

La forma de declaración de la estructura es la siguiente:

struct tag {
    
    member-list} variable-list;

Excepto por declaraciones incompletas de etiquetas de estructura, no se pueden omitir todas las partes opcionales y deben aparecer al menos dos . Esta oración es un poco confusa, solo necesita ver algunos ejemplos para comprender que en el desarrollo real, solo hay dos formas comunes de declarar estructuras, que se explicarán a continuación.

struct {
    
    
	int a;
	char b;
	float c;
}x;

Aquí declaramos una variable llamada

struct SIMPLE{
    
    
	int a;
	char b;
	float c;
};

hay este tipoEtiqueta, puedes evitar reinventar la rueda, un poco como los objetos creados en C++. Para cosas con los mismos atributos, solo necesitas crearlas una vez, en uso específico, puedes crear variables según tus propias necesidades.
Hay una forma más conveniente: crear typedefun nuevo tipo.

typedef struct {
    
    
	int a;
	char b;
	float c;
}Simple;

Este método parece un poco más complicado cuando se define, pero es más conveniente cuando se usa. Por ejemplo:

#include <stdio.h>
struct SIMPLE{
    
    
	int a;
	char b;
	float c;
};
typedef struct {
    
    
	int a;
	char b;
	float c;
}Simple;
int main()
{
    
    
    //定义结构体变量
	struct SIMPLE s1;
	Simple s2;
	//给结构体变量赋值
	s1.a = 10;
	s1.b = '?';
	s1.c = 3.1415;
	
	s2.a = 20;
	s2.b = '!';
	s2.c = 3.1415;	
	//打印输出
	printf("s1.a = %d\n",s1.a);
	printf("s1.b = %c\n",s1.b);
	printf("s1.c = %f\n",s1.c);
	printf("s2.a = %d\n",s2.a);
	printf("s2.b = %c\n",s2.b);
	printf("s2.c = %f\n",s2.c);
	
	return 0;
}

Ejecutar, imprimir salida:
Insertar descripción de la imagen aquí

Se puede observar que si se utilizan palabras clave typedefpara declarar la estructura, se puede escribir una palabra clave menos al definir las variables de la estructura, structno parece demasiado conciso, pero en el desarrollo de proyectos grandes se ahorrará más tiempo. .

1.2 Miembros de la estructura

En los ejemplos hasta ahora, sólo he usado tipos simples de miembros de estructura. Pero cualquier variable que pueda declararse fuera de una estructura puede usarse como miembro de la estructura. En particular, los miembros de la estructura pueden ser escalares , matrices , punteros o incluso otras estructuras ( los escalares generalmente se refieren a datos enteros o de punto flotante ).
Por ejemplo:

struct COMPLEX(
	float f;
	int   a[20];
	long  *lp;
	struct SIMPLE s;
	struct SIMPLE sa[10];
	struct SIMPLE *sp;
};

1.3 Acceso directo a los miembros de la estructura

Se accede a los miembros de las variables de estructura mediante el operador de punto (.). El operador de punto acepta dos operandos, el operando izquierdo es el nombre de la variable de estructura y el operando derecho es el nombre del miembro al que se debe acceder. El resultado de esta expresión es el miembro especificado. Por ejemplo, considere la siguiente declaración:

struct COMPLEX comp;

Por ejemplo, la expresión

((comp.sa)[4]).c

Las referencias de subíndice y los operadores de punto tienen la misma prioridad y su asociatividad es de izquierda a derecha, por lo que se pueden omitir todos los paréntesis. se convierte en la siguiente expresión

comp.sa[4].c

Los dos son equivalentes.

Por lo tanto, al acceder a tipos de datos anidados, no hay diferencia con el acceso a elementos de datos ordinarios. Solo es necesario acceder capa por capa.

1.4 Acceso indirecto a los miembros de la estructura.

Cuando tenemos un puntero a una estructura, ¿cómo accedemos a los miembros de la estructura? La primera es realizar operaciones de acceso indirecto a punteros. Luego use el operador punto para acceder a sus miembros. Sin embargo, el operador de punto tiene una prioridad más alta que el operador de acceso indirecto, por lo que ocurre la siguiente situación al acceder a miembros de la estructura.
Primero defina un puntero a la estructura:

struct COMPLEX *CP;

Luego accede a sus elementos con:

(*cp).f

->Un operador de este tipo es obviamente demasiado engorroso, por lo que los operadores aparecieron en el lenguaje C. Los punteros de estructura se pueden utilizar para acceder a los miembros de la estructura, de la siguiente manera:

cp->f
cp->a
cp->s

Una expresión 1accede a un miembro de punto flotante, una expresión 2accede a un nombre de matriz y una expresión 3accede a una estructura.

1.5 Autorreferencia de estructuras

¿Es legal que una estructura contenga una estructura del mismo tipo? la respuesta es negativa. Porque esto puede incluirse infinitamente, provocando que el programa no se ejecute. Por ejemplo:

struct SELF_REF1 {
    
    
	int a;
	struct SELF_REF1 b;
	int c;
};

Sin embargo, es legal que una estructura contenga punteros a estructuras del mismo tipo, como por ejemplo:

struct SELF_REF2 {
    
    
	int a;
	struct SELF_REF2 *b;
	int c;
};

Como solo tenemos un puntero más a una estructura del mismo tipo, no habrá inclusiones infinitas. En el desarrollo real, a menudo se usa para implementar algunas estructuras de datos, como listas vinculadas y árboles . Cada nodo puede apuntar al siguiente nodo del mismo tipo (a veces más de uno).

1.6 Declaración incompleta

Normalmente, nuestras declaraciones son completas, pero si hay dos estructuras que se incluyen mutuamente, ¿cuál debería definirse primero?

En este momento, nuestra declaración incompleta es útil, como se muestra a continuación:

struct B;
struct A {
    
    
    struct B *partner;
};
struct B {
    
    
    struct A *partner;
};

Aunque se declara la estructura B, no se declara completamente porque no se dan los miembros de la estructura, principalmente porque no se pueden dar, si se dan A, Aaún no se han definido. Entonces existe esta manera de abordarlo.

De esta manera se forman dos estructuras: “Yo estoy en ti y tú estás en mí”. Como se muestra abajo:

Insertar descripción de la imagen aquí

1.7 Inicialización de la estructura

El método de inicialización de una estructura es muy similar a la inicialización de una matriz. Sepárelos con comas dentro de un par de llaves y luego inicialícelos (asigne valores) a cada miembro por separado. Si la lista inicial no contiene suficientes valores, los miembros restantes de la estructura se inicializarán con los valores predeterminados. Por ejemplo:

struct INIT_EX {
    
    
	int a;
	short b[10];
	Simple c;
}x = {
    
     10,{
    
    1,2,3,4,5},{
    
    10,'x', 3.14} };

Puede ver que hay una matriz en el miembro b. Solo inicializamos 5los primeros elementos y los elementos restantes se inicializarán con los valores predeterminados. Generalmente, se inicializan en 0. Como se muestra abajo:

Insertar descripción de la imagen aquí

2 Estructuras, punteros y miembros.

Acceder a una estructura directamente o mediante un puntero es bastante sencillo, porque es muy similar a un array, pero en una situación un poco más complicada, ¿cómo accedemos a los miembros de la estructura?
Para explicar mejor la relación entre estructuras, punteros de estructura y miembros de estructura, primero definimos las estructuras relevantes.

typedef struct {
    
    
	int a;
	short b[2];
} Ex2;
typedef struct {
    
    
	int a;
	char b[3];
	Ex2 c;
	struct EX *d;
}EX;

Luego defina las variables de estructura relevantes.

	EX x = {
    
     10, "Hi", {
    
    5, {
    
     -1, 25}}, 0};
	EX *px = &x;

2.1 Puntero de acceso

Echemos un vistazo pxal significado.
El valor de la expresión px es el contenido de toda la estructura. Como se muestra abajo:

Insertar descripción de la imagen aquí
lvalue es fácil de entender, es decir, puede aceptar un nuevo valor.

2.2 Estructura de acceso

Para acceder a la estructura, es muy simple, simplemente use el operador de acceso indirecto, por lo que el *pxvalor r de la expresión es toda la estructura apuntada por px.

El valor l de una expresión también puede aceptar nuevos valores.

2.3 Acceso a los miembros de la estructura

Lo mismo ocurre con el acceso a los miembros de la estructura. Primero accedamos a las variables y variables xen las variables de la estructura . El acceso es muy simple. Puedes usar directamente la expresión. b es una matriz, por lo que representa la dirección del primer elemento de b. La expresión se usa para acceder al valor del primer elemento de la matriz. Acceso a elementos posteriores es similar al de la matriz. Como se muestra abajo:ab
apx->a
px->b*(px->b)px->b[0]

Insertar descripción de la imagen aquí

2.4 Accediendo a estructuras anidadas

C también es una estructura. Si desea acceder a los elementos en C, primero debe acceder px->ca la estructura c y luego acceder a los elementos que contiene.
Por ejemplo, px->c.aacceder al elemento a de la estructura c es, como la declaración anterior, un puntero constante que apunta a la primera dirección de la matriz en px->c.bla estructura. También hay dos formas de acceder a los elementos en b, acceso de subíndice ( ) y acceso indirecto. ( ). Como se muestra abajo:cbpx->c.b[0]*(px->c.b)

Insertar descripción de la imagen aquí

2.5 Miembros del puntero de acceso

Ahora nuestro miembro de estructura d no apunta a ninguna estructura, así que primero cree una estructura y apunte xd a ella.

	EX y = {
    
     20, "mm", {
    
    12, {
    
     5, 7}}, 0 };
	x.d = &y;

Ahora y también apunta a una estructura, y toda la estructura queda así:

Insertar descripción de la imagen aquí
Luego, si desea acceder a los elementos de la estructura y, primero debe px->yacceder ya la estructura. yLos métodos de acceso de algunos elementos de la estructura son los siguientes:

px->d->a;
px->d->b[0];
px->d->c.a;
px->d->c.b[0];

3 Asignación de almacenamiento de estructuras.

La asignación de almacenamiento de estructuras también es un tema muy interesante. Después de todo, el lenguaje C prefiere un lenguaje de bajo nivel y la definición de muchos datos está directamente relacionada con el tamaño de la memoria asignada.
Por ejemplo:

#include<stdio.h>
struct ALIGN1 {
    
    
    char a;
    int b;
    char c;
};
struct ALIGN2 {
    
    
    int b;
    char a;
    char c;
};

int main()
{
    
    
	printf("struct ALIGN1占%d个字节内存\n",sizeof(struct ALIGN1));
	printf("struct ALIGN2占%d个字节内存\n",sizeof(struct ALIGN2));
	return 0;
}

Imprimir:

Insertar descripción de la imagen aquí
Se puede ver que dos estructuras básicamente iguales ocuparán memoria diferente simplemente debido a la diferencia en el orden de almacenamiento de datos. La asignación de memoria específica se muestra en la siguiente figura:

Insertar descripción de la imagen aquí
Las partes verdes representan espacios sin un significado específico.

Reordene la lista de miembros de la estructura en la declaración para que los miembros con los requisitos de límites más estrictos aparezcan primero y los miembros con los requisitos de límites más débiles aparezcan al final. Este enfoque minimiza la pérdida de espacio causada por la alineación de los límites.

4 Estructuras como parámetros de función.

Las estructuras también se pueden pasar como parámetros de funciones. Pasar una estructura directamente es legal, pero no es muy "elegante". Al igual que las matrices, a menudo se pasan mediante punteros, pero las matrices se pasan mediante punteros de forma predeterminada, pero este no es el caso de las estructuras. Si pasa directamente el nombre de la variable de estructura como parámetro real, toda la estructura se pasará directamente a la función, lo cual es una pérdida de espacio de pila.

Entonces, cuando definimos una función personalizada, el parámetro formal se define como un puntero de estructura y luego se puede pasar el puntero de estructura. Por ejemplo:

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
typedef struct
{
    
    
	char name[10];
	short age;
}people_info;

void get_info(people_info *info)
{
    
    
	strcpy(info->name, "Mystic");
	info->age = 22;
}
int main()
{
    
    
	//定义结构体变量并初始化
	people_info p1 = {
    
    "No Name",25};
	//定义结构体指针
	people_info *p = &p1;
	//重新给结构体定义新值
	get_info(p);
	//打印输出
	printf("%s\n", p->name);
	printf("%d\n", p->age);
	system("pause");
	return 0;
}

Definimos una estructura y luego llamamos get_infoa la función para ingresar información personal en la estructura. Luego regrese a la función principal para verificar si la información que ingresamos es correcta.
Salida de impresión:
Insertar descripción de la imagen aquí
Se puede ver que nuestra función se ejecuta sin problemas.

segmento de 5 bits

El segmento de bits es una existencia mágica: solo desde este punto se puede ver cuánto esfuerzo dedicaron los diseñadores del lenguaje C para conectar estrechamente la memoria. Este diseño equivale a dividir datos completos en varias partes, y cada parte separada puede representar diferentes significados.

Hay dos puntos a tener en cuenta: primero, los miembros del segmento de bits deben declararse como tipo int, int con signo o int sin signo. En segundo lugar, después de que el nombre del miembro sea dos puntos y un número entero, este número entero especifica el número de segmentos de bits ocupados.

Por ejemplo:

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
typedef struct
{
    
    
	unsigned int class_1 : 5;
	unsigned int class_2 : 5;
	unsigned int class_3 : 5;
	unsigned int class_4 : 5;
	unsigned int class_5 : 5;
}class_name;

int main()
{
    
    
	class_name normal;

	normal.class_1 = 26;
	normal.class_2 = 23;
	normal.class_3 = 30;
	normal.class_4 = 31;
	normal.class_5 = 29;
	printf("%d\n", normal.class_1);
	printf("%d\n", normal.class_2);
	printf("%d\n", normal.class_3);
	printf("%d\n", normal.class_4);
	printf("%d\n", normal.class_5);
	printf("结构体占内存大小为%d个字节\n", sizeof(normal));
	system("pause");
	return 0;
}

Por ejemplo, si queremos almacenar 5el número de personas en una clase y el 31número máximo de personas , podemos definir una estructura para implementar segmentos de bits. Si no se utilizan segmentos de bits, incluso si el número de personas en cada clase se define como charuna variable de tipo, un total de 5Bytes, cuando definimos el campo de bits, podemos guardar un byte.

Impresión: Insertar descripción de la imagen aquí
Por supuesto, no es tan simple como ahorrar espacio de almacenamiento, pero en la práctica existen otros usos maravillosos. Por ejemplo, si necesitamos comunicarnos entre dos dispositivos y enviar ondas PWM, entonces necesitamos variables para guardar las PWMpropiedades básicas de la onda, incluida la cantidad de pulsos en cada grupo num, el ciclo de trabajo dutyy la cantidad total de grupos de envío group. Luego, la información debe enviarse primero a otro dispositivo para que el dispositivo pueda estar listo para recibir. Cada trama de datos tiene dos bytes y se compone de la siguiente manera:
Insertar descripción de la imagen aquí
Si no hay un campo de bits, necesitamos una serie de operaciones de desplazamiento (y otras operaciones) para obtener los datos finales que deseas enviar, pero con el segmento de bits es mucho más sencillo y menos propenso a errores, el programa es el siguiente:

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
union 
{
    
    
	struct signal_info
	{
    
    
		unsigned int num : 4;     //每组发送的PWM波数量
		unsigned int duty : 7;    //占空比(%)
		unsigned int group : 5;   //总共发送几组
	};
	unsigned int signal_information;
}signal_info_union;


int main()
{
    
    
	signal_info_union.num = 1;
	signal_info_union.duty = 50;
	signal_info_union.group = 10;


	printf("%d\n", signal_info_union.signal_information);
	system("pause");
	return 0;
}

Nota: Los ejemplos anteriores utilizan uniones. Si no está familiarizado con las uniones, puede leer primero los capítulos que aparecen más adelante en este artículo.

Impresión:
Insertar descripción de la imagen aquí
Los resultados de nuestros cálculos manuales también son 21281consistentes y los dos son consistentes.

6 unidos

En lenguaje C, la definición de variables es el proceso de asignación de espacio de almacenamiento. Generalmente, cada variable tiene su propio espacio de almacenamiento único, entonces, ¿se pueden almacenar diferentes tipos de datos en el mismo espacio de memoria ?

La respuesta es sí, esto se puede lograr mediante un consorcio. En comparación con las estructuras, los sindicatos no aparecen con mucha frecuencia en el desarrollo real, pero esto no significa que carezcan de importancia.

Se puede observar que en términos de memoria, los diseñadores del lenguaje C han puesto mucho esfuerzo, esta es una de las razones por las que el lenguaje C nunca ha desaparecido en el desarrollo de los lenguajes informáticos, aunque ha pasado por muchas dificultades.

6.1 Registros de variantes

Los registros de variación pueden considerarse como una versión mejorada de los sindicatos. Los miembros del sindicato en los registros de variación son estructuras más complejas intque las sumas.float

Entonces, ¿qué es exactamente un registro variante? Encontré esta descripción en línea.

Si un registro se compone de una parte fija y otra parte que cambia según el valor específico de un elemento de datos en la parte fija, se denomina variante de registro.

Probablemente sepas lo que significa, por lo que este llamado registro variante no es un concepto exclusivo de la federación. Es sólo una forma registrada de datos .

Considere la siguiente situación:
el almacén almacena dos tipos de mercancías, uno son piezas y el otro es subconjunto, el conjunto se compone de algunas piezas. La información de una pieza incluye el costo de la pieza, el número de proveedor de la pieza; la información de un conjunto incluye el número de piezas que componen el conjunto y la información de la pieza. Obviamente, un registro de inventario (inventario) en el almacén puede ser una pieza o un conjunto, y contiene la fecha de almacenamiento y el número de operador, que se pueden implementar utilizando registros variantes.

// 零件
struct PARTINFO {
    
    
    int cost;      // 零件成本
    int supplier;  // 供应商编号
};
// 装配件
struct SUBASSYINFO {
    
    
    int n_parts;   // 零件数
    PARTINFO parts[MAXPARTS]; // 每个零件信息
};
// 存货记录
struct INVREC {
    
    
    char date[9]; // 入库日期
    int oper;     // 操作员编号
    enum (PART, SUBASSY} type;
    union {
    
    
        struct PARTINFO part;
        struct SUBASSYINFO subassy;
    } info;
} record;

Podemos acceder a los registros de inventario de las siguientes maneras.

record.oper 获取存货操作员编号
if(record.type == PART)
{
    
    
	record.info.part.cost获取存货零件成本 
	record.info.part.supplier 获取存货零件供应商编号 
}
else if(record.type == SUBASSY)
{
    
    
	record.info.subassy.n_parts 获取存货装配件包含零件数 
	record.info.subassy.parts[0].cost 获取存货装配件第一个零件的成本 
}

6.2 Inicialización de la unión

Las variables de unión se pueden inicializar, pero este valor inicial debe ser la uniónprimer miembrotipo, y debe estar dentro de un par de llaves. Por ejemplo:

union {
    
    
	int a;
	float b;
	char c[4];
}x = {
    
     5 };

No podemos inicializarlo a un valor de punto flotante o un valor de carácter. Si el valor inicial dado es de cualquier otro tipo, se convierte (si es posible) en un número entero y se asigna a x.a.

7 Resumen

Los miembros de una estructura pueden ser escalares , matrices o punteros . Una estructura también puede contener miembros que sean en sí mismos estructuras (distintos de ella misma, pero sus miembros pueden ser punteros a esta estructura).

Todos los miembros de un sindicato se almacenan en la misma ubicación de memoria. Al acceder a miembros sindicales de diferentes tipos, la misma combinación de bits en la memoria puede interpretarse como cosas diferentes. Las variables de unión también se pueden inicializar, pero el valor de inicialización debe coincidir con el 1tipo de variable de unión.

En el desarrollo de proyectos a gran escala, a menudo se utilizan combinados tipos de datos como estructuras, uniones y matrices, así como una deslumbrante variedad de punteros, lo que impone mayores exigencias Ca la base técnica de nuestro lenguaje.

---FIN---

Supongo que te gusta

Origin blog.csdn.net/weixin_43719763/article/details/131034768
Recomendado
Clasificación