目录

文章目录

  • 目录
  • 前文列表
  • 结构体
    • 定义结构体
    • 初始化结构体变量
    • 访问结构体成员
    • 结构体的内存分布
    • 将结构体作为实参传入函数
    • 指向结构体变量的指针
  • 位域
    • 定义位域
    • 使用位域结构体的成员

前文列表

《程序编译流程与 GCC 编译器》
《C 语言编程 — 基本语法》
《C 语言编程 — 基本数据类型》
《C 语言编程 — 变量与常量》
《C 语言编程 — 运算符》
《C 语言编程 — 逻辑控制语句》
《C 语言编程 — 函数》
《C 语言编程 — 高级数据类型 — 指针》
《C 语言编程 — 高级数据类型 — 数组》
《C 语言编程 — 高级数据类型 — 字符串》
《C 语言编程 — 高级数据类型 — 枚举》

结构体

我们知道数组(Array)是一组具有相同数据类型的数据集合。而在实际的编程中,我们往往还需要一组具有不同数据类型的数据集合,例如:学生信息的登记表,其中包含类型为字符串的 “姓名”,为整数的 “学号” 以及 “年龄”,和为小数的 “”成绩。数组类型显然无法满足这一需求,此时可以使用结构体(Struct)来存放一组不同类型的数据

C 语言中,像 int、float、char 等是由 C 语言本身提供的数据类型,不能再进行分拆,我们称之为基本数据类型。而结构体则是一种复杂数据类型(构造数据类型),它由程序员自己定义,由一系列具有相同或不同数据类型的变量组成。我们可以使用结构体表示更加复杂的数据类型。

注意:我们应该将结构体定义在所有需要用到它的函数的上方,枚举类型和基本数据类型的使用方法没有任何区别。

定义结构体

使用 struct 关键字声明一个结构体数据类型:

struct 结构体名 {结构体所包含的变量成员
};

使用 struct 关键字声明并定义一个结构体数据类型:

struct tag { membermembermember...
} variable-list ;
  • tag 是结构体的标识(名字)。
  • member 是几结构体的成员,为标准的变量定义语句,比如:int i
  • variable-list 结构体变量,可以一次性指定一个或多个结构体变量。

e.g. 定义一个 book 结构体变量:

struct Books {char  title[50];char  author[50];char  subject[100];int   book_id;
} book;

声明 只在编译阶段有意义,使得编译器知道这样的一个 “名称”,让代码得以完成编译;而 定义 是为了创建一个实际的 “对象”,即分配具体的内存空间,在程序运行的期间有意义。我们经常会顺口溜的说:声明类型、定义变量,从而避免概念上的混淆。

可见,结构体就像一个 “模板”,定义出来的变量都具有相同的性质,可以使用结构体来实现 C++ 中的类和实例的继承机制。

结构体的定义具有多种方式,比较灵活。通常的,tag、member、variable-list 这 3 部分至少要出现 2 个:

  1. 直接定义:在声明结构体类型的同时定义结构体变量,如果后面不再需要定义其他新的结构体变量,那么我们可以在定义时不给出结构体类型名称。这种方式书写简单,但是因为没有结构体类型名称,所以后面就没法用该结构体类型定义新的变量。
struct {int a;char b;double c;
} s1;
  1. 间接定义:先声明定义结构体类型,再另外的定义结构体变量。
// 声明
struct SIMPLE
{int a;char b;double c;
};// 定义
struct SIMPLE t1, t2[20], *t3;

注意,在上述的两个示例中,第一个和第二声明被编译器当作两个完全不同的类型,即使他们的成员列表是一样的。

  1. 别名(无名)定义:我们可以同时使用关键字 struct 和 typedef 来声明一个结构体类型,并未其赋予一个 “别名”,后续使用该 “别名” 定义结构体变量时,就可以不重复的书写 struct 关键字了。
typedef struct
{int a;char b;double c;
} Simple2;// 现在可以用 Simple2 作为类型,声明新的结构体变量
Simple2 u1, u2[20], *u3;

NOTE:在实际的编程中,我们经常会使用 *_s 后缀来命名一个结构体类型,而是用 *_t 后缀来命名一个结构体类型的别名(也称之为自定义一个私有的数据类型)。e.g.

typedef struct student_t {char   *name;int       num;int     age;char    group;float score;
} student_sstudent_s stu1;
sizeof(student_s);

结构体的成员可以是其他结构体,也可以包含指向自己结构体类型的指针,这种指针的应用通常是为了实现一些更高级的数据结构如链表和树等。

struct COMPLEX {char string[100];struct SIMPLE a;
};struct NODE {char string[100];struct NODE *next_node;
};

如果两个结构体互相包含,则需要对其中一个结构体进行声明,同时还要注意语句的顺序,否则会出现编译错误:

struct B;struct A{struct B *partner;//other members;
};struct B {struct A *partner;//other members;
};

初始化结构体变量

下述是初始化结构体变量的一种写法(整体赋值,仅限于定义结构体变量的时候):

#include <stdio.h>/*** 声明 Books 结构体类型;* 定义 book 结构体变量;* 初始化 book 结构体变量;*/
struct Books {char    title[50];char    author[50];char    subject[100];int     id;
} book = {"is book", "fanguiju", "C", 123};int main() {printf("Book's title: %s\nauthor: %s\nsubject: %s\nid: %d\n", book.title, book.author, book.subject, book.id);return 0;
}

访问结构体成员

下述是初始化结构体变量的一种写法(分别赋值,在使用过程中只能对成员逐一赋值),并使用成员访问运算符 . 对结构体的成员进行访问:

#include <stdio.h>
#include <string.h>/* 声明 Books 结构体类型 */
struct Books {char    title[50];char    author[50];char    subject[100];int     id;
};int main() {/* 定义 book 结构体变量 */struct Books book1;/* 初始化 book 结构体变量 */strcpy(book1.title, "C Programming");strcpy(book1.author, "Nuha Ali");strcpy(book1.subject, "C Programming Tutorial");book1.id = 123;/* 访问结构体成员 */printf("Book's title: %s\nauthor: %s\nsubject: %s\nid: %d\n", book1.title, book1.author, book1.subject, book1.id);return 0;
}

结构体的内存分布

理论上讲,结构体的各个成员在内存空间中是连续存储的,和数组非常类似,比如上面的结构体变量 stu1 的内存分布如下图所示,共占用 4 + 4 + 4 + 1 + 4 = 17 个字节。

但是在编译器的具体实现中,各个成员之间可能会存在缝隙,对于 stu1 的成员变量 group 和 score 之间就可能存在 3 个字节的空白填充。这样算来,stu1 其实占用了 17 + 3 = 20 个字节。

将结构体作为实参传入函数

#include <stdio.h>
#include <string.h>struct Books {char    title[50];char    author[50];char    subject[100];int     id;
};void printBook(struct Books book) {printf("Book's title: %s\nauthor: %s\nsubject: %s\nid: %d\n", book.title, book.author, book.subject, book.id);
}
int main() {struct Books book1;strcpy(book1.title, "C Programming");strcpy(book1.author, "Nuha Ali");strcpy(book1.subject, "C Programming Tutorial");book1.id = 123;printBook(book1);return 0;
}

指向结构体变量的指针

定义基类为结构体 Books 的指针类型变量:

struct Books *struct_pointer;

现在,就可以在上述定义的指针变量中存储结构体变量的内存地址了:

struct_pointer = &book1;

在使用指向该结构体变量的指针访问结构体成员时,必须使用 -> 运算符,如下所示:

struct_pointer->title;

因为结构体指针变量 struct_pointer 本质是一个内存地址,跟结构体变量不同,不可以直接使用成员访问运算符 .,而是使用 -> 运算符。

#include <stdio.h>
#include <string.h>struct Books {char    title[50];char    author[50];char    subject[100];int     id;
};void printBook(struct Books *book) {printf("Book's title: %s\nauthor: %s\nsubject: %s\nid: %d\n", book->title, book->author, book->subject, book->id);
}
int main() {struct Books book1;strcpy(book1.title, "C Programming");strcpy(book1.author, "Nuha Ali");strcpy(book1.subject, "C Programming Tutorial");book1.id = 123;printBook(&book1);return 0;
}

位域

在某些场景中,需要存储的数据值并不需要占用一个完整的字节(Byte),而只需占几个或一个二进制位(Bit)。例如:存放一个开关量,只有 0 和 1 两种状态,使用到 1 位二进位即可。

为了节省存储空间,并使处理简便,C 语言又提供了一种数据结构 —— 位域。

所谓 “位域” 就是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数以及标识(域名),允许在程序中按域名进行操作。这样就可以把几个不同的变量用同一个字节总的不同二进制位域来表示。

定义位域

位域的定义与结构体定义类型,本质是一种特殊的结构体:

struct 位域结构体名 {类型说明符 [位域名]: 位域长度(Bit)...
};

e.g.

struct bs {int a:8;int b:2;int c:6;
} data;

上例位域结构体变量 data 占用了 2 个字段(16 位)。

  • 一个位域存储在同一个字节(单元)中,当一个字节所剩的空间不够存放另一位域时,则会从下一单元起存放该位域。也可以有意使某位域从下一单元开始:
struct bs {unsigned a:4;unsigned  :4;    /* 空域,填 0 表示不使用 */unsigned b:4;    /* 刻意从下一单元开始存放 */unsigned c:4
}
  • 由于位域不允许跨字节,因此位域的长度不能大于一个字节的长度(8 Bit)。如果最大长度大于计算机的整数字长,一些编译器可能会允许域的内存重叠,另外一些编译器可能会把大于一个域的部分存储在下一个字中。视乎于编译器的实现,这也是 C 语言的特点之一。

  • 位域可以是无名位域,这时它只用来作填充或调整位置。无名的位域是不能使用的。如上例子中的空域。

使用位域结构体的成员

#include <stdio.h>int main() {struct BS {unsigned a:1;unsigned b:3;unsigned c:4;};struct BS bit;struct BS *pbit;bit.a = 1;    /* 给位域赋整型值 1,数值没有超过位域 a 的 1bit */bit.b = 7;    /* 给位域赋整型值 7,数值没有超过位域 b 的 3bit */bit.c = 15;   /* 给位域赋整型值 15,数值没有超过位域 c 的 4bit */printf("%d, %d, %d\n", bit.a, bit.b, bit.c);pbit = &bit;   /* 把位域结构体变量 bit 的地址赋给位域结构体指针变量 pbit */pbit->a = 0;   /* 结构体变量访问结构体成员 */pbit->b &= 3;  /* 与赋值运算 */pbit->c |= 1;  /* 或赋值运算 */printf("%d, %d, %d\n", pbit->a, pbit->b, pbit->c);return 0;
}

运行:

$ ./main
1, 7, 15
0, 3, 15

C 语言编程 — 高级数据类型 — 结构体与位域相关推荐

  1. Go 语言编程 — 高级数据类型 — 结构体

    目录 文章目录 目录 结构体 访问结构体成员 向函数传递结构体 结构体指针 结构体标签(Struct Tag) 结构体 Golang 中,结构体是由一系列具有相同类型或不同类型的数据构成的数据集合.与 ...

  2. C 语言编程 — 高级数据类型 — 共用体

    目录 文章目录 目录 前文列表 共用体 定义共用体 访问共用体成员 前文列表 <程序编译流程与 GCC 编译器> <C 语言编程 - 基本语法> <C 语言编程 - 基本 ...

  3. C 语言编程 — 高级数据类型 — void 类型

    目录 文章目录 目录 前文列表 void 类型 前文列表 <程序编译流程与 GCC 编译器> <C 语言编程 - 基本语法> <C 语言编程 - 基本数据类型> & ...

  4. C 语言编程 — 高级数据类型 — 字符串

    目录 文章目录 目录 前文列表 字符串 字符串拷贝 字符串比较 strcmp strncmp 前文列表 <程序编译流程与 GCC 编译器> <C 语言编程 - 基本语法> &l ...

  5. C 语言编程 — 高级数据类型 — 枚举

    目录 文章目录 目录 前文列表 声明枚举类型 定义枚举类型的变量 枚举类型变量的枚举值 枚举在 switch 语句中的使用 将整型转换为枚举类型 前文列表 <程序编译流程与 GCC 编译器> ...

  6. C 语言编程 — 高级数据类型 — 数组

    目录 文章目录 目录 前文列表 数组 声明数组 初始化数据 访问数组元素 二维数组 指向数组的指针 将数组指针作为实参传入函数 从函数返回一个数组指针 指针数组 数组名和取数组首地址的区别 前文列表 ...

  7. C 语言编程 — 高级数据类型 — 指针

    目录 文章目录 目录 前文列表 指针 声明一个指针变量 使用指针 空指针 悬空指针 野指针 指针的算术运算 指向指针的指针 将指针作为实际参数传入函数 从函数返回指针 一个古老的笑话 前文列表 < ...

  8. Go 语言编程 — 高级数据类型 — Interface、多态、Duck Typing 与泛式编程

    目录 文章目录 目录 Golang 的接口 Interface 实例存储的是实现者的值 如何判断某个 Interface 实例的实际类型 Empty Interface Interface 与多态 I ...

  9. Go 语言编程 — 高级数据类型 — 数组

    目录 文章目录 目录 数组 定义数组 数组与切片的区别 访问数组元素 多维数组 初始化二维数组 访问二维数组 向函数传递数组 指针数组 数组 数组是具有相同数据类型的一组已编号且长度固定的数据项序列, ...

最新文章

  1. vue-cli 3.0 源码分析
  2. Java并发编程:ThreadLocal
  3. oracle 数据库 字符串函数
  4. python输入圆的半径公式_[图文]铁路曲线正矢的计算公式
  5. 课程 2B: 制作一款交互性应用
  6. 还在担心快应用没流量?全场景新玩法来袭!
  7. matlab simout,每日学习Matlab(2)
  8. 理光打印机如何设置邮件服务器,技术员帮您win10系统网络共享理光打印机的设置方法的操作办法...
  9. 2D Tookit (一) 精灵切割
  10. 计算机会计学ufo报表,《计算机会计学》教学大纲.pdf
  11. DAS-DVS分布式光纤振动传感-发展现状与未来趋势分析(华为光纤传感新产品Huawei OptiXsense EF3000)
  12. docker 启动服务 Up Less than a second ago
  13. 单片机c语言小车转圈,用51单片机编写的智能小车全程前进的C程序
  14. 中国互联网25年:成也资本,败也资本!
  15. css 文本、文字展开与收缩,查看更多收起
  16. loadrunner入门教程(17) --关联
  17. unity3d录音播放
  18. 正在试用的免费云服务器1
  19. 英文样式教师求职简历免费word模板
  20. 椰风,海风——海南行

热门文章

  1. 三分钟破解无线网——无线网络安全攻防
  2. ms-sql是mysql吗_mssql和mysql有哪些区别?
  3. html标签里写js,JS事件(HTML标签内)
  4. c语言指针和结构体难点,C语言指针和结构体
  5. bootstrapr表格父子框_使用Bootstrap Table建立父子表
  6. 在世界第二届半机械人奥运会上,瘫痪飞行员在Cybathlon BCI竞赛中争夺金牌
  7. 动态删除nod linux_Linux文件操作实用笔记
  8. 苹果用户可以自修手机了!原厂零件工具都能买,网友:iScrew螺丝刀600多块?...
  9. 用无人车硬件玩GTA 5,这个12岁孩子的外挂有点硬核
  10. 潘建伟团队最新研究成果登上Nature:首次实现1120公里长距离无中继纠缠量子密钥分发...