结构体创建
- 前言
- 结构体的声明
- 全局声明
- 特殊声明
- 结构体变量定义与成员初始化
- 成员类型
- 变量定义
- 匿名结构体变量定义
- 结构体的自引用
- 结构体成员的访问
- 成员初始化
- 操作符
- 结构体传参
- 结构体内存对齐
- 对齐数
- 对齐规则
前言
在生活中我们要描述一个人时是不是要知道他的名字、性别、年龄啊,同理在c语言中要描述一个人也要他相关的信息,而我们在c语言中储存一个人的信息时可以发现一个人的信息需要多种数据类型来储存我们可以把这类对象称之为复杂对象,因此我们来介绍自定义类型中的结构体,结构体就可以很好的储存一个复杂对象(如一个人或一本书)。
结构体的声明
全局声明
结构体声明的语法是这样的
struct 结构体名
{成员变量;
};/*注意分号不能丢*/
举例当我要描述一个人的信息时我们可以这样写。
#include <stdio.h>struct Jk
{char name[10];/*名字*/char gender[5];/*性别*/int age;/*年龄*/
};int main()
{return 0;
}
此时写的Jk这个结构体是全局的,如果想写成局部只需这样写
#include <stdio.h>int main()
{struct Jk{char name[10];/*名字*/char gender[5];/*性别*/int age;/*年龄*/};return 0;
}
特殊声明
还有一种特殊声明的声明叫不完全声明语法如下。
struct
{成员变量;
};
这种声明省略了结构体名此时的结构体叫匿名结构体。
此时的匿名结构体是无法使用的,他的使用我们在结构体变量定义与成员初始化里讲。
结构体变量定义与成员初始化
成员类型
结构的成员可以是标量、数组、指针,甚至是其他结构体。
变量定义
声明好结构体后,如何定义变量其实很简单他有三种定义的方法:
- 声明时顺带定义一个全局结构体变量
#include <stdio.h>struct Jk
{char name[10];/*名字*/char gender[5];/*性别*/int age;/*年龄*/
}jk;/*<--声明时顺带定义一个全局结构体变量*/int main()
{return 0;
}
- 定义一个全局结构体变量
#include <stdio.h>struct Jk
{char name[10];/*名字*/char gender[5];/*性别*/int age;/*年龄*/
};struct Jk n;/*<--定义一个全局结构体变量*/int main()
{return 0;
}
- 定义一个局部结构体变量
#include <stdio.h>struct Jk
{char name[10];/*名字*/char gender[5];/*性别*/int age;/*年龄*/
};int main()
{struct Jk n;/*<--定义一个局部结构体变量*/return 0;
}
除了结构体变量,结构体指针,结构体数组这些也是可以定义的如
#include <stdio.h>struct Jk
{char name[10];/*名字*/char gender[5];/*性别*/int age;/*年龄*/
}*p, arr[10];/*<--定义了一个结构体指针和结构体数组*/int main()
{return 0;
}
匿名结构体变量定义
匿名结构体变量定义只能在声明时顺带定义如下
#include <stdio.h>struct
{int a;
}n;/*<--只能在声明时顺带定义一个全局结构体变量*/int main()
{return 0;
}
当我们的程序中声明了两个匿名结构体时不管他两的成员是否相同,编译器都会把他俩看成两个不同的类型
结构体的自引用
在结构体成员中除了上面说的成员类型里说的成员类型,还可以包含一个类型为该结构体本身的指针成员的,写法如下
#include <stdio.h>struct KK
{char a;struct KK* p;/*<--包含了一个类型为 struct KK 的指针p*/
};int main()
{return 0;
}
此时的kk类型的结构体,就可以指向下一个同类型的结构体了
结构体成员的访问
成员初始化
只需要在{}中输入对应的数据就行了
#include<stdio.h>struct kk
{int a;char b;float c;int arr1[2];char arr2[];
}i3 = {1, 'a', 3.5f, {1,2}, "abc"};struct kk i2 = {1, 'a', 3.5f, {1,2}, "abc"};int main()
{struct kk i1 = {1, 'a', 3.5f, {1,2}, "abc"};return 0;
}
嵌套结构体初始化只需要在嵌套结构体成员对应的位置用{}来初始就可以了
#include<stdio.h>struct SS
{int a;char arr[];
};struct kk
{int a;struct SS n;
}i3 = {1, {1, "abc"} };struct kk i2 = {1, {1, "abc"} };int main()
{struct kk i1 = {1, {1, "abc"} };return 0;
}
操作符
结构体的操作符有两个
操作符1:.
使用方法:结构体 . 成员名
操作符2:->
使用方法:结构体指针 -> 成员名
#include <stdio.h>struct SS
{int a;char arr[];
}a1 = { 1, "abc" };/*<--声明时顺带定义一个全局结构体变量*/int main()
{int a = a1.a;char* arr1 = a1.arr;char arr2 = a1.arr[1];struct SS* p = &a1;int b = p->a;char* arr3 = p->arr;char arr4 = p->arr[1];return 0;
}
结构体传参
函数形参接收时只需要保持类型一致即可。
#include<stdio.h>struct kk
{int a;char b;float c;int arr1[2];char arr2[];
};void s1(struct kk n)
{}void s2(struct kk *p)
{}
int main()
{struct kk i1 = { 1, 'a', 3.5f, {1,2}, "abc" };s1(i1);s2(&i1);return 0;
}
以上两种方法虽然都可以接收但是s2的方法是比s1的方法好的,因为在函数传参中的形参是实参的一份临时拷贝,所以s1的参数在传输的过程中是要压栈的,如果传输的结构体很大那在他压栈时会花费很多的内存和时间,而s2的接收结构体地址的方法就可以很好的节约时间跟内存的开销。
结构体内存对齐
对齐数
在不同的编译器中对齐数也是不同的,如vs中默认的对齐数是8而linux 32默认是4,所以下面我们以vs的对齐数来介绍对齐规则。
对齐规则
我们先看结构体的对齐规则:
- 第一个成员在与结构体变量偏移量为0的地址处。
- 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
- 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
如int是4、char是1、float是4、double是8- 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
知道规则后我们来计算一下以下结构体
struct Jk{char a;char b;int c;char d;};
我们先把各个成员的对齐数求出来
char a 变量自带对齐数是1 默认对齐数是8 取较小值当对齐数也就是 1
char b 变量自带对齐数是1 默认对齐数是8 取较小值当对齐数也就是 1
int c 变量自带对齐数是4 默认对齐数是8 取较小值当对齐数也就是 4
char d 变量自带对齐数是1 默认对齐数是8 取较小值当对齐数也就是 1
先看规则一:第一个成员在与结构体变量偏移量为0的地址处。
所以我们的第一个成员a需要在偏移量为0的地址处开始往下用占一个字节
再看规则二: 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
此时0偏移量的地址被占用了所以我们从偏移量1的地址开始看,偏移量1是成员b的对齐数1的倍数我们就在偏移量为1的地址处开始往下占用1个字节。
此时1偏移量的地址被占用了所以我们从偏移量2的地址开始看,偏移量2、3都不是成员c的对齐数4的倍数,继续往下可以看到偏移量4是成员c的对齐数4的倍数我们就在偏移量为4的地址处开始往下占用4个字节。
此时没被占用的偏移量地址是8,偏移量8是成员d的对齐数1的倍数我们就在偏移量为8的地址处开始往下占用1个字节。
这时a、b、c、d就排好了但结束了吗?,并没有我们来看规则三:
结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
我们知道c是最大的对齐数4,通过上面的排序我们排到第9个字节显然9个字节不是偏移量4的倍数,所以我们要给还是9个字节的结构体多占用3个字节让他的大小变成12个字节,此时就满足了结构体大小是偏移量4的倍数这一条件了。
最终我没得出的结构体大小是12个字节,效果如下图
搞定是无嵌套的后,我们来计算嵌套的结构体
struct Jk1
{int b;char c;
};
struct Jk
{char a;struct Jk1 n;char d;
};
嵌套结构体只需加上规则四:
如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
嵌套结构体最大对齐数是4,所以只需要在4的倍数的偏移量那里开始向下占用嵌套结构体大小的字节即可。
当d占用完后此时的结构体为13个字节,我们知道这个结构体最大的对齐数是嵌套结构体中的b的对齐数4,显然此时的结构体并不满足规则四中的:
结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
所以要给结构体多占用3个字节,此时的结构体是16个字节就满足上面的条件了。
最终我们得出的结果是16个字节,效果如下图
以上就是全部内容了,如有错误欢迎在评论区指出,我会积极修正。