目录
一、结构体声明
struct Stu//结构体关键字,标签(名字)
{
char name[20];
char tele[12];
char sex[10];
int age;
}s4, s5, s6;//全局变量
struct Stu s3;//全局变量
int main()
{
struct Stu s1;//局部
struct Stu s2;//变量
return 0;
}
一些特殊声明
struct
{
char name[20];
char tele[12];
char sex[10];
int age;
}x;
没有了名字,是一种匿名类型。如果不在下面紧接着声明x这个变量,那么就没有别的方法去声明变量。
struct
{
char name[20];
char tele[12];
char sex[10];
int age;
}sa;
struct
{
char name[20];
char tele[12];
char sex[10];
int age;
}*psa;
完全一样,如果这时候psa = &sa,编译器会报错。因为编译器会把他们当成两个独立的个体,不合法。
匿名结构体不要用。
二、自引用
参考数据结构中的链表类型,12345五个数字,都是结点,通过1能连接到2,2连到3,依次类推。结构体中也可以实现
struct Node
{
int data;
struct Node n;
};
这样的代码,data代表着1,n就是下一个结构体。但是结构体就太大了,Node n里面还放着一个数据,一个结构体,这一个结构体还放着一个数据,一个结构体,所以说无法计算大小。既然目的是通过一个结点找到另一个结点,那么只要存放地址即可,所以:
struct Node
{
int data;
struct Node* next;
};
可以用typedf来重命名
typedef struct Node
{
int data;
struct Node* next;
}Node;
但是必须原本就有结构体的名字,不能去重命名匿名结构体。
三、定义和初始化
之前也写过,里面也可以嵌套初始化,比如初始化一个结构体
四、内存对齐(计算结构体大小)
规则:
第一个成员在与结构体变量偏移量为0的地址处存放
其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数=编译器默认的一个对齐数 与 该成员大小的较小值。
结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍
gcc编译器没有默认对齐数,所以对齐数就是成员大小
如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍
#include <stdio.h>
struct S1
{
char c1;
int a;
char c2;
};
struct S2
{
char c1;
char c2;
int a;
};
int main()
{
struct S1 s1 = { 0 };
printf("%d\n", sizeof(s1));
struct S2 s2 = { 0 };
printf("%d\n", sizeof(s2));
return 0;
}
S1,char占1个字节,int需要挪到到第5个字节处存储,前面有4个字节,3个空着,第一个位置存储char,这时候4 + 4,就已经占据8个字节了,char自然是接着占1个字节,这时候就是9,但这不是4的倍数,把char放到第12个字节处,所以大小就是12。
S2,char占1个字节,char占1个字节,这时候是2,int需要到第5个字节处存储,所以是4 + 4,8是4的倍数,所以大小就是8个字节。
嵌套
struct S3
{
double d;
char c;
int i;
};
struct S4
{
char c1;
struct S3 s3;
double d;
};
为什么要存在内存对齐?
没有官方原因,很多资料是这样的:
平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。(有些平台,访问int类型的数据,只能去访问4的整数倍的地址处取得)
性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要做两次内存访问;而对齐的内存访问仅需要一次访问。
总结:结构体的内存对齐是拿空间换时间的做法
假设一个32位的机器,每次读取都是4个字节。如果不对齐,对于一个char和int类型的数据,想要读取完全这个int类型的数据,那么第一次读取4个字节,读到了char数据和部分int数据的空间,需要第二次读取才能读到完整的int数据。如果对齐了,char后面空3个字节再放上int数据,也需要读两次,但是对于int数据,第二次读就可以读完。
在设计结构体的时候,我们既要满足对齐,又要节省空间,那么:
让占用空间小的成员尽量集中在一起。
修改默认对齐数
#pragma预处理指令可以改变
#pragma pack(4)//设置默认对齐数为4
struct S
{
char c1;
double d1;
};
#pragma pack()//取消设置的默认对齐数
另外一个关键字offsetof(structName, memberName)
offset就是计算偏移量,of 就是指哪个元素
#include <stdio.h>
void Init(struct S* ps)
{
ps->a = 100;
ps->c = 'w';
ps->d = 3.14;
}
void Print1(struct S tmp)
{
printf("%d %c %lf\n", tmp.a, tmp.c, tmp.d);
}
void Print2(const struct S* ps)
{
printf("%d %c %lf\n", ps->a, ps->c, ps->d);
}
int main()
{
struct S s = { 0 };
Init(&s);
Print1(s);
Print(&s);
return 0;
}
void Print1(struct S tmp)
传值和传址调用。
如果传过去的参数过大,那么就会出问题,压栈。
之前写过
第二篇写位段
结束。