上篇文章介绍了一些常用的字符串函数,大家可以去我的主页进行浏览。
各种源码大家可以去我的github主页进行查找:Nerosts/just-a-try: 学习c语言的过程、真 (github.com)
今天要介绍的是:结构体的相关内容
结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量
结构的声明的原型:
struct tag { member - list ; } variable - list ;
eg:
struct Student
{
char name[20];//名字
int age;//年龄
char sex[5];//性别
char id[20];//学号
}; //分号不能丢
匿名结构体:
struct
{
int a;
char b;
float c;
}x;
struct
{
int a;
char b;
float c;
}a[20], *p;
我们也许回想在结构中包含一个类型为该结构本身的成员是否可以呢?像下面这样
struct Node
{
struct Node a1;
int date[2];
};
这种情况下,需要确保结构体类型的定义是在使用它的结构体类型之前。否则,编译器将无法确定结构体类型的大小。
所以是不行的,正确的自引用方式如下:
struct Node
{
int data;
struct Node* next;
};
注意:
typedef struct
{
int data;
Node* next;
}Node;
在定义指针变量next时,使用了Node类型。由于Node类型的定义在当前代码中尚未完成,编译器无法识别Node类型
正确的如下:
typedef struct Node
{
int data;
struct Node* next;
}Node;
struct Point
{
int x;
int y;
}p1; //声明结构体的同时定义变量p1
struct Point p2; //定义结构体变量p2
//也可以
struct Point p3 = {x, y};//初始化:定义变量的同时赋初值。
struct Stu //类型声明
{
char name[15];//名字
int age; //年龄
};
struct Stu s = {"zhangsan", 20};//初始化
struct Node
{
int data;
struct Point p;
struct Node* next;
}n1 = {10, {4,5}, NULL}; //声明结构体的同时嵌套初始化
struct Node n2 = {20, {5, 6}, NULL};//单独结构体嵌套初始化
struct S
{
int data[1000];
int num;
};
struct S s = { {1,2,3,4}, 1000 };
//结构体传参
void print1(struct S s)
{
printf("%d\n", s.num);
printf("%d\n", s.data[0]);
printf("%d\n", s.data[1]);
printf("%d\n", s.data[2]);
}
//结构体地址传参
void print2(struct S* ps)
{
printf("%d\n", ps->num);
printf("%d\n", ps->data[0]);
printf("%d\n", ps->data[1]);
printf("%d\n", ps->data[2]);
}
int main()
{
print1(s); //传结构体变量
printf("_________");
print2(&s); //传地址
return 0;
}
两种调用,传参的结果都是一样的:
但是,还是用printf2来传地址时更好:
函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。 如果传递一个结构体对象的时候, 结构体过大,参数压栈的的系统开销比较大 ,所以会导致性能的下降
struct S1 { char c1 ; int i ; char c2 ; }; 这个结构体有多大呢? 第一反应大抵是:1+4+1=6吧
但其实:
1. 第一个成员在与结构体变量偏移量为 0 的地址处。 2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。 对齐数 = 编译器默认的一个对齐数 与 该成员大小的 较小值 。 VS中默认的值为8 3. 结构体 总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍 。 4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
按照对其规则,这是上述答案的分析:
接下来再看另外一个例子:
eg:
struct S2
{
char c1;
char c2;
int i;
};
int main()
{
printf("%d", sizeof(struct S2));
return 0;
}
答案便是:
结构体的内存对齐是拿空间来换取时间的做法
一些资料也显示说:
那我们如何尽量减少内存对齐所产生的浪费呢?
让占用 空间小的成员尽量集中 在一起
所以上述S1的例子我们可以改造成:
struct S1
{
char c1;
int i;
char c2;
};
struct S2
{
char c1;
char c2;
int i;
};
int main()
{
printf("%d\n", sizeof(struct S1));
printf("%d", sizeof(struct S2));
return 0;
}
结果也确实减小了内存使用:
这里我们使用#pragma,可以改变我们的默认对齐数
#pragma pack(8)//设置默认对齐数为8
struct S1
{
char c1;
int i;
char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
#pragma pack(1)//设置默认对齐数为1
struct S2
{
char c1;
int i;
char c2;
};
int main()
{
printf("%d\n", sizeof(struct S1));
printf("%d", sizeof(struct S2));
return 0;
}
这次S2的大小便是我们最初认为的6了:
位段的声明和结构是类似的,有两个不同:
struct A {
int a : 2; //a占用2个bit位
int b : 5; //b占用5个bit位
int c : 10;
int d : 30;
};
int main()
{
struct A a;
printf("%d", sizeof(a));
}
不使用位段的话是占16个字节,现在是:
struct S
{
char a : 3;
char b : 4;
char c : 5;
char d : 4;
};
int main()
{
struct S s = { 0 };
s.a = 10;
s.b = 12;
s.c = 3;
s.d = 4;
return 0;
}
这次关于结构体相关的内容就先到这里啦!
在下一篇文章中,我们将详细介绍枚举和联合体的内容。感谢大家的支持,加油!!!