1.结构体声明
先来回顾一下结构体的基本概念:结构体是一些值的集合(可以为不同类型的数据),其中这些值被称为成员变量。
[1]结构体声明
由 struct+结构体标签
{
变量1;
变量2;
变量3;
... ...
}变量名;所组成
//例如创建一个结构体描述学生信息
struct Stu//这一部分相当于结构体类型,与int,char用法类似
{
char name[10];
char sex[5];
int age;
char id[10];
}s1,s2;//全局变量
//可以在变量名处创建新的变量,这种变量属于全局变量
//也可以在main函数内部定义结构体变量
int main()
{
struct Stu s3;
//此时创建的结构体变量属于局部变量
return 0;
}
通常为了简便,我们会采用typedef来进行结构体类型的重定义
typedef struct Stu
{
char name[10];
char sex[5];
int age;
char id[10];
}Stu;
//在创建新的结构体变量的时候便可以这么使用
Stu s1,s2;
//typedef struct Stu Stu;
//也可以将typedef单独使用
[2]特殊声明
在声明结构的时候,可以不完全声明。称为匿名结构体类型 ,在声明时省略结构体标签,但要在结构体声明的时候就创建变量,且只能使用一次。
struct
{
char name[10];
char sex[5];
int age;
char id[10];
}a;
struct
{
char name[10];
char sex[5];
int age;
char id[10];
}*p;
int main()
{
//struct s3;
//这种写法是错误的,没有完整的结构类型
return 0;
}
注意:p=&a编译器并不认为这两种变量的类型一致,因此这种写法是错误的
当然也可以对这种匿名结构体类型进行typedef重定义
typedef struct
{
char name[10];
char sex[10];
int age;
char id[10];
}node;
//struct
//{
// char name[10];
// char sex[5];
// int age;
// char id[10];
//};
//typedef struct node;
//而这种写法却不行,node并不算一个完整地结构体类型
//不能单独去重定义一个匿名结构体类型
int main()
{
node s1={"Tom","female",12,"u1023"};
node s2={"Jerry","male",15,"u1239"};
//使用重定义过的结构体类型可以创建多个变量
return 0;
}
[3]结构体的自引用
struct Node
{
int data;
struct Node next;
};
//在结构体中包含一个类型为该结构体本身的成员,这样就无法确定结构体的大小了
//在struct Stu大小还未确定的情况下,在结构体内部使用该结构体类型创建新的结构体成员
//因此这种写法本质上是错误的
struct Node
{
int data;
struct Node* next;
};
//这种写法是可行的,因为结构体指针大小是明确的4/8字节
2.结构体初始化
struct Stu
{
char name[10];
int age;
char sex[10];
}s1={"Tom",19,"male"},s2={"Jerry",20,"female"};//全局变量
int main()
{
struct Stu s3 = { "lisi",26,"male" };//局部变量
printf("%d %d %d", s1.age, s2.age, s3.age);
return 0;
}
注意:全局变量未初始化时,整形变量会默认为0,字符型会默认为'\0'而局部变量未初始化则是随机值,若局部变量写为={ }则默认为0('\0')
1.当全局变量未初始化时
struct Stu
{
char name[10];
int age;
char sex[10];
}s1,s2;//全局变量
int main()
{
struct Stu s3 = { "lisi",26,"male" };//局部变量
printf("%d %d %d ", s1.age, s2.age, s3.age);
printf("%s %s %s ", s1.name, s2.name, s3.name);
printf("%s %s %s ", s1.sex, s2.sex, s3.sex);
return 0;
}
输出结果为:
2.当局部变量未初始化时
struct Stu
{
char name[10];
int age;
char sex[10];
}s1 = { "Tom",19,"male" }, s2 = { "Jerry",20,"female" };//全局变量
int main()
{
struct Stu s3;//局部变量
printf("%d %d %d", s1.age, s2.age, s3.age);
printf("%s %s %s ", s1.name, s2.name, s3.name);
printf("%s %s %s ", s1.sex, s2.sex, s3.sex);
return 0;
}
输出结果为:
VS上:会报错无法输出
vc++6.0上:会出现随机值
3.当局部变量写为={ }时
struct Stu
{
char name[10];
int age;
char sex[10];
}s1 = { "Tom",19,"male" }, s2 = { "Jerry",20,"female" };//全局变量
int main()
{
struct Stu s3={};//局部变量
printf("%d %d %d ", s1.age, s2.age, s3.age);
printf("%s %s %s ", s1.name, s2.name, s3.name);
printf("%s %s %s ", s1.sex, s2.sex, s3.sex);
return 0;
}
输出结果为:
3.结构体内存对齐
[1]结构体的对齐规则:
结构体的第一个成员,对齐到结构体在内存中存放位置的0偏移处。
从第二个成员开始,每个成员对齐到某个数字(对齐数)的整数倍偏移处。
对齐数:结构体成员自身大小和默认对齐数的较小值
默认对齐数根据编译器的不同而有所区别,在VS中默认对齐数为8,而在Linux gcc中没有默认对齐数,对齐数就是结构体成员的自身大小
结构体的总大小,必须是所有成员对齐数中最大对齐数的整数倍。
如果结构体中嵌套了另外一个结构体,要将结构体对齐到自己的最大对齐数的整数倍,结构体的整体大小是最大对齐数(含嵌套结构体的对齐数)的整数倍。
其中数组的对齐数为数据类型的大小(只看结构体成员的类型)eg:char arr[5]的对齐数是1(相当于5个char类型变量) int arr[10]的对齐数是4
下面举个栗子来感受一下吧
那么为什么要进行内存对齐呢?
[2]修改默认对齐数
VS默认对齐数是8修改默认对齐数通过预处理指令#pragma完成
#pragma pack(4)//修改默认对齐数为4
struct Stu
{
char name[5];
int age;
char sex[5];
};
#pragma pack()//恢复编译器原有默认对齐数,为8
int main()
{
printf("%d ", sizeof(struct Stu));
return 0;
}
[3]结构体传参
struct Stu
{
char name[10];
int age;
char sex[5];
};
void print1(struct Stu s)//结构体接收
{
printf("%d", s.age);
}
void print2(struct Stu* s)//结构体指针接收
{
printf("%d", s->age);
}
int main()
{
struct Stu s1 = { "zhangsan",19,"male" };
print1(s1);//传结构体
print2(&s1);//传结构体的地址
return 0;
}
上面的两种传参方式,显然第二种传地址的方式较好。
原因:传参是需要压栈的,会有时间以及空间上的消耗。由于传递一个结构体变量时,结构体过大,压栈时开销较大,会导致性能下降,因此首选结构体指针作为参数。
3.位断
【1】定义:
1.成员必须是int, unsigned int, signed int.(char 也可以 因为它属于整形家族)
2.成员名后必须有一个冒号和数字。
用结构体实现位断
struct S
{
char a : 3;
char b : 4;
char c : 5;
char d : 4;
}s1;
【2】位断的内存分配
在空间上是按照4字节(int型)或1字节(char型)的方式开辟空间的。
位断涉及很多不确定因素,不跨平台,可移植程序应避免使用位断。
下面通过一个栗子来了解位断的内存分配:
通过查看内存存储发现确实如此:
4.位断的跨平台问题
根据上面栗子的分析可以总结出位断所存在的一些问题:
int型位断被当做有符号数还是无符号数是不确定的
位断中最大位数目无法确定(在早期16位机器中,int是16比特位,也就是2字节,如果位断的整形部分写为27在16位机器上会报错;而现在32位机器上int是32比特位,也就是4字节)
位断中成员在内存中是由右向左分配空间,还是由左向右分配空间无法确定
当一个结构包含两个位断,其中第二个位断成员较大,无法容纳于第一个位断剩余空间时,是舍弃剩余空间还是利用剩余空间时不确定的
5.位断的应用
在我们日常聊天过程中,并非是简单的信息传输过程,而是有一套较为复杂的信息分装,这时就需要使用位断来节省空间,提高空间利用率。如果使用char/int等类型变量来进行存储,会浪费很多空间,因此选择位断来进行存储。