【C语言】手把手教会结构体

自定义类型——结构体

在C语言中,我们可以将类型分为:

  • 内置类型
  1. char
  2. short
  3. int
  4. long
  5. float
  6. double
  7. ....
  • 自定义类型(C语言允许自创的类型)
  1. 结构体
  2. 枚举
  3. 联合体(共用体)


A.结构体概括

我们学习过数组,数组为由一种类型的集合

而结构体也与数组类似,结构体可多种类型的集合


B.结构体的声明与组成

注意:创建结构体时必须包含"{};" 花括号的分号不能忘

特殊声明:

           注意:匿名结构体类型只能用一次

p = &x;时

这种情况是不允许的,编译器会认为两个不同类型,所以非法


 C.结构体创建变量与初始化

//方式一:
struct S
{
	int a;
	char b[10];
	float c;
}s;

//方式二:
struct S
{
	int a;
	char b[10];
	float c;
};
int main()
{
	//初始化
	struct S s = {1,"张三",178.5};
	printf("%s", s.b);
	return 0;
}

//方式三:
struct S
{
	int a;
	char b[10];
	float c;
};
struct S s = { 1,"张三",178.5 };
int main()
{
	//初始化
	printf("%s", s.b);
	return 0;
}

//方式四:
struct S
{
	int a;
	char b[10];
	float c;
};
int main()
{
	//初始化
    // 正常情况再{}里初始化要与struct里的成员变量位置一一对应
    // 当想不按照顺序初始化时,则需要通过 .成员变量名 的方式初始化
	struct S s = {.b = "zhangsan", .c = 3.14f};
	printf("%s", s.b);
	return 0;
}

           


 D.结构体的自引用

利用typedef自定义结构体类型名:


E.结构体内存对齐

结构体类型在内存中是如何存储的呢?大小又是多少?是否是将成员变量的大小加起来 = 结构体类型大小?接下来我将一一解答:

// struct 的大小计算方式
// 成员变量中不包括struct

struct s
{
    char s;
    int a;
}s;


// 计算规则:
// 1、将第一个成员变量放在从0偏移量处,根据字节数,决定占据多少空间。
// 注:VS 编译器平台上默认对齐数为 8,gcc没有默认对齐数,则取自身大小
// 2、其它的成员变量,第一步:先计算出自身的对齐数,公式为:自身字节数 与 编译器默认对齐数对比出谁更小,则较小的数为自身的对齐数。如:int a; 的字节数为4,编译器默认为8,则int a的对齐数为 4; 第二步:根据自身对齐数,找到符合自身对齐数倍数的偏移量,如:int a对齐数为4,则自身倍数对应的偏移量为8,因此int a的空间应该从偏移量8这个位置开始,依次占据8 9 10 11这4个空间.
// 3、最后计算结构体的大小,第一步:利用 2、 的方法,分别算出结构体中所有成员变量的对齐数;
      第二步:进行对比,得到成员变量中最大的对齐数,然后在偏移量中观察符合 最大对齐数倍数的字节数。如:现在偏移量空间到了11,因为偏移量的范围是0 ~ 11,则现在为12个字节了,现在比较出最大的对齐数是4,则需要是关于4的倍数,的字节数,那12字节为4的倍数,则该struct s大小为12.


// 成员变量中包括struct
struct a
{
    char l;
    int s;
}a;
struct s
{
    char s;
    int a;
    struct a a;
}s;

// 这种计算规则和上述差不多
// 对于成员变量中有结构体类型时
// 我们要得到struct a a 中最大的对齐数,就是成员变量中最大的对齐数后;
   找到这个最大对齐数所对应偏移量的倍数,如:4 就要找到关于4倍数的偏移量;
   然后在此处开始依次占据空间,占据多少,取决该结构体的大小

为什么结构体类型存在内存对齐呢?

1. 平台原因(移植原因):
        不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

2. 性能原因:
        数据结构(尤其是栈)应该尽可能地在自然边界上对齐。
原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

总体来说:
        结构体的内存对齐是拿空间来换取时间的做法。

那在设计结构体的时候,我们既要满足对齐,又要节省空间,如何做到:


F.修改默认对齐数

我们可以利用 #pragma 这个预处理指令,改变我们的默认对齐数

结论:
结构在对齐方式不合适的时候,我么可以自己更改默认对齐数。


 G.结构体传参

观察以下代码:

//结构体传参选择
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);
}
//结构体地址传参
void print2(struct S* ps)
{
	printf("%d\n", ps->num);
}
int main()
{
	print1(s); //传结构体
	print2(&s); //传地址
	return 0;
}

我们是在调用函数,将结构体作为参数时,时使用值传递好还是地址传递好呢?

答案是:首选print2函数。
        原因:
        函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。
如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值