一,结构体声明
C语言中为我们准备了许多现成的数据类型例如: int short float double char long long long
等等. . . . . . 但是我们描述一些复杂的事物,光靠上述的数据类型是描述不清的,例如,我们描述一个大学生,可以描述他的身高,年龄,学号等等。
那么结构体类型是如何生命的呢?
struct Stu
{
int hight;
char num[20];
int age;
};
注意: 大括号后面的分号不能丢哦。
二,特殊结构体声明
结构体在声明的时候,可以不完全声明,像下面一样。
struct
{
int a;
int b;
char c;
}x;
struct
{
int a;
int b;
char c;
}b,*p;
但是,当不完全声明的时候,我们可以将x变量的地址,存到p指针当中吗?
p = &x;
可以看到,是不可以的,虽然两个结构体变量的各个成员相同,但是编译器仍然认为这是两个不同的结构体。
并且不完全声明的结构体,不能再用来定义新的变量例如下面
struct
{
int a;
int b;
char c;
};
int main()
{
struct x;
return 0;
}
不完全声明的结构体如果想定义变量,只能在声明的同时定义变量例如下面
struct
{
int a;
int b;
char c;
}x;
struct
{
int a;
int b;
char c;
}b,*p;
三,结构体自引用
我们先看几行代码
struct Node
{
int data;
struct Node next;
};
这样的代码,可行吗?
如果可行的话?那sizeof(struct Node)是多少呢?
我们可以看出结构体在不断的嵌套你中有我,我中有你,不断地这样下去,想必肯定是错误的。 实现结构体自引用的正确的方法如下:
struct Node
{
int data;
struct Node* next;
};
四,结构体变量的定义与初始化
1,在结构体声明的同时初始化
struct stu
{
int age;
int hight;
char num[20];
}ss1;
2,在结构体声明后定义
int main()
{
struct stu ss2;
return 0;
}
3,按照结构体成员顺序初始化
int main()
{
struct stu ss2 = { 19,182,"47385782" };
return 0;
}
4,不按照成员顺序初始化
int main()
{
struct stu ss3 = { .hight = 183,.num = "42345235",.age = 22 };
return 0;
}
五,结构体内存对齐
我们先看一段代码
struct a
{
char c1;
int i;
char c2;
};
struct b
{
char c1;
char c2;
int i;
};
int main()
{
printf("%d\n", sizeof(struct a));
printf("%d\n", sizeof(struct b));
return 0;
}
你想一下,答案是什么呢?
可能有人会想,这两个的答案不应该都是6吗? 首先,我们先介绍一个宏:offsetof 他的返回值是,结构体成员相对于结构体变量的偏移量 例如:
struct a
{
char c1;
int i;
char c2;
};
#include<stddef.h>
int main()
{
printf("%d\n", offsetof(struct a, c1));
printf("%d\n", offsetof(struct a,i));
printf("%d\n", offsetof(struct a, c2));
return 0;
}
可以看到根据offsetof的返回值,画出的图是上述那样的,结构体变量的大小应该是9个字节大小啊。 下面我们就来介绍一下,结构体内存对齐的规则
规则: (1)结构体变量的第一个成员永远放在相对于结构体变量偏移量为0 的位置。 (2)其他成员放在偏移量为其对齐数整数倍的位置处 对齐数:为默认对齐数和结构体成员大小的较小值 vs:默认对齐数 8 Linux:不设置默认对齐数 (3)结构体变量的总大小为最大对齐数的整数倍 (4)如果嵌套了结构体的情况,嵌套的结构体对齐到自己最大对齐数的整数倍处, 结构体的总大小为最大对齐数的整数倍(包括嵌套结构体中的对齐数)
下面,针对第四条写一段代码
struct c
{
int i;
char c;
struct d
{
double;
int n;
short t;
};
};
int main()
{
printf("%d\n", sizeof(struct c));
return 0;
}
那么这个结构体的总大小是多少呢? 答案是24,你想对了吗?
六,修改默认对齐数
可以用#pragma pack()来改变默认对齐数
#pragma pack(4)//改变默认对齐数为4
#pragma pack()//回复默认对齐数
七,结构体传参
传参有两种方式 (1)传值
struct STU
{
int age;
int hight;
char num[20];
};
void print1(struct STU s)
{
printf("%d\n", s.age);
printf("%d\n", s.hight);
printf("%s\n", s.num);
}
int main()
{
struct STU ss1 = { 19,182,"154235265" };
print1(ss1);
return 0;
}
(2)传址
void print2(struct STU* s)
{
printf("%d\n", s->age);
printf("%d\n", s->hight);
printf("%s\n", s->num);
}
int main()
{
struct STU ss1 = { 19,182,"154235265" };
print1(ss1);
print2(&ss1);
return 0;
}
我们知道形参是实参的一份临时拷贝,会开辟新的空间,如果当我们的结构体较大的时候,利用传值的传参方式会很浪费空间,所以结构体传参的时候采用传址调用比较好。
|