1.结构体
1.1结构体的基础知识
即不同类型的集合:
类比数组,数组是同一类型的集合
而结构体可以是不同类型的集合
1.2结构体声明
struct stu
{
int age;
char name[20];
char tel[12];
}s1, s2; //可直接在该处创建结构体变量(对象),此时的变量s1, s2为全局变量
int main()
{
struct stu s = { 18, "zhangsan", "1008611" };//也可以在此处创建结构体变量(对象),此时s为局部变量
printf("%d %s %s", s.age, s.name, s.tel);
return 0;
}
1.3结构体的特殊声明
结构体匿名
struct //此时没有声明结构体即结构体匿名,但该写法在语法上亦支持,该写法只能调用一次
{
int age;
char name[20];
char tel[12];
}s;
1.4结构体的自引用
struct Node
{
int date;
struct Node* next;
};
int main()
{
sizeof(struct Node);
return 0;
}
写法1:
typedef struct Node
{
int date;
struct Node* next;
}*linklist;
写法2:
struct Node
{
int date;
struct Node* next;
};
typedef struct Node* linklist;
1.5结构体变量的定义和初始化
struct num
{
int x;
int y;
}s = {2, 3};//此时即创建变量并且初始化
struct stu
{
int age;
char ch[20];
struct num n;
}s2 = { 18, "zhangsan", {3, 4} };//结构体嵌套初始化
int main()
{
struct stu s = { 18, "zhangsan", {3, 4} };
printf("%d %s %d %d", s.age, s.ch, s.n.x, s.n.y);
return 0;
}
1.6结构体内存对齐
结构体对齐规则:
1.结构体内首成员位于结构体变量的首地址处
2.其他成员要对齐到某个数字(对齐数)的整数倍的地址处
对齐数=编译器默认的一个对齐数与该成员大小的较小值
vs默认值为:8
3.结构体的总大小为最大对齐数的整数倍(每个成员都有一个对齐数)
4.嵌套结构体的情况:嵌套的那个结构体对齐到自己最大对齐数的整数倍处
eg1:
struct s1
{
char c1;//该成员对齐数:默认值8与该类型所占字节1相比,选取较小值为对齐数,故c1对齐数为1
int i; //默认值为8,该类型所占字节为4,选取较小值,故i对齐数为4;因为1不为4的整数倍,故往下
char c2;//同理c2对齐数为1,此时8为1的倍数,故直接存放进去
};
int main()
{
printf("%d\n", sizeof(struct s1));//结构体总体大小为最大对齐数的整数倍
return 0;
}
eg2:
struct s2
{
char c1; //按上述规则对齐数c1为1
char c2; //c2为1
int i; //对齐数为4
};
int main()
{
printf("%d\n", sizeof(struct s2));
return 0;
}
eg3:
struct s2
{
char c1; //按上述规则对齐数c1为1
char c2; //c2为1
int i; //对齐数为4
};
struct s3
{
char c1;
char c2;
struct s2 s;//嵌套的结构体将对齐到自己最大对齐数的整数倍处,由上述可知,s2最大对齐数为4,此时对齐到4
};
int main()
{
printf("%d", sizeof(struct s3));
}
1.7修改默认对齐数
#pragma pack(1)//此时将默认值修改为1
struct s2
{
char c1; //按上述规则对齐数c1为1
char c2; //c2对齐数为1
int i; //对齐数为1
};
struct s3
{
char c1;
char c2;
struct s2 s;//嵌套的结构体将对齐到自己最大对齐数的整数倍处,由上述可知,s2最大对齐数为1,此时对齐到2直接存放
};
int main()
{
printf("%d", sizeof(struct s3));
}
#pragma pack();//代表修改的默认值在该处中止
1.8结构体传参
更具体内容可看前文结构体(初阶)文章
struct S
{
int arr[3];
int num;
};
void print1(struct S s)//传值在该处再次开辟一份空间copy实参,大大浪费内存,效率低
{
int i = 0;
for (i = 0; i < 3; i++)
{
printf("%d ", s.arr[i]);
}
printf("%d\n", s.num);
}
void print2(const struct S* s)//保护该指针所指向的对象防止被修改
{
int i = 0;
for (i = 0; i < 3; i++)
{
printf("%d ", s->arr[i]);
}
printf("%d\n", s->num);
}
int main()
{
struct S s = { {1, 2, 3}, 66 };
print1(s); //传值
print2(&s); //传址
return 0;
}
2.位段
2.1位段概念
位段用于结构体上:
位段的成员必须是整形家族(如:int, unsigned int, signed int, char)
成员名后必须加冒号和数字
eg:
struct s //此时s就是一个位段类型
{
int _a : 3;//位段用法:变量名后加冒号和数字
int _b : 4;//数字表示_b只要分配4个比特位的内存
int _c : 5;
};
2.2位段的内存分配
在各个平台的内存分配方式不一样,也没统一的标准规定位段的分配方式,所以位段的跨平台应用性比较差
代码移植性低,但可以节省内存空间
3.枚举
枚举类型同结构体,int, float, 等等一样,是个类型
枚举类型是个自定义类型
eg:
enum day //此时day就是个枚举类型,类似于结构体的声明,声明这个枚举类型
{
mon = 1, //此时mon,thus等为枚举常量,枚举常量是常量,有具体值
thus, //默认枚举类型里的第一个枚举常量默认赋值为零
wed = 5, //后面枚举常量值依次加1,即thus==1, wed==2以此类推
thur,
fri, //枚举常量在初始化时赋值,如:mon=1,此时直接赋值为1
sat, //后面的枚举常量初始化时一样可以赋值,如wed=5,此时即为5、
sun //没有赋具体值的枚举常量就在前一个枚举常量的值的基础上加1
};
int main()
{
printf("%d\n", mon); //此时mon为1
printf("%d\n", thus); //mon基础上加1为2
printf("%d\n", wed); //此时wed初始化赋值为5
printf("%d\n", thur); //wed基础上加1为6
return 0;
}
枚举优点:
1.增加代码可读性和可维护性
2.和define定义的类型相比枚举类型有类型检查,逻辑更严谨
3.便于调试
4.一次性定义多个常量
4.联合(共用体)
4.1联合体定义特点与结构体差别:
联合体在内存中各个成员共用一块内存空间
在很大程序上可以节省内存空间
union Un //联合体也是自定义类型:内部成员共用一块内存空间
{ //union为联合体(与结构体相类似去来理解)
//Un为联合体声明,类似于(结构体声明)
char c;
int i;
}u; //此时创建联合体变量(对象)u
int main()
{
printf("%p\n", &u);
printf("%p\n", &u.c);
printf("%p\n", &u.i);
return 0;
}
int main()
{
u.c = 1;
printf("%d\n", u.c);
u.i = 2;
printf("c=%d\n", u.c);
printf("i=%d\n", u.i);
/*因为他们用的是共同内存,调用时该内存就会被覆盖,故不能同时调用该内存赋值
该内存所存的值为最后一次调用创建的值*/
}
4.2联合体大小计算
1.与结构体一样有同样的对齐规则
2.所开辟的空间最小为内部最大成员所占空间
eg1:
union Un
{
char ch; //所占字节1,对齐数为1
int i; //所占字节数4,对齐数为4
}u;
int main()
{
printf("%d", sizeof(u));
//此时联合体对齐数为4,
return 0;
}
eg2:
union Un
{
char ch[5]; //在内存中占用5个字节,对齐数为1
int i; //在内存中占用4个字节,对齐数为4
}u;
int main()
{
printf("%d", sizeof(u));
//联合体对齐数为内部成员最大对齐数,此时对齐数为4
//而5不是4的整数倍,联合体仍需往下开辟空间
//当开辟到8时为对齐数的整数倍,停止开辟
//多开辟出来的字节数为浪费的字节数
return 0;
}