关键字1.struct
一、结构体概念
前面我们了解了数组(Array),它是一组具有相同类型的数据的集合。但在实际的编程过程中,我们往往还需要一组类型不同的数据,例如对于学生信息登记表,姓名为字符串,学号为整数,年龄为整数,所在的学习小组为字符,成绩为小数,因为数据类型不同,显然不能用一个数组来存放。在C语言中,可以使用结构体(Struct)来存放一组不同类型的数据。结构体的定义形式为:
struct 结构体名{
结构体所包含的变量或数组
};
结构体是一种集合,它里面包含了多个变量或数组,它们的类型可以相同,也可以不同,每个这样的变量或数组都称为结构体的成员(Member)。请看下面的一个例子:
struct stu{
char *name; //姓名
int num; //学号
int age; //年龄
char group; //所在学习小组
float score; //成绩
};
stu 为结构体名,它包含了 5 个成员,分别是 name、num、age、group、score。结构体成员的定义方式与变量和数组的定义方式相同,只是不能初始化。注意大括号后面的分号;不能少,这是一条完整的语句。结构体也是一种数据类型,它由程序员自己定义,可以包含多个其他类型的数据。像 int、float、char 等是由C语言本身提供的数据类型,不能再进行分拆,我们称之为基本数据类型;而结构体可以包含多个基本类型的数据,也可以包含其他的结构体,我们将它称为混合数据类型。
二、结构体的三种定义方式:
结构体变量
既然结构体是一种数据类型,那么就可以用它来定义变量。例如:
① struct stu stu1, stu2;
定义了两个变量 stu1 和 stu2,它们都是 stu 类型,都由 5 个成员组成。注意关键字struct不能少。
②你也可以在定义结构体的同时定义结构体变量:
struct stu{
} stu1, stu2;
将变量放在结构体定义的最后即可。
③如果只需要 stu1、stu2 两个变量,后面不需要再使用结构体名定义其他变量,那么在定义时也可以不给出结构体名,如下所示:
struct{ //没有写 stu
} stu1, stu2;
这样做书写简单,但是因为没有结构体名,后面就没法用该结构体定义新的变量。
成员的获取和赋值
结构体和数组类似,也是一组数据的集合,整体使用没有太大的意义。数组使用下标[ ]获取单个元素,结构体使用点号 . 获取单个成员。获取结构体成员的一般格式为:
结构体变量名.成员名;
赋值方式:
1、使用逐一赋值的方式对结构体各元素进行赋值。(例2)
2、在定义时整体赋值。
注:整体赋值仅限于定义结构体变量的时候,在使用过程中只能对成员逐一赋值,这和数组的赋值非常类似。
结构体数组
所谓结构体数组,是指数组中的每个元素都是一个结构体。在实际应用中,结构体数组常被用来表示一个拥有相同数据结构的群体,比如一个班的学生、一个车间的职工等。
定义结构体数组和定义结构体变量的方式类似,请看下面的例子:
struct stu{
char *name; //姓名
int num; //学号
int age; //年龄
char group; //所在小组
float score; //成绩
}class[5];
表示一个班级有5个学生。
结构体数组在定义的同时也可以初始化,例如:
struct stu{
char *name; //姓名
int num; //学号
int age; //年龄
char group; //所在小组
float score; //成绩
}class[5] = {
{"Li ping", 5, 18, 'C', 145.0},
{"Zhang ping", 4, 19, 'A', 130.5},
{"He fang", 1, 18, 'A', 148.5},
{"Cheng ling", 2, 17, 'F', 139.0},
{"Wang ming", 3, 17, 'B', 144.5}
};
当对数组中全部元素赋值时,也可不给出数组长度
结构体数组的使用也很简单,例如,获取 Wang ming 的成绩:
class[4].score;
修改 Li ping 的学习小组:
class[0].group = 'B';
结构体和指针
一、结构体指针含义:
指针也可以指向一个结构体,定义的形式一般为:
struct 结构体名 *变量名;
下面是一个定义结构体指针的实例:
struct stu{
} stu1 = { "Tom", 12, 18, 'A', 136.5 };
struct stu *pstu = &stu1;
注意:结构体变量名和数组名不同,数组名在表达式中会被转换为数组指针,而结构体变量名不会,无论在任何表达式中它表示的都是整个集合本身,要想取得结构体变量的地址,必须在前面加&,所以给 pstu 赋值只能写作:
struct stu *pstu = &stu1;
而不能写作:
struct stu *pstu = stu1;
2.typedef
为了编码方便,C语言允许为一个数据类型起一个新的别名。
使用关键字 typedef 可以为类型起一个新的别名,语法格式为:
typedef oldName newName;
例如:
1、typedef int INTEGER;
INTEGER a, b;等效于int a, b; (例5)
typedef 还可以给数组、指针、结构体等类型定义别名。
2、typedef char ARRAY20[20];
ARRAY20 a1, a2, s1, s2;它等价于:char a1[20], a2[20], s1[20], s2[20];
3、为结构体类型定义别名:
typedef struct stu{
char name[20];
int age;
char sex;
} STU;
STU body1,body2;它等价于:struct stu body1, body2;
4、为指针类型定义别名:
typedef int (*PTR_TO_ARR)[4];表示 PTR_TO_ARR 是类型int * [4]的别名,它是一个二维数组指针类型。
PTR_TO_ARR p1, p2;
5、为函数指针类型定义别名:
typedef int (*PTR_TO_FUNC)(int, int);
PTR_TO_FUNC pfunc;
Typedef与define的区别
typedef 在表现上有时候类似于 #define,但它和宏替换之间存在一个关键性的区别。正确思考这个问题的方法就是把 typedef 看成一种彻底的“封装”类型,声明之后不能再往里面增加别的东西。
1) 可以使用其他类型说明符对宏类型名进行扩展,但对 typedef 所定义的类型名却不能这样做。例如:
#define INTERGE int
unsigned INTERGE n; //没问题
typedef int INTERGE;
unsigned INTERGE n; //错误,不能在 INTERGE 前面添加 unsigned
2) 在连续定义几个变量的时候,typedef 能够保证定义的所有变量均为同一类型,而 #define 则无法保证。例如:
#define PTR_INT int *
PTR_INT p1, p2;
经过宏替换以后,第二行变为:
int *p1, p2;
这使得 p1、p2 成为不同的类型:p1 是指向 int 类型的指针,p2 是 int 类型。
相反,在下面的代码中:
typedef int * PTR_INT
PTR_INT p1, p2;
p1、p2 类型相同,它们都是指向 int 类型的指针。
内存管理
Malloc函数
malloc 函数的原型:
(void *)malloc(int size)
malloc 函数的返回值是一个 void 类型的指针,参数为 int 类型数据,即申请分配的内存大小,单位是字节(byte)。内存分配成功之后,malloc 函数返回这块内存的首地址。需要一个指针来接收这个地址。但是由于函数的返回值是 void *类型的,所以必须强制转换成你所接收的类型。
比如:
char *p = (char *)malloc(100);
在堆上分配了 100 个字节内存,返回这块内存的首地址,把地址强制转换成 char *类型后赋给 char *类型的指针变量 p。同时告诉我们这块内存将用来存储 char 类型的数据。也就是说你只能通过指针变量 p 来操作这块内存。
注意: 1、malloc 函数申请内存有不成功的可能,在使用指向这块内存的指针时,必须用 if( NULL != p )语句来验证内存确实分配成功了。
2、调用之前加上头文件 #include<stdlib.h> (例6 malloc、realloc、calloc、free )