1.结构体
首先先来解释一下,数组(Array),它是一组具有相同类型的数据的集合。但在实际的编程过程中,我们往往还需要一组类型不同的数据,例如对于学生信息登记表,姓名为字符串,学号为整数,年龄为整数,所在的学习小组为字符,成绩为小数,因为数据类型不同,显然不能用一个数组来存放。
这时我们就可以用结构体来表示
struct stu{
char *name; //姓名
int num; //学号
int age; //年龄
char group; //所在学习小组
float score; //成绩
}stu1, stu2;
struct为结构体关键字,stu为结构体名, stu1、stu2为结构体变量;每个结构体变量下都包含{}中的成员变量;
结构体取值方式为结构体变量名+“.”+成员变量。
stu1.name = "Tom";
除了可以采用这种赋值方式外还可以进行整体赋值:
struct{
char *name; //姓名
int num; //学号
int age; //年龄
char group; //所在小组
float score; //成绩
} stu1, stu2 = { "Tom", 12, 18, 'A', 136.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}
};
添加结构体变量方法:
struct stu stu3;
使用typedef关键字方法:
typedef struct
{
int a;
char b;
}stu;
此处的stu便不再是结构体名 ,而是定义成为数据类型 便不需要struct来修饰。
结构体指针:
struct stu{
char *name; //姓名
int num; //学号
int age; //年龄
char group; //所在小组
float score; //成绩
} stu1 = { "Tom", 12, 18, 'A', 136.5 };
stu *pi=&stu1; //获取结构体地址时必须加&
通过结构体指针可以获取结构体成员,一般形式为:
(*pointer).memberName
或者:
pointer->memberName
接下来 再介绍一下 sizeof();
sizeof()你可能很熟 ,但如果sizeof的入口参数时一个结构体呢?
为了提高CPU的存储速度,C对一些变量的起始地址做了“对齐”处理。在默认情况下,C规定各成员变量存放的起始地址相对于结构的起始地址的偏移量必须为该变量的类型所占用的字节数的倍数。sizeof计算对象的大小也是转换成对对象类型的计算,也就是说,同种类型的
不同对象其sizeof值都是一致的。下面列出常用类型的对齐方式(32位系统)。
类型 对齐方式(变量存放的起始地址相对于结构的起始地址的偏移量)
Char 偏移量必须为sizeof(char)即1的倍数
Short 偏移量必须为sizeof(short)即2的倍数
int 偏移量必须为sizeof(int)即4的倍数
float 偏移量必须为sizeof(float)即4的倍数
double 偏移量必须为sizeof(double)即8的倍数
而对于指针变量,32位系统是 一个指针变量的返回值必定是4(注意结果是以字节为单位),可以预计,在将来的64位系统中指针变量的sizeof结果为8。
各成员变量在存放的时候根据在结构中出现的顺序依次申请空间,同时按照上面的对
齐方式调整位置,空缺的字节会自动填充。同时为了确保结构的大小为结构的字节边
界数(即该结构中占用最大空间的类型所占用的字节数)的倍数,所以在为最后一个成员
变量申请空间后,还会根据需要自动填充空缺的字节。例:
struct MyStruct
{
double dda1;
char dda;
int type
};
sizeof(MyStruct)=8+1+3+4=16;// sizeof(double)+sizeof(char) + 3 +sizeof(int)
为什么+3? sizeof(double)+sizeof(char) =9,这时下一个可以分配的地址对于结构的起始地址的偏移量为12,刚好是sizeof(int)=4的倍数,所以把type存放在偏移量为12的地方
刚好为结构的字节边界数(即结构中占用最大空间的类型所占用的字节数sizeof(double)=8)的倍数。
2.枚举体
#define ONE 1
#define TWO 2
#define pi 3.1415926
#define
命令虽然能解决问题,但也带来了不小的副作用,导致宏名过多,代码松散,看起来总有点不舒服。C语言提供了一种枚举(Enum)类型,能够列出所有可能的取值,并给它们取一个名字。
例如,列出一个星期有几天:
enum week{ Mon, Tues, Wed, Thurs, Fri, Sat, Sun };
可以看到,我们仅仅给出了名字,却没有给出名字对应的值,这是因为枚举值默认从 0 开始,往后逐个加 1(递增);也就是说,week 中的 Mon、Tues ...... Sun 对应的值分别为 0、1 ...... 6。如果给第一个值赋1,那么就是从1 开始递增。
我们也可以给每个名字都指定一个值:
- enum week{ Mon = 1, Tues = 2, Wed = 3, Thurs = 4, Fri = 5, Sat = 6, Sun = 7 };
- 枚举是一种类型,通过它可以定义枚举变量:
enum week a, b, c;
//或者
enum week{ Mon = 1, Tues, Wed, Thurs, Fri, Sat, Sun } a, b, c;
//有了枚举变量,就可以把列表中的值赋给它:
enum week a = Mon, b = Wed, c = Sat;
//或者
enum week{ Mon = 1, Tues, Wed, Thurs, Fri, Sat, Sun } a = Mon, b = Wed, c = Sat;
//或者使用typedef关键字
typedef enum {Mon , Tues, Wed, Thurs, Fri, Sat, Sun} date;
date a,b,c;
3.共用体
。在C语言中,还有另外一种和结构体非常类似的语法,叫做共用体(Union),它的定义格式为:
结构体和共用体的区别在于:结构体的各个成员会占用不同的内存,互相之间没有影响;而共用体的所有成员占用同一段内存,修改一个成员会影响其余所有成员。
结构体占用的内存大于等于所有成员占用的内存的总和(成员之间可能会存在缝隙),共用体占用的内存等于最长的成员占用的内存。共用体使用了内存覆盖技术,同一时刻只能保存一个成员的值,如果对新的成员赋值,就会把原来成员的值覆盖掉。
union data{
int n;
char ch;
double f;
} a, b, c;
a.n=40;
printf("%d,%c,%f".a.n,a.ch,a.f);
//输出
40 ( 40
4.函数指针
一个函数总是占用一段连续的内存区域,函数名在表达式中有时也会被转换为该函数所在内存区域的首地址,这和数组名非常类似。我们可以把函数的这个首地址(或称入口地址)赋予一个指针变量,使指针变量指向函数所在的内存区域,然后通过指针变量就可以找到并调用该函数。这种指针就是函数指针
returnType (*pointerName)(param list);
///
//返回两个数中较大的一个
int max(int a, int b){
return a>b ? a : b;
}
int main(){
int x, y, maxval;
//定义函数指针
int (*pmax)(int, int) = max;
//也可以写作int (*pmax)(int a, int b)
printf("Input two numbers:");
scanf("%d %d", &x, &y);
maxval = (*pmax)(x, y);
printf("Max value: %d\n", maxval);
return 0;
}
pmax 是一个函数指针,在前面加 * 就表示对它指向的函数进行调用