结构体(内存对齐)和共用体—C语言

来源:互联网 发布:淘宝保证金有什么用 编辑:程序博客网 时间:2024/06/06 02:27

结构体

C语言学到现在,相信大家已经熟知了基本类型(整型、实型、字符型)的变量和一种构造类型数据(数组),但是只有这些数据类型是不够的,因此我们接下来介绍C语言中可以将不同类型的定义自己的数据类型——结构体。

结构体与数组的比较

由于结构体和数组有很大的类似之处,所以我们首先来说说结构体与数组异同:

  1. 都由多个元素组成
  2. 各个元素在内存中的存储空间是连续的
  3. 数组中各个元素的数据类型相同,而结构体中的各个元素的数据类型可以不相同

结构体的定义和使用

1. 结构体的定义语法:

struct 结构体名{    类型名1 成员名1;    类型名2 成员名2;    ...    类型名n 成员名n;};

刚开始学的童鞋一定要注意,结构体是一种数据类型而不是一个变量,既然是一种数据类型,我们下来就讲讲定义结构体类型变量的几种方法。

2. 定义结构体类型变量的几种方法:

  • 定义结构体类型时,同时定义该类型的变量
struct [student] /* [ ]表示结构体名是可选的 */{    char name[10];    char sex;    int age;    float score;}stu1, *ps, stu[5]; /* 定义结构体类型的普通变量、指针变量和数组 */
  • 先定义结构体类型,再定义该类型的变量
struct student{    char name[10];    char sex;    int age;    float score;};struct student stu1, *ps, stu[5]; /* 定义结构体类型的普通变量、指针变量和数组 */
  • 用类型定义符typedef先给结构体类型命别名,再用别名定义变量
typedef struct [student]{    char name[10];    char sex;    int age;    float score;}STU;STU stu1, *ps, stu[5]; /* 用别名定义结构体类型的普通变量、指针变量和数组 */

再次给初学的童鞋说明一下:

  1. 类型和变量不要混淆,只能对变量赋值、存取或运算,并且在编译时,对类型是不分配空间的,只对变量分配空间。
  2. 结构体的成员也可以是一个结构体变量
  3. 结构体中的成员名可以与程序中的变量名相同,二者不代表同一对象。(如变量num和struct student中的num是两回事)

3. 结构数组和结构指针:

  1. 结构数组:该数组的每一个元素都是相同的结构体类型
  2. 结构指针:是指向结构的指针。由一个加在结构变量名前的"*" 操作符来定义

4. 结构体变量成员的引用:

在定义了结构体变量以后,当然可以引用这个结构体,但不能将一个结构体变量作为一个整体进行操作,只能对结构体变量中的某个成员进行操作,引用方式如下:

  1. 结构体变量名. 成员名: stu1.name
  2. 结构体指针变量->成员名: ps->name
  3. (*结构体指针变量). 成员名: (*ps).name
  4. 结构体变量数组名. 成员名: stu[0].name

5. 结构体变量的初始化

和其他类型一样,对结构体变量可以在定义时指定初始值:

struct [student]{    char name[10];    char sex;    int age;    float score;}stu[2]={{"SAN", 'F', 22, 90.5}, {"GI", 'M', 20, 88.5}}; 

结构体内存对齐问题

C语言结构体对齐也是老生常谈的话题了,也算是结构体中的一个小难点,内容虽然很基础,但一不小心就会弄错。
内存对齐的规则很简单

  1. 起始地址为该变量类型所占内存的整数倍,若不足则不足部分用数据填充至所占内存的整数倍。
  2. 该结构体所占总内存为结构体成员变量中最大数据类型的整数倍。

接下来我们看一个例子:

struct str1    {        char a;        int b;        float c;        double d;    };

str1这个结构体占用的内存是多少呢?如果用变量类型直接想加,得到的结果是17,但显然不是这样的。这个程序运行的正确结果是24。为什么呢?

分析如下
char型变量占一个字节,所以它的起始地址为0,
而int类型占4个字节,它的起始地址应该是4(的整数倍),那么内存地址1、2、3就需要被填充。
float占用4个字节,而结构体中a,b两个成员变量占了0到7内存地址,c的地址从8开始,符合规则一,占用内存地址为8到11。
double类型占8个字节,所以d的起始地址就应该从16开始,那么12、13、14、15内存地址就需要被填充。d从16地址开始,占用8个字节。
整个结构体占用字节数为24,符合规则二。内存分配如图:红色区域为填充部分

--


总之结构体对齐可以总结为三个基本原则:

  1. 数据成员对齐规则:结构体的数据成员中,第一个成员从offset为0的地址开始,以后每一个成员存储的起始位置为该成员大小的整数倍
  2. 结构体作为成员: 如果一个结构体1作为另一个结构体2的数据成员,则在结构体2中结构体1要从1内部成员最大的整数倍地址开始存储。
  3. 结构体的总大小(sizeof): 为该结构体内部最大基本类型的整数倍,不足的要补齐,而不是简单的所有成员的大小总和。

再举一个例子:

struct str2    {        double a;        int b;        char c;        double d;    };

str2这个结构体占用的内存空间是多少呢?是24!

分析如下
首先double类型的a占用内存地址为0~7
int类型的b起始地址为8,符合规则一,占用地址为8~11
char类型的c占一个字节,地址为12
那么double类型的d,起始地址为13吗?显然不是,满足规则一的地址是16,所以d起始地址为16,占用16到23。
结构体总共24个字节,满足规则二。

但是如果这个结构体最后再加一个成员变量 char e,那这个结构体占用的内存是多少?
分析:
char类型的e起始地址为24,占用地址为24,但是结构体一共有25个字节,就不满足规则二了,怎么办呢?
为了满足规则二,我们将25~31进行填充,因此整个结构体占用32个字节。

共用体

C语言中还有一种将不同类型的几种变量存放到同一段内存单元中。这几个不同的变量共同占用同一段内存结构的数据类型———共用体

  1. 共用体类型变量的定义
union   共用体名{成员列表;}变量列表;
  1. 共用体变量的引用 和结构体变量一样,共用体变量也不能被直接引用,而只能引用共用体变量中的成员。
  2. 共用体同一个内存段可以用来存放几种不同类型的成员,但是在每一瞬间只能存放其中的一种,而不是同时存放几种。换句话说,每一瞬间只有一个成员起作用,其他的成员不起作用,即不是同时都在存在和起作用。
  3. 共用体变量的地址和它的各个成员的地址都是同样的
  4. 共用体变量中起作用的成员是最后一次被赋值的成员。
  5. 共用体类型可以出现在结构体类型的定义中,也可以定义共用体数组。反之,结构体也可以出现在共用体类型的定义中,数组也可以作为共用体的成员。

结构体与共用体并不难,结构体更加重要一些,用的也多一点,尤其要格外掌握结构体的内存对齐问题

阅读全文
0 0
原创粉丝点击