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
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;
}
可以用#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;
}
我们知道形参是实参的一份临时拷贝,会开辟新的空间,如果当我们的结构体较大的时候,利用传值的传参方式会很浪费空间,所以结构体传参的时候采用传址调用比较好。