- 结构体概述
1.1 概念:有时需要将不同类型的数据组合成一个有机的整体,以便于引用。因此C语言采用结构体形式将不同类型的数据包含到一个整体中。
1.2 定义一个结构的一般形式为:
struct 结构名
{
成员表列
};
(注:成员表列由若干个成员组成,每个成员都是该结构的一个组成部分。对每个成员也必须作类型说明,其形式为: 类型说明符 成员名;)
//例1,定义一个结构体date
struct date
{
int year;
int month;
int day;
};
- 定义结构体类型变量的方法
2.1 先声明结构体类型再定义变量名,见例2
//例2
/#include<stdio.h>void main()
{
struct date //定义结构体
{
int year;
int month;
int day;
};struct date D; // 先声明结构体类型struct date,再定义变量名D,即D 有struct date的结构
printf("Please input year:");
scanf("%d", &D.year);
printf("Please input month:");
scanf("%d", &D.month);
printf("Please input day:");
scanf("%d", &D.day);
printf("%d, %d, %d\n", D.year, D.month, D.day);
}
//注:
struct (类型名) date(结构体名) D(变量名);
2.2 在定义结构体类型的同时定义变量,一般形式如下:
struct 结构体名
{
成员列表
}变量名列表;
//例3
/#include<stdio.h>void main()
{
struct date //定义结构体
{
int year;
int month;
int day;
}D; //定义变量Dprintf("Please input year:");
scanf("%d", &D.year);
printf("Please input month:");
scanf("%d", &D.month);
printf("Please input day:");
scanf("%d", &D.day);printf("%d, %d, %d\n", D.year, D.month, D.day);
}
2.3 直接定义结构体类型变量,其一般形式如下:
struct
{
成员列表
}变量名列表;
//例4
include<stdio.h>
void main()
{
struct //定义结构体,但省去结构体名
{
int year;
int month;
int day;
}D; //定义变量Dprintf("Please input year:");
scanf("%d", &D.year);
printf("Please input month:");
scanf("%d", &D.month);
printf("Please input day:");
scanf("%d", &D.day);printf("%d, %d, %d\n", D.year, D.month, D.day);
}
- 结构体变量的引用
3.1引用结构体变量应注意:
1)不能将一个结构体变量作为一个整体进行输入和输出。
如:printf("%d, %d, %d\n", D); 是错误的!!!!!!!!
正确引用结构体变量中成员的方式为: 结构体变量名 . 成员名
D.year 表示 D, 变量中的year成员 。可以对变量的成员赋值 , 例如:D.year= 2017;
“.” 是成员 ( 分量 ) 运算符 , 它在所有的运算符中优先级最高 , 因此可以把 D.year作为一个整体来看待。上面赋值语句的作用是将年份2017赋给 变量中的成员 year 。
2)如果成员本身又属一个结构体类型 , 则要用若干个成员运算符 , 一级一级地找到最低的一级的成员。只能对最低级的成员进行赋值或存取以及运算。见例5.
//例5
/#include <stdio.h>void main()
{
struct date
{
int month;
int day;
int year;
};struct student
{
int num;
char name[20];
char sex;
struct date birthday; //结构体student内嵌套结构体date
float score;
} boy1, boy2;printf("Please input birthday(YY:) ");
scanf("%d", &boy1.birthday.year); //对最低级year进行赋值
printf("Please input birthday(MM:) ");
scanf("%d", &boy1.birthday.month);
printf("Please input birthday(DD:) ");
scanf("%d", &boy1.birthday.day);
printf("\n");boy2 = boy1;
printf("boy1's birthday is %d-%d-%d\n", boy1.birthday.year, boy1.birthday.month, boy1.birthday.day);
printf("boy2's birthday is %d-%d-%d\n", boy2.birthday.year, boy2.birthday.month, boy2.birthday.day);
}
3)对结构体变量的成员可以像普通变量一样进行各种运算(根据其类型决定可以进行的运算)。
例如:
student2.score = student1.score;
sum = student1.score + student2.score;
student1.age++;
++student2.age;
4)可以引用结构体变量成员的地址,也可以引用结构体变量的地址。见例6.
//例6
/#include <stdio.h>void main()
{
struct student
{
int num;
char *name;
char sex;
float score;
} boy1;boy1.num = 007;
boy1.name = "Jane";printf("The address of struct is: %o \n", &boy1 );
printf("The address of num is: %o \n", &boy1.num );
}
//The address of struct is 12006276760
The address of num is 12006276760
(由此可见,结构体变量的首地址即为结构体变量的首成员的地址。而且引用结构体变量的成员是从结构体变量的首地址为入口开始查找,直到查找出目标成员)
- 结构体变量的初始化,见例7.
//例7.
/#include <stdio.h>void main()
{
struct student /定义结构/
{
int num;
char *name;
char sex;
float score;
}boy1, boy2 = { 102, "Jane", 'M', 98.5 };boy1 = boy2;
printf("Number = %d\nName = %s\nScore = %d\n", boy1.num, boy1.name, boy1.score);
printf("\n\n");
printf("Number = %d\nName = %s\nScore = %d\n", boy2.num, boy2.name, boy2.score);
}
- 结构体数组
一个结构体变量中可以存放一组数据(如一个学生的学号、姓名、成绩等数据)。
如果有10个学生的数据需要参加运算,显然应该用数组,这就是结构体数组。
5.1概念:结构体数组与以前介绍过的数值型数组不同之处在于,每个数组元素都是一个结构体类型的数据,它们都分别包括各个成员(分量)项。见例8.
//例8.
/#include"stdio.h"/#define NUM 3
struct person
{
char name[20];
char phone[10];
};void main()
{
struct person man[NUM];
int i;for( i=0; i < NUM; i++)
{
printf("input name:\n");
gets(man[i].name);
printf("input phone:\n");
gets(man[i].phone);
}printf("name\t\t\tphone\n\n");
for( i=0; i < NUM; i++)
{
printf("%s\t\t\t%s\n",man[i].name,man[i].phone);
}
}
//输出:
name phonejack 123
tom 123
peter 123
5.2 定义结构体数组
和定义结构体变量的方法相仿,只需说明其为数组即可。(三种定义结构体变量的方式)
5.3 结构体数组的初始化
与其他类型的数组一样,对结构体数组可以初始化。例如:
struct student
{
int num;
char name[20] ;
char sex ;
int age ;
float score;
char addr[30] ;
} stu [ 2 ]= {
{101 ,′′ LiLin′′ ,′ M′ , 18 , 87.5 ,′′ Beijing′′ },
{102 ,′′ Zhang′′ ,′ F′ , 19 , 99 ,′′ Shanghai′′}
} ;
当然,数组的初始化也可以用以下形式:
struct student
{
int num ;
...
};
struct student str[]{{...},{...},{...}} ;
(即先声明结构体类型,然后定义数组为该结构体类型,在定义数组时初始化。)
- 指向结构体类型数据的指针
6.1 定义:一个结构体变量的指针就是该结构体变量所占据的内存段的起始地址。
6.2结构指针变量说明的一般形式为:
struct 结构名 * 结构指针变量名
例如,在前面定义了 student 这个结构,如要说明一个指向 student 的指针变量 pstu ,可写为:struct student *pstu;
当然也可在定义 student 结构时同时说明 pstu 。
6.3 与其他各类指针变量相同,结构指针变量也必须要先赋值后才能使用。赋值是把结构变量的首地址赋予该指针变量,不能把结构名赋予该指针变量。
不能把结构名赋予该指针变量。
如果 boy 是被说明为 stu 类型的结构变量,则:pstu = &boy 是正确的。pstu=&student 是错误的。
因为,结构名和结构变量是两个不同的概念,不能混淆。结构名只能表示一个结构形式,编译系统并不对它分配内存空间。只有当某变量被说明为这种类型的结构时,才对该变量分配存储空间。因此上面 &student 这种写法是错误的,不可能去取一个结构名的首地址。有了结构指针变量,就能更方便地访问结构变量的各个成员。
6.4 指向结构体类型数据的指针访问结构体变量成员的一般形式为:
(* 结构指针变量 ). 成员名
或为:
结构指针变量 -> 成员名
例如:(*pstu).num 或者:pstu->num
//例9
/#include <stdio.h>struct student
{
int num;
char *name;
char sex;
float score;
} boy1 = {102, "Jack", 'M', 78.5};void main()
{
struct student *pstu;
pstu = &boy1;printf("Number = %d\nName = %s\n", boy1.num, boy1.name);
printf("Sex = %c\nScore = %f\n\n", boy1.sex, boy1.score);printf("Number = %d\nName = %s\n", (pstu).num, (pstu).name);
printf("Sex = %c\nScore = %f\n\n", (pstu).sex, (pstu).score);printf("Number = %d\nName = %s\n", pstu->num, pstu->name);
printf("Sex = %c\nScore = %f\n\n", pstu->sex, pstu->score);
}
//输出:
Number = 102
Name = Jack
Sex = M
Score = 78.500000Number = 102
Name = Jack
Sex = M
Score = 78.500000Number = 102
Name = Jack
Sex = M
Score = 78.500000
6.5 结构指针变量做函数参数
将一个结构体变量的值传递给另一个函数,有 3 个方法 :
1)用结构体变量的成员作参数
2)用结构体变量作实参,见例10.
//例10
/#include <stdio.h>
/#include <string.h>struct student
{
int num;
char *name;
float score[3];
};void print(struct student);
void main()
{
struct student stu;stu.num = 102;
stu.name = "Jack";
stu.score[0] = 98.5;
stu.score[1] = 99.0;
stu.score[2] = 99.5;print( stu );
}void print( struct student stu )
{
printf("num : %d\n", stu.num);
printf("name : %s\n", stu.name);
printf("score_1 : %5.2f\n", stu.score[0]);
printf("score_2 : %5.2f\n", stu.score[1]);
printf("score_3 : %5.2f\n", stu.score[2]);
}
//输出
num : 102
name : Jack
score_1 : 98.50
score_2 : 99.00
score_3 : 99.50
3)用指向结构体变量(或数组)的指针作实参,将结构体变量(或数组)的地址传给形参。见例11.
//例11
/#include <stdio.h>
/#include <string.h>struct student
{
int num;
char name[20];
float score[3];
};void print(struct student *);
void main()
{
struct student stu;stu.num = 102;
strcpy(stu.name, "Jack"); /strcpy是将从存“Jack”字符串的内存单元的首地址开始,且含有'\0'结束符的字符串复制到以stu.name[0]开始的地址空间/
stu.score[0] = 98.5;
stu.score[1] = 99.0;
stu.score[2] = 99.5;print( &stu );
}void print( struct student *p )
{
printf("num : %d\n", p -> num);
printf("name : %s\n", p -> name);
printf("score_1 : %5.2f\n", p -> score[0]);
printf("score_2 : %5.2f\n", p -> score[1]);
printf("score_3 : %5.2f\n", p -> score[2]);
}
//输出:
num : 102
name : Jack
score_1 : 98.50
score_2 : 99.00
score_3 : 99.50
- 动态分配数组
实际的编程中,往往会发生这种情况,即所需的内存空间取决于实际输入的数据,而无法预先确定。因此,C语言提供了一些内存管理函数,这些内存管理函数可以按需要动态地分配内存空间,也可把不再使用的空间回收待用,为有效地利用内存资源提供了手段。
常用的内存管理函数有以下三个:
1) 分配内存空间函数 malloc 、 calloc;
2)释放内存空间函数 free
7.1 malloc函数
函数原型为: void *malloc(unsigned int size);
其作用是在内存的动态存储区中分配一个长度为 size 的连续空间( size 是一个无符号数)。此函数的返回值是一个指向分配域起始地址的指针(类型为 void )。如果此函数未能成功地执行(例如内存空间不足),则返回空指针 (NULL) 。
7.2 calloc函数
函数原型为:void *calloc ( unsigned n, unsigned size ) ;
其作用是在内存的动态存储区中分配n个长度为size 的连续空间。函数返回一个指向分配域起始地址的指针。如果分配不成功,返回 NULL 。
(用 calloc 函数可以为一维数组开辟动态存储空间, n 为数组元素个数,每个元素长度为 size 。)
7.3 free函数
函数原型为:void free ( void *p ) ;
其作用是释放由 p 指向的内存区,使这部分内存区能被其他变量使用。p 是最近一次调用 calloc 或 malloc 函数时返回的值。free 函数无返回值。