结构体类型:
1.结构体类型创建:
声明一个结构体类型的一般形式为:struct 结构体名 {成员列表 };
eg:struct Student
{
int num;
char name;
char sex;
int age;
};
这个声明创建一个结构体类型Student,它包含四个成员:num ,name , sex, age .
eg:struct Student
{
int num;
char name;
char sex;
int age;
}Stu,stu[100],*stu;
这个声明创建了一个Student 类型的变量Stu,和一个数组stu,它包含100个Student类型结构,*stu是一个指针,它指向Student这个结构。
声明结构时可以使用typedef创建一种新的类型:
typedef struct Student
{
int num;
char name;
char sex;
int age;
}stu;
此时stu就像int一样是个类型名,然后就可以用这个类型名去声明一个Student类型的结构。
此时上边的代码可以这样写:stu Stu,stu[100],*stu;
2.结构体成员
结构体成员可以使标量、数组、指针、甚至是其他结构体。
2.结构体成员的访问:
1、直接访问
结构体成员的直接访问是通过点操作符(.)访问的。点操作符接受两个参数,左操作数就是结构体变量的名字,右操作数就是需要访问的结构体成员的名字。
eg:Stu.name ;
2.间接访问:
如果有一个结构体指针该如何访问一个结构体成员?用到操作符->(箭头操作符)。箭头操作符也接受两个操作数,左操作数必须是一个指向一个结构体的指针,右操作数跟点操作符一样是需要访问的结构体成员的名字。
eg:stu->age;
3.结构体的自引用:
eg:struct Student
{
int num;
char name;
char sex;
struct Student Stu;
int age;
};
这种声明是非法的,这样像一个永远不会终止的递归程序。每一层都包含自己,这样重复下去永无止境。
下面是正确自引用方式:
struct Student
{
int num;
char name;
char sex;
struct Student* Stu;
int age;
};
3.结构体成员的初始化:
结构体初始化很数组的初始化很相似。一个位于一对花括号内部、有逗号分隔的初始值序列表可以用于结构哥哥成员的初始化。这些值根据结构成员列表的顺序写出。如果值不够,自动缺省进行初始化。
4.结构体内存对齐:
如何计算结构体的大小?就得提到结构体内存对齐。
对齐规则:
1.第一个成员在与结构体变量偏移量为零的地址处。
2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。对齐数=编译器默认的一个对齐数与该成员大小的较小值。vs默认的值是8,linux默认的值是4.
3.结构体总大小为最大对齐数(每个成员变量都有一个对其数)的整数倍。
4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对其数的整数倍处,结构体的整体大小就是所有对齐数(含嵌套的对齐数)的整数倍。
例如:
struct S1 { char c1; int i; char c2; } printf("%s\n",sizeof(struct S1));//1+3+4+1+3=12 struct S2 { char c1; int i; }; printf("%d\n", sizeof(struct S2));//1+1+2+4=8 struct S3 { double d; char c; int i; }; printf("%d\n", sizeof(struct S3));//8+1+3+4=16 struct S4 { char c1; struct S3 s3; double d; } printf("%d\n", sizeof(struct S4));//1+7+16+8=32
5.结构体传参
结构体传参要传地址,指针要比整个结构要小的多,所以要把它压堆栈上效率要高很多。传递指针另外需要付出的代价是我们必须在函数中使用间接访问来访问结构体成员。结构越大,把指向它的指针传递给函数的效率就越高。
位段
位段的声明和结构类似,但又两个不同:
1.位段的成员必须是int、unsigned int 或signed int。
2.位段的成员后边有一个冒号和一个数字。
例如:
struct S { char _a:3; //冒号后的数字表示几个比特位 char _b:4; char _c:5; char _d:4; }
位段的内存分配:
1、位段的成员可以是int、unsigned int 、sigend int或者是char(属于整形家族)
2、位段的空间是按照需要以四个字节(int)或者1个字节(char)的方式开辟
3、位段涉及很多不确定的因素,位段是不跨平台的,注重可移植的程序应该避免使用位段
位段的跨平台问题
1、int位段被当成有符号数还是无符号数是不确定的
2、位段最大位的数目不能确定(16位机器最大16,32位机器最大32,写成27,在16位机器上会出问题)
3、位段中的成员在内存中是从左到右分配,还是从右到左分配标准尚未定义
4、当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用还是不确定
枚举
定义:
枚举就是一一列举
enum Color { RED, GREEN, BLUE };
上面{}定义的是枚举常量,这些可能取值都是有值的,默认从0依次递增1,当然在定义时也可以赋初值
枚举的优点:(与宏比较)
1、增加代码的的可读性和可维护性
2、#define 定义的标识符比较枚举有类型检测,更加严谨
3、防止命名污染
4、便于调试
5、使用方便,一次可以定义多个常量
联合(共用体)
联合(共用体)的定义
联合也是一种特殊的自定义类型,这种类型页包含一系列的成员,特征是这些成员共用同一块空间(所以也叫共用体)。比如:
union Un { char c; int i; };
联合的初始化:
union Un { char c; int i; }a; a.i=10;
判断当前计算机的大小端存储
int check_sys() { union Un { int i; char c; }un; un.i=1; return un.c ; } int main() { if(check_sys()==1) printf("小端\n"); else printf("大端\n"); return 0 }
联合大小的计算
(1)联合的大小至少是最大成员的大小。
(2)当最大成员大小不是最大对齐数的整数倍的时候。就要对齐到最大对齐数的整数倍