C 语言编程 — 高级数据类型 — 结构体与位域
目录
前文列表
《程序编译流程与 GCC 编译器》
《C 语言编程 — 基本语法》
《C 语言编程 — 基本数据类型》
《C 语言编程 — 变量与常量》
《C 语言编程 — 运算符》
《C 语言编程 — 逻辑控制语句》
《C 语言编程 — 函数》
《C 语言编程 — 高级数据类型 — 指针》
《C 语言编程 — 高级数据类型 — 数组》
《C 语言编程 — 高级数据类型 — 字符串》
《C 语言编程 — 高级数据类型 — 枚举》
结构体
结构体用来自定义一个新的数据类型。与枚举类型的枚举值都是整型不同,结构体由一系列具有相同或不同数据类型的变量组成。我们可以使用结构体表示更加复杂的数据类型。
注意,我们应该将结构体放在所有用到它的函数的上方。这个类型和内建的基本数据类型的用法没有任何区别。
定义结构体
使用 struct 关键字来类型结构体数据类型:
struct tag {
member
member
member
...
} variable;
- tag 是结构体的标识(名字)。
- member 是几结构体的成员,为标准的变量定义语句,比如:
int i
。 - variable-list 结构体变量,可以一次性指定一个或多个结构体变量。
e.g.
struct Books {
char title[50];
char author[50];
char subject[100];
int book_id;
} book;
结构体的定义有多种方式,比较灵活。通常的,tag、member、variable-list 这 3 部分至少要出现 2 个:
- 直接创建:在声明定义结构体类型的同时创建结构体变量
struct {
int a;
char b;
double c;
} s1;
- 间接创建:先声明定义结构体类型,再另外创建结构体变量
struct SIMPLE
{
int a;
char b;
double c;
};
struct SIMPLE t1, t2[20], *t3;
注意,在上面的声明中,第一个和第二声明被编译器当作两个完全不同的类型,即使他们的成员列表是一样的。
- 无名创建:当使用直接创建方式时,可以省略结构体类型名,所以称为无名创建,我们可以同时使用
struct
和typedef
来声明一个无名结构体。
typedef struct
{
int a;
char b;
double c;
} Simple2;
// 现在可以用 Simple2 作为类型,声明新的结构体变量
Simple2 u1, u2[20], *u3;
结构体的成员可以是其他结构体,也可以包含指向自己结构体类型的指针,这种指针的应用通常是为了实现一些更高级的数据结构如链表和树等。
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>
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>
struct Books {
char title[50];
char author[50];
char subject[100];
int 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;
printf("Book's title: %s\nauthor: %s\nsubject: %s\nid: %d\n", book1.title, book1.author, book1.subject, book1.id);
return 0;
}
将结构体作为实参传入函数
#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 语言编程 — 高级数据类型 — 结构体与位域的更多相关文章
- Golang面向对象编程-struct(结构体)
Golang面向对象编程-struct(结构体) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.什么是面向对象编程 面向对象编程(Object Oriented Program ...
- OpenGL ES着色器语言之语句和结构体(官方文档第六章)内建变量(官方文档第七、八章)
OpenGL ES着色器语言之语句和结构体(官方文档第六章) OpenGL ES着色器语言的程序块基本构成如下: 语句和声明 函数定义 选择(if-else) 迭代(for, while, do-wh ...
- Android For JNI(五)——C语言多级指针,结构体,联合体,枚举,自定义类型
Android For JNI(五)--C语言多级指针,结构体,联合体,枚举,自定义类型 我们的C已经渐渐的步入正轨了,基础过去之后,就是我们的NDK和JNI实战了 一.多级指针 指针的概念我们在前面 ...
- C结构体之位域(位段)
C结构体之位域(位段) 有些信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位.例如在存放一个开关量时,只有0和1 两种状态, 用一位二进位即可.为了节省存储空间,并使处理简便,C ...
- [转]C结构体之位域(位段)
有些信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位.例如在存放一个开关量时,只有0和1 两种状态, 用一位二进位即可.为了节省存储空间,并使处理简便,C语言又提供了一种数据结构 ...
- C结构体中位域
C结构体中位域的一点小知识,最近在看资料时遇到结构体中的一种特殊操作——位域,在下面的程序中我将根据自己的理解进行简单介绍.程序只是为了了解位域的一些性质,运行时有些编译器可能会报错. 程序代码如下( ...
- C/C++编程笔记:C语言对齐问题【结构体、栈内存以及位域对齐】
引言 考虑下面的结构体定义: 假设这个结构体的成员在内存中是紧凑排列的,且c1的起始地址是0,则s的地址就是1,c2的地址是3,i的地址是4. 现在,我们编写一个简单的程序: 运行后输出: 为什么会这 ...
- Go语言基础之8--面向对象编程1之结构体(struct)
一.结构体详解 1.1 声明和定义 1.Go中面向对象是通过struct来实现的, struct是用户自定义的类型 2.Go 语言中数组可以存储同一类型的数据,但在结构体中我们可以为不同项定义不同的数 ...
- C语言入门第十章----结构体
C语言结构体从本质上讲是一种自定义的数据类型,只不过这种数据类型比较复杂,是由int.char .float等基本类型组成的,你可以认为结构体是一种聚合类型. 在实际开发中,我们可以将一组类型不同的. ...
- C语言博客作业-结构体
一.PTA实验作业 6-2 按等级统计学生成绩 1. 本题PTA提交列表 2. 设计思路 定义i,count存放不及格人数 for i=0 to n-1{ 判断 score的值的范围 if 100&g ...
随机推荐
- Codeforces Round #726 (Div. 2)
CF1537A Arithmetic Array 洛谷传送门 CF1537A 分析 用这 \(n\) 个数的总和 \(sum\) 判断: 如果 \(sum<n\) 直接用 \(n-sum+1\) ...
- vue3探索——使用ref与$parent实现父子组件间通信
在vue3中,可以使用vue3的API defineExpose()函数结合ref或者$parent,实现父子组件数据的传递. 子组件向父组件传递数据defineExpose()和ref 子组件:通过 ...
- HDC技术分论坛:ArkCompiler(方舟编译器)原理解析
作者:xianyuqiang 编译器首席架构师 ArkCompiler(方舟编译器)是组件化.可配置的多语言编译和运行平台,它既能支撑单一语言运行环境,也能支撑多种语言组合的运行环境.它目前主要支持的 ...
- 动态规划(三)——线性dp
一.概念 具有线性阶段划分的动态规划算法叫作线性动态规划(简称线性DP).若状态包含多个维度,则每个维度都是线性划分的阶段,也属于线性DP,如下图所示: 二.线性dp的三大经典例题 1.LIS问题:求 ...
- spark 计算前后两条记录之间的差(diff),时间差等
有时候会遇到这样的场景:有一个datafram,我们需要计算同一组对象中,前后两条记录之间的差值,此处并不仅限于时间,还可以是其他的数据类型 需要用到两个工具:spark窗口函数Window对对象分组 ...
- Pytorch-实战之对Himmelblau函数的优化
1.Himmelblau函数 Himmelblau函数: F(x,y)=(x²+y-11)²+(x+y²-7)²:具体优化的是,寻找一个最合适的坐标(x,y)使得F(x,y)的值最小. 函数的具体图像 ...
- 对象数组(java)
如果程序需要某个类的若干个对象,例如Student类的10个对象,显然如下声明10个Student对象是不可取的: Student stul, stu2, stu3, stu4, stu5, stu6 ...
- CF1995E Long Inversions(贪心 + 差分)
见代码 点击查看代码 //背景:夸一下自己,CF1955A~E均是自己想出来的,这道题思路是自己想的,唯一没想到用差分数组维护 //原理:贪心(想最左边的1开始,不可能对其左边包括自己操作了,否则又要 ...
- 力扣451(java)-根据字符出现频率排序(中等)
题目: 给定一个字符串 s ,根据字符出现的 频率 对其进行 降序排序 .一个字符出现的 频率 是它出现在字符串中的次数. 返回 已排序的字符串 .如果有多个答案,返回其中任何一个. 示例 1: 输入 ...
- 揭秘sealer背后实现整个集群一键交付的奥秘 | 龙蜥技术
简介:解读集群镜像"开箱即用"神器--sealer! 编者按:集群镜像把整个集群看成一台服务器,把 k8s 看成云操作系统,实现整个集群的镜像化打包和交付,为企业级软件提供一种& ...