【C语言】关键字的深入理解(第六期)(上)

简介: 说起 struct 关键字,学过 C 语言的小伙伴应该都有了解,struct 也可以理解成自定义类型,我们知道 C 语言内置类型远远还不够满足我们的需求,假设说我们要录入一个学生的信息,简简单单的 int char 这些是完全不够的

1、struct 关键字的理解和柔性数组

说起 struct 关键字,学过 C 语言的小伙伴应该都有了解,struct 也可以理解成自定义类型,我们知道 C 语言内置类型远远还不够满足我们的需求,假设说我们要录入一个学生的信息,简简单单的 int char 这些是完全不够的,一个学生的信息拥有姓名,性别,年龄,身高,地址... 所以 C 语言就提供了 struct 可以使我们程序猿自定义想要的类型,我们就先简单看下它的语法吧:

但是我们通常在创建结构体的时候会用 typedef 来重命名一下,避免书写的复杂:

typedef struct stu
{
  char name[10];
  char sex;
  int age;
  float high;
  char addr[30];
}stu;
int main()
{
  stu s;
  return 0;
}

1.1 结构体传参问题

虽然现在我们还没讲到指针这个章节,所以这部分内容有指针基础的小伙伴可以看下,没基础的小伙伴可以等学完指针再回过头来看这个问题:

上面两个打印函数哪个函数会更好呢?

这里我们简单了解一个知识点,当结构体作为参数传递给函数时,我们可以选择传值调用或者是传址调用,简单来说,传值是需要在函数接收时在开辟一个相同大小的结构体来接收,(形参只是实参的一份临时拷贝)传址则只需要用一个同类型指针变量来即可,然后通过指针可以直接访问到这个结构体。

有了这样一个概念我们就应该能明白,显然是 Printf2 这个函数会更好一些!

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

结论: 结构体传参的时候,要传结构体的地址。

1.2 柔性数组的理解与简单使用:

或许有很多小伙伴没听过柔性数组的概念,但它确实存在,C99 中,结构中的最后一个元素允许是未知大小的数组,这就叫做柔性数组,但结构体中的柔性数组成员前必须至少有一个其他成员。包含柔性数成员的结构用 malloc( ) 函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。

struct Data
{
  int num;
  int arr[];
};

定义好了之后,我们通常就可以使用了(简单使用):

通过以上代码我们可以发现,我们用 malloc( ) 函数申请了一个 struct Data 类型大小的空间,并加上 10 个整型的空间,这里我想问小伙伴们一个问题,此时如果我们用 sizeof 求结构体的大小它会是多大呢?

在 Linux 平台环境下我们也做了一个测试:

这里有小伙伴就纳闷了,我们明明已经给结构体里数组分配空间了啊,但是为何只占 4 个字节呢?

其实在定义这个结构体的时候,已经确定不包含柔性数组的内存大小了,柔性数组可以理解不占结构体的内存,只是说我们在使用柔性数组时需要把它当作结构体的一个成员!

没完全理解没关系,接下来我们用图解让小伙伴们可以更直观的理解柔性数组:

在看下面一张图片之前,我们得先了解 malloc 函数开辟的空间是在堆区开辟的,堆区是先使用低地址后使用高地址(栈区相反),数组的下标是随着地址的增长而增长的!

通过上面的讲解,相信你们已经了解了柔性数组这个概念,实在不了解也无所谓,这个东西确实不常用。

2、union 内存级布局理解

union 关键字的用法与 struct 关键字的用法非常相似,union 并不会为每一个数据成员分配空间,在 union 中所有的数据成员共用一个空间,联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)。所有数据成员具有相同的起始地址

一个 union 只分配一个足够大的空间来容纳最大长度的数据成员,比如上面的例子,最大成员是 int,所以 union Data 大小就是 int 数据类型的大小! 而且,union 中的成员,同一时间只能存储其中一个数据成员!

上例注意:c 变量永远在 a 变量的低地址处!每一个都是第一个元素!

2.1大小端对 union 的影响:

前几期中我们了解了大小端的概念,既然联合体是共用内存的,而且联合体中每个元素都能称得上是第一个元素,我们不妨来思考如下这样一段代码:

显然打印小端的结果我们并不意外,但是这段代码内存中的布局是怎么样的呢?这跟大小端存储又有什么关系呢?

这里我们来回忆一下大小端存储的概念:

  • 大端:按照字节为单位,低权值位数据存储在高地址处;
  • 小端:按照字节为单位,低权值位数据存储再低地址处;

这里也就验证了我们之前的一个结论:在 union 中里面每个成员都是第一个元素!

2.2 内存对齐的概念:

可能有的小伙伴写过 union 包括 struct 的代码,在求这些自定义类型大小的时候, 可能求出的大小并不是我们想的一样,这就要考虑到我们的内存对齐了,这里我们不细讲,只是一笔带过,小伙伴感兴趣可以自行下来了解!

当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。

对齐数 = 编译器默认的一个对齐数 该成员大小的较小值

VS 中默认的值为 8


相关文章
|
6月前
|
存储 程序员 C语言
C语言关键字是什么?什么是关键字?什么是字符和ascll码值
C语言关键字是什么?什么是关键字?什么是字符和ascll码值
|
4月前
|
C语言
|
4月前
|
存储 C语言
C语言中static关键字的作用与用法解析
C语言中static关键字的作用与用法解析
|
5月前
|
存储 C语言
C语言中的typedef关键字:为类型定义新名称
C语言中的typedef关键字:为类型定义新名称
|
5月前
|
C语言
深入探索C语言中的sizeof关键字
深入探索C语言中的sizeof关键字
|
5月前
|
存储 编译器 C语言
C语言中的关键字与标识符详解
C语言中的关键字与标识符详解
|
6月前
|
算法 编译器 API
C语言易混淆、简单算法、结构体题目练习、常见关键字总结-1
C语言易混淆、简单算法、结构体题目练习、常见关键字总结
|
6月前
|
安全 编译器 C语言
C语言中的const关键字
C语言中的const关键字
50 2
|
6月前
|
存储 C语言
【C语言】数据:数据类型关键字
【C语言】数据:数据类型关键字
|
6月前
|
存储 编译器 C语言
c语言中static关键字的作用
c语言中static关键字的作用