1. Resumen
El lenguaje C permite al usuario especificar una estructura de datos de este tipo, que combina datos de diferentes tipos en un todo para su referencia. Los datos combinados en este todo están relacionados entre sí. Esta estructura de datos se denomina estructura, y equivale al registro en otros lenguajes de alto nivel.
La forma general de declarar un tipo de estructura es la siguiente:
struct 结构体名
{成员列表};
El nombre de la estructura se utiliza como identificador del tipo de estructura; también se denomina etiqueta de estructura. Dentro de las llaves se encuentran los miembros de la estructura, que forman la estructura. Cada miembro debe declararse con su tipo, por ejemplo:
nombre de tipo nombre de miembro;
La lista de miembros también se denomina tabla de campos, y el primer miembro también se llama campo de la estructura. Las reglas para nombrar a los miembros son las mismas que para nombrar variables.
struct student
{
int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
};
2. Métodos para definir variables de tipo estructura
Antes solo se ha especificado un tipo de estructura, que equivale a un modelo, pero no contiene datos concretos y el sistema no le asigna unidades de memoria reales. Para poder usar datos de tipo estructura en un programa, se deben definir variables de tipo estructura y almacenar datos concretos en ellas. Se pueden utilizar los siguientes tres métodos para definir variables de tipo estructura.
(1) Declarar primero el tipo de estructura y luego definir el nombre de la variable
Como arriba ya se ha definido un tipo de estructura `struct student`, se puede usar para definir variables. Por ejemplo:
struct student{ //结构体类型名
...
...
...
}student1, student2 //结构体变量名
Se definen `student1` y `student2` como variables de tipo `struct student`.
Después de definir las variables de estructura, el sistema les asigna unidades de memoria. Por ejemplo, `student1` y `student2` ocupan cada una 59 bytes en memoria.
Se debe tener en cuenta que la diferencia entre definir una variable como tipo estándar (tipo de datos básico) y definirla como tipo estructura es que en este último caso no solo se requiere especificar que la variable es de tipo estructura, sino también especificar un tipo de estructura particular (por ejemplo, el tipo `struct student`), porque se pueden definir muchos tipos de estructura concretos. En cambio, al definir una variable como entera, solo se necesita especificar el tipo `int`.
(2) Definir la variable al mismo tiempo que se declara el tipo
Por ejemplo:
struct student
{
int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
}student1, student2;
Su efecto es el mismo que el del primer método, es decir, define dos variables `student1` y `student2` de tipo `struct student`. La forma general de esta definición es:
struct 结构体名
{
成员表列
}变量名表列;
(3) Definir directamente la variable de tipo estructura
Su forma general es:
struct
{
成员表列
}变量名表列;
Es decir, no aparece el nombre de la estructura.
Respecto al tipo de estructura, hay varios puntos que aclarar:
- a. El tipo y la variable son conceptos diferentes; no deben confundirse. Solo se pueden asignar valores, acceder o realizar operaciones sobre variables, no sobre un tipo. Durante la compilación, no se asigna espacio al tipo, solo a las variables.
- b. Los miembros de la estructura (es decir, los campos) se pueden usar individualmente; su función y estatus equivalen a los de una variable normal.
-
c. Un miembro también puede ser una variable de estructura. Por ejemplo:
struct date // 声明一个结构体类型 { int month; int day; int year; } struct student { int num; char name[20]; char sex; int age; struct date birthday; char addr[30]; }student1, student2;Primero se declara un tipo `struct date`, que representa una fecha e incluye tres miembros: `month`, `day`, `year`. Luego, al declarar el tipo `struct student`, se especifica el miembro `birthday` como de tipo `struct date`.
- d. El nombre de un miembro puede ser igual al nombre de una variable en el programa; ambos no representan el mismo objeto.
3. Referencia a variables de estructura
(1) No se puede introducir o generar una variable de estructura como un todo.
Solo se pueden introducir o generar individualmente cada uno de los miembros de la variable de estructura. La forma de referenciar a un miembro de una variable de estructura es:
nombre de variable de estructura.nombre de miembro
Por ejemplo, `student1.num` representa el miembro `num` de la variable `student1`, es decir, el campo `num` de `student1`. Se puede asignar un valor al miembro de la variable. Por ejemplo: student1.num = 10010;
.El operador de miembro (componente) tiene la prioridad más alta entre todos los operadores, por lo que `student1.num` puede considerarse como un todo. La declaración de asignación anterior asigna el entero `10010` al miembro `num` de la variable `student1`.
(2) Si el miembro en sí es de tipo estructura, se deben usar varios operadores de miembro, nivel por nivel, hasta encontrar el miembro de nivel más bajo. Solo se puede asignar, acceder o realizar operaciones sobre el miembro de nivel más bajo.
Por ejemplo: la variable de estructura `student1` se puede acceder a sus miembros de la siguiente manera:
student1.num student1.birthday.month
Nota: no se puede usar `student1.birthday` para acceder al miembro `birthday` de la variable `student1`, porque `birthday` en sí es una variable de estructura.
((3) Los miembros de una variable de estructura pueden realizar diversas operaciones como una variable normal (las operaciones posibles dependen de su tipo).
student2.score = student1.score; sum = student1.score + student2.score; student1.age ++; ++ student1.age;
Dado que.la prioridad del operador es la más alta, por lo que `student1.age ++` es una operación de incremento sobre `student1.age`, y no un incremento sobre `age` primero.
(4) Se puede referenciar la dirección de un miembro de una variable de estructura. También se puede referenciar la dirección de la variable de estructura. Por ejemplo:
scanf("%d", &student1.num);// 输入 student1.num 的值
printf("%o", &student1);// 输出 student1 的首地址
Pero no se puede usar la siguiente instrucción para leer una variable de estructura como un todo, como:
scanf("%d,%s,%c,%d,%f,%s", &student1);
La dirección de una variable de estructura se usa principalmente como parámetro de función, para pasar la dirección de la estructura.
4. Inicialización de variables de estructura
Al igual que otros tipos de variables, a las variables de estructura se les pueden especificar valores iniciales en el momento de la definición.Ejemplo
5. Arreglos de estructuras
Una variable de estructura puede almacenar un conjunto de datos (como el número de estudiante, el nombre, las calificaciones, etc.). Si hay datos de 10 estudiantes que necesitan participar en operaciones, obviamente se debe usar un arreglo; esto es un arreglo de estructuras. La diferencia entre un arreglo de estructuras y los arreglos de valores numéricos presentados anteriormente es que cada elemento del arreglo es un dato de tipo estructura, e incluye cada uno de los miembros (componentes).
5.1 Definición de arreglos de estructuras
Es similar al método para definir variables de estructura; solo se debe indicar que es un arreglo.
struct student
{
int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
};
struct student stu[3];
Arriba se ha definido un arreglo `stu`, cuyos elementos son datos de tipo `struct student`; el arreglo tiene 3 elementos. También se puede definir directamente un arreglo de estructuras. Por ejemplo:
struct student
{
int num;
....
}stu[3];
o
struct
{
int num;
...
}stu[3];
5.2 Inicialización de arreglos de estructuras
Al igual que los arreglos de otros tipos, un arreglo de estructuras se puede inicializar, por ejemplo:
struct student
{
int mum;
char name[20];
char sex;
int age;
float score;
char addr[30];
}stu[3] = {{10101,"Li Lin", 'M', 18, 87.5, "103 Beijing Road"},
{10101,"Li Lin", 'M', 18, 87.5, "103 Beijing Road"},
{10101,"Li Lin", 'M', 18, 87.5, "103 Beijing Road"}};
Al definir el arreglo `stu`, se puede omitir el número de elementos, es decir, se escribe de la siguiente forma:
stu[] = {{...},{...},{...}};
Durante la compilación, el sistema determina el número de elementos del arreglo según la cantidad de constantes de estructura con valores iniciales proporcionados.
Por supuesto, la inicialización del arreglo también se puede realizar de la siguiente forma:
struct student
{
int num;
...
};
struct student stu[] = {{...},{...},{...}};
Es decir, primero se declara el tipo de estructura, luego se define el arreglo como de ese tipo de estructura y se inicializa al definir el arreglo.
De lo anterior se puede ver que la forma general de inicializar un arreglo de estructuras es agregar después de la definición del arreglo:
5.3 Ejemplo de aplicación de arreglos de estructuras
El siguiente ejemplo ilustra la definición y referencia de arreglos de estructuras.
Ejemplo
El resultado de la ejecución es el siguiente:
LI Li Fun Zhang Zhang Fun Li Fun Zhang Li Li: 3 Zhang: 3 Fun: 3
6. Punteros a datos de tipo estructura
El puntero de una variable de estructura es la dirección inicial del segmento de memoria que ocupa dicha variable. Se puede establecer una variable puntero para que apunte a una variable de estructura; en ese caso, el valor de la variable puntero es la dirección inicial de la variable de estructura. La variable puntero también se puede usar para apuntar a elementos de un arreglo de estructuras.
6.1 Puntero a una variable de estructura
Aplicación del puntero a una variable de estructura:
Ejemplo
.num o stu
.name como argumento real de la función, pasando el valor del argumento real al parámetro formal. El uso es igual que usar una variable ordinaria como argumento real; pertenece al modo de paso por valor. Se debe prestar atención a que los tipos del argumento real y del parámetro formal sean consistentes.
NO. :89101 name: Li Lin sex:M score:89.500000 NO. :89101 name: Li Lin sex:M score:89.500000
Se puede ver que los resultados de los dos printf son iguales.
En lenguaje C, para mayor comodidad y para hacerlo más intuitivo, se puede reemplazar (*p).num por p->num, que representa el miembro num de la variable de estructura a la que apunta *p. De igual manera, (*p).name equivale a p->name.
Es decir, las siguientes tres formas son equivalentes:
- a. variable de estructura.nombre del miembro
- b. (*p).nombre del miembro
- c. p->nombre del miembro
El elemento de salida del último printf anterior puede reescribirse como:
printf("NO. :%ld\nname: %s\nsex:%c\nscore:%f\n",p->num, p->name, p->sex, p->score);
donde->se denomina operador de puntero.
Analicemos los siguientes operadores:
- p -> n obtiene el valor del miembro n de la variable de estructura a la que apunta p.
- p -> n ++ obtiene el valor del miembro n de la variable de estructura a la que apunta p, y después de usarlo, lo incrementa en 1.
- ++p -> n obtiene el valor del miembro n de la variable de estructura a la que apunta p y lo incrementa en 1 (preincremento).
6.2 Puntero a un arreglo de estructuras
Anteriormente se introdujo que se pueden usar punteros y variables puntero que apuntan a arrays o elementos de arrays. De igual manera, para arrays de estructuras y sus elementos también se pueden usar variables puntero para apuntar a ellos.
Aplicación de punteros a arrays de estructuras.
Ejemplo
El resultado de la ejecución es el siguiente:
No. name sex age 10101 Li Lin M 18 10102 Zhang Fun M 19 10103 Wang Min F 20
Observe los siguientes dos puntos:
(1) Si el valor inicial de p es stu, es decir, apunta al primer elemento, entonces p + 1 apunta a la dirección inicial del siguiente elemento. Por ejemplo:
(++p) -> num primero incrementa p en 1, y luego obtiene el valor del miembro num del elemento al que apunta (es decir, 10102).
(p++) -> num primero obtiene el valor de p->num (es decir, 10101), y luego incrementa p en 1, apuntando a stu
Nótese la diferencia entre los dos casos anteriores.
(2) El programa ya ha definido la variable puntero p como apuntando a datos de tipo struct student; solo puede apuntar a un dato de tipo struct student (el valor de p es la dirección inicial de un elemento del array stu), y no puede apuntar a un miembro de un elemento del array stu (es decir, la dirección de p no puede ser la dirección de un miembro). Por ejemplo, lo siguiente no es correcto:
p = &stu[1].name
Se producirá un error en tiempo de compilación. No piense que, como p almacena direcciones, se le puede asignar cualquier dirección. Si los tipos de dirección no son iguales, se puede usar una conversión forzada de tipos. Por ejemplo:
p = (struct student *)&stu[1].name;
En este caso, en p se almacena la dirección inicial del miembro name del elemento stu
6.3 Usar variables de estructura y punteros a estructuras como parámetros de funciones
Para pasar el valor de una variable de estructura a otra función, hay 3 métodos:
- (1) Usar miembros de la variable de estructura como parámetros. Por ejemplo: usar stu
- (2) Usar la variable de estructura como parámetro. Las versiones antiguas del sistema C no permitían usar variables de estructura como argumentos reales; ANSI C eliminó esta restricción. Sin embargo, cuando se usa una variable de estructura como argumento real, se adopta el modo de paso por valor, transmitiendo secuencialmente todas las celdas de memoria ocupadas por la variable de estructura al parámetro formal. El parámetro formal también debe ser una variable de estructura del mismo tipo. Durante la llamada a la función, el parámetro formal también ocupa celdas de memoria. Este modo de transmisión tiene un gran costo en espacio y tiempo; si la estructura es de gran tamaño, el costo es considerable. Además, dado que se usa el paso por valor, si durante la ejecución de la función llamada se modifica el valor del parámetro formal (que también es una variable de estructura), ese valor no puede devolverse a la función que llama, lo que a menudo causa inconvenientes en el uso. Por lo tanto, este método se usa poco.
- (3) Usar un puntero a una variable de estructura (o array) como argumento real, pasando la dirección de la variable de estructura (o array) al parámetro formal.
Usar variables de estructura como parámetros de función.
Ejemplo
Cambie lo anterior para usar punteros a variables de estructura como argumentos reales.
Ejemplo
7. Manejo de listas enlazadas con punteros
7.1 Resumen de listas enlazadas
Una lista enlazada es una estructura de datos común e importante. Es una estructura que realiza asignación dinámica de memoria.
Una lista enlazada tiene una variable de puntero de cabecera, que almacena una dirección; dicha dirección apunta a un elemento. Cada elemento de la lista enlazada se llama nodo. Cada nodo debe incluir dos partes: una son los datos reales que el usuario necesita, y la otra es la dirección del siguiente nodo. Se puede ver que el puntero de cabecera head apunta al primer elemento, el primer elemento apunta al segundo, ..., hasta el último elemento. Este último elemento ya no apunta a otros elementos; se llama cola de la lista. En su parte de dirección se coloca un NULL (que significa dirección vacía), y la lista enlazada termina aquí.
Se puede ver que los elementos de la lista enlazada no necesitan estar almacenados de forma contigua en la memoria. Para encontrar un elemento, primero se debe encontrar el elemento anterior, y según la dirección del siguiente elemento que este proporciona, se puede encontrar el siguiente elemento. Si no se proporciona el puntero de cabecera head, no se puede acceder a toda la lista enlazada.
Se puede ver que esta estructura de datos de lista enlazada solo se puede implementar mediante variables puntero; es decir, un nodo debe contener una variable puntero, que se usa para almacenar la dirección del siguiente nodo.
Anteriormente se presentaron las variables de estructura. Son las más adecuadas para usarse como nodos de una lista enlazada. Una variable de estructura contiene varios miembros; estos miembros pueden ser de tipo numérico, tipo carácter, tipo array, o tipo puntero. Usamos este miembro de tipo puntero para almacenar la dirección del siguiente nodo. Por ejemplo, se puede diseñar un tipo de estructura como este:
struct student
{
int num;
float score;
struct student *next;
};
Entre ellos, los miembros num y score se usan para almacenar los datos útiles del nodo (los datos que el usuario necesita). next es un miembro de tipo puntero, que apunta a datos de tipo struct student (este es el tipo de estructura al que pertenece next). Un miembro de tipo puntero puede apuntar tanto a datos de estructura de otros tipos como a datos del tipo de estructura al que pertenece. Ahora next es un miembro del tipo struct student, y a su vez apunta a datos de tipo struct student. Con este método se puede construir una lista enlazada.
Tenga en cuenta: solo se define un tipo struct student; no se asigna realmente espacio de almacenamiento. Solo cuando se define una variable se asignan celdas de memoria.
7.2 Lista enlazada simple
A continuación, mediante un ejemplo, se explica cómo crear y mostrar una lista enlazada simple:
Ejemplo
Resultado de la ejecución:
99101 89.5 99103 90.0 99107 85.0
7.3 Funciones necesarias para manejar listas enlazadas dinámicas
(1) Función malloc
void *malloc(unsigned int size);
Su función es asignar un espacio contiguo de longitud size en el área de almacenamiento dinámico de la memoria. El valor de esta función (es decir, el valor de retorno) es un puntero a la dirección inicial del espacio asignado (tipo base void). Si esta función no se ejecuta con éxito (por ejemplo, espacio de memoria insuficiente), devuelve el puntero nulo NULL.
(2) Función calloc
void *calloc(unsigned n, unsigned size);
Su función es asignar n espacios contiguos de longitud size en el área de almacenamiento dinámico de la memoria. La función devuelve un puntero a la dirección inicial del espacio asignado; si la asignación no tiene éxito, devuelve NULL. Con la función calloc se puede abrir un espacio de almacenamiento dinámico para un array unidimensional; n es el número de elementos del array, y cada elemento tiene longitud size.
(3) Función free
void free(void *p);
Su función es liberar el área de memoria apuntada por p, para que esa parte del área de memoria pueda ser utilizada por otras variables. p es el valor devuelto en la última llamada a las funciones calloc o malloc. La función free no tiene valor de retorno. Tenga en cuenta: las versiones anteriores de C proporcionaban las funciones malloc y calloc que devolvían punteros a datos de tipo carácter. ANSI C especifica que las funciones malloc y calloc proporcionadas son de tipo void *.
7.4 Creación de listas enlazadas dinámicas
La llamada creación de una lista enlazada dinámica significa que, durante la ejecución del programa, se construye una lista enlazada desde cero, es decir, se van creando nodos uno a uno, se ingresan los datos de cada nodo y se establece la relación de encadenamiento entre ellos.
Ejemplo
A continuación se muestran varias operaciones sobre la lista enlazada:
Imprimir la lista enlazada:
void printlist(struct student *head)
{
struct student *p;
p = head;
if(head != NULL)
{
do
{
printf("num=%d score=%5.2f\n", p->num, p->score);
p = p->next;
} while (p != NULL);
}
/* while(p -> next != NULL)
{
printf("num=%d score=%f\n", p->num, p->score);
p = p->next;
}*/
}Eliminar un nodo:struct student *delNode(struct student *head, int num)
{
printf("delNode.\n");
struct student *p1, *p2;
if(head == NULL)
{
printf("The List is NULL.\n");
}
else
{
p1 = head;
while(p1->next != NULL && p1->num != num)
{
p2 = p1;
p1 = p1->next;
}
if(p1->num == num)
{
if(p1 == head)
head = p1->next;
else
p2->next = p1->next;
}
else
printf("Can not find list num.\n");
}
return head;
}
Actualizar un nodo:
struct student *update(struct student *head, int index, int num, float score)
{
printf("update.\n");
struct student *p;
if(head == NULL)
{
printf("The List is NULL.\n");
}
else
{
p = head;
while(p->next != NULL && p->num != index)
{
p = p->next;
}
if(p->num == index)
{
p->num = num;
p->score = score;
}
else
printf("Can not find list index.\n");
}
return head;
}
Agregar un nodo:
struct student *add(struct student *head, int index, int num, float score)
{
printf("add.\n");
struct student *p1, *p2, *p3;
if(head == NULL)
{
printf("The List is NULL.\n");
}
else
{
p1 = p2 = head;
while(p1->next != NULL && p1->num != index)
{
p1 = p1->next;
p2 = p1;
}
if(p1->num == index)
{
p3 = (struct student *)malloc(LEN);
p3->num = num;
p3->score = score;
if(p2->next == NULL)
{
p2->next = p3;
p3->next = NULL;
}
else
{
p3->next = p2->next;
p2->next = p3;
}
}
else
printf("Can not find list index.\n");
}
return head;
}
Enlace original: http://www.cnblogs.com/qkhhxkj/archive/2011/06/28/2091818.html
Libro de referencia: el capítulo sobre tipos de estructura del libro "Programación en C" del profesor Tan Haoqiang.