当前位置: 首页 > news >正文

C语言——结构体、共用体、枚举、位运算

C语言——结构体、共用体、枚举、位运算

  • 结构体
  • 共用体
  • 枚举
  • 位运算

结构体

如果将复杂的复杂的数据类型组织成一个组合项,在一个组合项中包含若干个类型不同(当然也可以相同)的数据项。 C语言允许用户自己指定这样一种数据结构,它称为结构体。

结构体的语法定义

struct 结构体名
{
成员列表
};

其中struct关键字表示在构造一个结构体类型,结构体名用来该结构体这个类型的名称,成员列表表示要描述的复杂数据中用到的具体的成员变量,成员列表的定义方式和普通变量的定义方式相同。例如下面定义一个student结构体类型:

struct student
{
char name[20];
int sno;
int age;
char sex[10];
float score;
};

在studen这个结构体中包含了姓名name、学号sno、年龄age;、性别sex[10];分数score;这些数据类型该结构体可以用来描述一个学生的基本信息。注意在结束一个结构体的定义时要在右括号“}”后面加上一个括号。

上述只是结构体的一种定义变量,还有其余两中结构体的定义方式:
1、

struct student
{
char name[20];
int sno;
int age;
char sex[10];
float score;
}s;

这里在定义结构体的同时也定义了一个结构体这种数据类型的变量s,这样写就可以直接使用该变量了。

2、

struct
{
char name[20];
int sno;
int age;
char sex[10];
float score;
}s;

这里在定义结构体类型的同时也定义了变量,可以省略结构体名,这种定义方式表示该结构体类型只能使用一次。

结构体的初始化
结构体初始化:
结构体的初始化也是采用初始化器去对结构体进行初始化,
struct student s = { “tom”, 1, 18, “man”, 99 };
结构体初始化的规则:
1、看每个成员变量,具体是什么数据类型。
2、根据各个成员变量自身的数据类型进行初始化。
3、初始化的顺序要按照定义的顺序依次进行初始化。

其实还可以在定义结构体的同时定义变量然后进行初始化:
struct student
{
char name[20];
int sno;
int age;
char sex[10];
float score;
}s = { “tom”, 1, 18, “man”, 99 };

结构体的成员变量引用的方式
结构体的引用成员变量方式一共有两种一个是通过结构体变量名.成员名,另一个是结构体指针->成员名,下面以一个例子来具体说明结构体成员变量的引用方式;

#include <stdio.h>struct student
{char name[20];int age;char sex[10];float score;
};int main(int argc, const char *argv[])
{struct student s = { "tom", 18, "man", 90 };printf("name  : %s\n", s.name);printf("age   : %d\n", s.age);printf("sex   : %s\n", s.sex);printf("score : %.2f\n", s.score);return 0;
}

上述代码中定义了一个struct student的结构体类型然后在main函数定义变量的同时进行了初始化,在打印结构体的数据时采用了结构体变量名.成员名的方式。

#include <stdio.h>
#include <stdlib.h>void outputStu(struct student *s, int len)
{int i = 0;for(i = 0; i < len; ++i){printf("name  :  %s\n", (s+i)->name);printf("Sno   :  %d\n", (s+i)->Sno);printf("age   :  %d\n", (s+i)->age);printf("sex   :  %s\n", (s+i)->sex);printf("score :  %.2f\n", (s+i)->score);printf("\n");}
}int main(int argc, const char *argv[])
{struct student s[3] = { { "tom", 1, 18, "m", 99 }, { "jerry", 2, 18, "w", 90 }, { "lucy", 3, 18, "w", 92 } };outputStu(s, 3);return 0;
}

上述程序将outputStu()函数的形参设置成结构体指针去接收一个结构体指针在打印结构体成员变量时采用了结构体指针->成员名的方式。

结构体的大小

结构体的大小遵循内存对齐规则:

结构体的对齐规则: //内存地址的对齐
1.在32位的平台上,默认都是按4字节对齐的。
2.对于成员变量,
各自在自己的自然边界上对齐。
char – 1字节
short – 2字节
int – 4字节
3.如果成员变量中有比4字节大。
此时整个结构体按照4字节对齐。 //32位的平台
4.如果成员变量中没有有比4字节大。
此时整个结构体按照最大的那个成员对齐。
注意在32位的平台下:
//如果有超过4字节 ,按照4字节对齐
//如果没有超过4字节的,则按成员变量中最大对齐
在64位的平台下:
//如果超过4字节的,按超过的最大的成员变量对齐
//如果没有超过4字节的,则按成员变量中最大对齐

首先要知道的是系统读取内存当中的数据时是4个字节4个字节地读取的,这样的读取方式能提高数据的读取效率和解析效率。

下面以一些例子来说明:

#include <stdio.h>struct s
{char a;short b;int c;
};int main(void)
{struct s aa;printf("sizeof(struct s) = %ld\n", sizeof(struct s));return 0;
}

在这里插入图片描述
我所用的平台是64为的平台所以下面也就主要说明64为平台下的内存对齐规则。在上述程序定义的结构体的成员变量所占的字节总共是7个字节,其中没有超过4字节的,则按成员变量中最大对齐char a;占一个字节它可以放在能被1整除的地址编号的内存当中short b;占两个字节放在a的后面且放在首地址编号能被2整除的内存当中,int c;占四个字节它放在首地址能被4整除的内存空间当中,最终整个结构体也要对齐该结构体没有超过4字节的,则按成员变量中最大对齐也就是8个字节。

#include <stdio.h>struct s
{char a;//一字节double b;//八字节int c;//四字节
};int main(void)
{struct s aa;printf("sizeof(struct s) = %ld\n", sizeof(struct s));return 0;
}

在这里插入图片描述
在上述程序定义的结构体的成员变量所占的字节总共是13个字节,其中有超过4字节的double类型,char a;占一个字节它可以放在能被1整除的地址编号的内存当中double b;占八个字节放在a的后面且放在首地址编号能被8整除的内存当中,int c;占四个字节它放在首地址能被4整除的内存空间当中,最终整个结构体也要对齐该结构体有超过4字节的,则按成员变量中最大对齐也就是24个字节。

共用体

共用体的语法:

union 共用体名
{
成员变量;
};

语法定义例子:

union demo
{
char a;
short b;
int c;
};

共用体成员变量共用的是一块内存空间且公用的是最大成员的空间 。
在使用共用体时要注意:
1.共用体初始化时,只能给一个值,默认时给到第一个成员的。
2.共用体变量中的值,取决与最后一次给到的值,还要看能影响几个字节。

利用共用体判断当前操作系统是大端还是小端存储:

#include <stdio.h>int isLittleEndian(void)
{union s{int a;char b;}c = { 1 };return c.b;
}int main(int argc, const char *argv[])
{printf("%d\n", isLittleEndian());return 0;
}

在这里插入图片描述
上述程序的共用体在初始化时给了一个1,一位int a;char b;共用的是同一块空间它们对应的首地址也是相同的,如果当前系统为小端存储那么1在存储时低位数据就会存放在地址所以如果是小端存储1就放在高位地址,则isLittleEndian()函数返回的是1反之就返回0。

枚举

“枚举”是指将变量的值一一列举出来,变量的值只限于列举出来的值的范围内。

枚举类型的语法定义:

enum 枚举名
{
列举各种值
}
列举各种值时不需要类型名,每个值之间用逗号隔开;
例如:
enum week
{
Monday = 1,
Tuesday,
Wednesday,
Thusday,
Firday,
Saturday,
Sunday
};
上述定义了一个名为week的枚举类型,并给第一个值初始化为1;
在枚举中逐个列举的值,默认是从0开始的,如果有给定的值且后续没有给值的枚举元素就依次加1;
枚举类型的本质实际是一个int类型的数据 ;
枚举类型的变量与整型类型的变量通用的;比如说下面我将Monday以%d的形式打印可以看到程序无警告报错且输出结果为1;

#include <stdio.h>enum week
{Monday = 1,Tuesday,Wednesday,Thusday,Firday,Saturday,Sunday
};int main(int argc, const char *argv[])
{printf("Monday = %d\n", Monday);return 0;
}

在这里插入图片描述

枚举类型与宏定义的对比:
1、二者的使用阶段不同,宏定义是在预处理阶段使用完毕, 而枚举在编译阶段时要检查语法并且在运行阶段参与代码的运行 ;
2、在可读性方面,二者都提高了可读性但是枚举更能说明有相关性的一些值间的关系;

typedef重定义类型

typedef重定义就是给类型起别名,使用typedef重定义以后在定义对应类型的变量时可以使用该别名来代表该类型,使用typedef重定义类型有一个十分方便的用处,下面举例子说明:
signal()的函数原型:
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
这里使用了typedef重定义了void (*)(int)函数类型,此时sighandler_t和void (*)(int)是等价的;
如果不把void (*)函数的类型重定义则该函数的原本的写法为:
void (*)(int) signal(int signum, void (*handler)(int) handler);
–>void (*signal(int signum, void (*handler)(int) ))(int);
这样的写法十分地不便于代码的阅读,如果使用重定义的写法则在代码的可读性方面得到了大幅度的提升。

位运算

位运算是C语言的特点,位运算是指进行二进制位的运算。在系统软件中,常要处理二进制位的问题。

位运算符的分类:
在这里插入图片描述

"按位与”运算符(&)
"按位与”运算符(&)的运算规则是一假则假,同真才真例如:
00000011
(&) 00000101
00000001
在上述例子看到当0和1做&运算时结果为0,1和1做运算&时结果为1;
&运算常用做清零操作:
在这里插入图片描述
上述程序就是把a中的值的偶数位置零。

按位或 运算符(|)

按位或 运算符(|)的运算规则是一真则真,比如:
00110000
(I) 00001111
00111111
同过上述例子可以知道,只有出现1运算结果就为1;
在实际应用中按位或运算常用做置一的操作,例如:

#include <stdio.h>int main(void)
{int a = 0;int i = 0;printf("%#x\n", a);for(i = 0; i < 32; ++i){if(i % 2 != 0){a = a | (1 << i);}}printf("a = %#x\n", a);return 0;
}

上述程序通过判断当前位是不是奇数位如果是就让1左移i位然后再和a做|运算,实现了把a的奇数位置一的效果。

"异或”运算符(^)
异或运算的运算规则为:若参加运算的两个二进制位同号,则结果为 0( 假);异号则为 1( 真)。即 0^0=0,0^1=1, 1^0=1, 1^1=0

异或运算可以实现两个数的交换:
a = a ^ b;
b = a ^ b;
a = a ^ b;

a = a ^ b; 将 a 和 b 的值进行异或运算,结果存储在 a 中。此时 a 存储了 a 和 b 的异或结果。
b = a ^ b; 将上一步的结果(存储在 a 中)与 b 进行异或运算,结果存储在 b 中。由于 a 现在存储的是 a 和 b 的异或结果,这一步实际上将 a 的原始值赋给了 b。
a = a ^ b; 再次将 a 和 b(现在 b 存储的是 a 的原始值)进行异或运算,结果存储在 a 中。由于 a 现在存储的是 a 和 b 的异或结果,而 b 存储的是 a 的原始值,所以这一步实际上将 b 的原始值赋给了 a。

<< 左移

写法:
a<<n,表示将a这个数据左移n位;
例如:
0000 0001
左移1位相当于乘 2
0000 0001
0000 0010
在左移时左移一位最低位补零;

>> 右移

a>>n,表示将 a这个数据右移n位,右移1位相当于除 2;
在进行右移时最低位补0还是1呢?
对于算术右移补0还是1要看符号位和数据类型:
如果是有符号类型的数据右移时最高位补的是符号位 ,如果是无符号类型的数据右移时最高位补的0 。

相关文章:

C语言——结构体、共用体、枚举、位运算

C语言——结构体、共用体、枚举、位运算 结构体共用体枚举位运算 结构体 如果将复杂的复杂的数据类型组织成一个组合项&#xff0c;在一个组合项中包含若干个类型不同&#xff08;当然也可以相同&#xff09;的数据项。 C语言允许用户自己指定这样一种数据结构&#xff0c;它称…...

[LitCTF 2024]exx

输入任意账号密码进行抓包 考查xxe漏洞 我们加入xxe语句并让它回显我们要它会显的东西&#xff1a;先来读取一下用户名和密码 我们可以看到&#xff0c;它已经读取了服务器下的账号密码文件&#xff0c;接着我们直接读取根目录下的flag文件。通常情况下flag文件的位置一般就根…...

kafka运维常用命令

KAFKA常用命令 一、KAFKA常用命令1.1kafka造数1.2kafla抓包1.2.1实时查看kafka数据1.2.2查看kafka历史数据1.2.3查看kafka中带有某个ip的历史数据1.2.4将kafka数据存入文件中 一、KAFKA常用命令 1.1kafka造数 示例&#xff1a;给topic为 ids-test 造数 ./kafka-console-produ…...

笔记:在WPF中OverridesDefaultStyle属性如何使用

一、目的&#xff1a;介绍下在WPF中OverridesDefaultStyle属性如何使用 OverridesDefaultStyle 属性在 WPF 中用于控制控件是否使用默认的主题样式。将其设置为 True 时&#xff0c;控件将不会应用默认的主题样式&#xff0c;而是完全依赖于你在 Style 中定义的样式。以下是如何…...

MATLAB/Simulink 与Gazebo联合仿真

在机器人技术、自动化控制和仿真领域,MATLAB和Gazebo是两种常用的工具,它们各自具有不同的优势,但在某些情况下,可以联合使用以实现更复杂的仿真效果。下面将介绍如何在MATLAB环境中与Gazebo进行联合仿真。 MATLAB与Gazebo联合仿真的基础 MATLAB环境:MATLAB是一款强大的数…...

并查集-应用方向以及衍生汇总+代码实现(c++)-学习一个数据结构就会做三类大题!

并查集的核心功能&#xff0c;合并集合&#xff0c;查找元素&#xff0c;这两个最基本的功能相关题目本文不列举了&#xff0c;主要是一些和图相关的&#xff1a; 并查集的核心母题 一、连通性检测&#xff1a; 问题&#xff1a;判断在一个图中&#xff0c;任意两点是否连通。…...

设计模式六大原则-开放封闭原则(二)

开放封闭原则&#xff08;Open-Closed Principle, OCP&#xff09;是设计模式六大原则之一&#xff0c;也是面向对象设计&#xff08;OOD&#xff09;中的核心原则之一。它强调软件实体&#xff08;如类、模块、函数等&#xff09;应该对扩展开放&#xff0c;对修改封闭。这一原…...

C# 截取两个点之间的线段,等距分割线

//取线段上两点之间的沿线线段//line 线//startDist:距离线第一个点的起点位置//stopDist:距离线第一个点的终点位置public static List<double[]> lineSliceAlong(List<double[]> line, double startDist, double stopDist){double travelled 0;double overshot …...

打造聊天流式回复效果:Spring Boot+WebSocket + JS实战

本篇博客将带领你使用 Spring Boot、WebSocket 和 JavaScript 实现一个类似 ChatGPT 的流式回复效果。前端发送消息后&#xff0c;后端接收消息并请求 AI API&#xff0c;并将 AI 返回的流式响应实时推送到前端&#xff0c;最终在聊天界面呈现出逐字出现的打字效果。 技术原理…...

202年版最新Python下载安装+PyCharm下载安装激活和使用教程!(附安装包+激活码)

一、Python解释器下载【运行环境】 Python官网&#xff1a; https://www.python.org Python各版本解释器官网&#xff1a; https://www.python.org/downloads/ 二、Windows系统安装Python解释器 下载Python版本解释器 现在已经更新到了3.13版本的Python解释器&#xff0c;但…...

【面试宝典】spring常见面试题总结[上]

一、什么是 Spring 框架&#xff1f; Spring 框架是一个为 Java 应用程序的开发提供了综合、广泛的基础性支持的 Java 平台。 Spring 帮助开发者解决基础性的问题&#xff0c;使开发者专注编写业务代码。 二、Spring Freamework 有哪些功能&#xff1f; IOC: 控制反转AOP: 面…...

NC单链表的排序

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff0c;这篇文章男女通用&#xff0c;看懂了就去分享给你的码吧。 描述 给定一个节点…...

阿里云部署open-webui实现openai代理服务(持续更新)

一、展示 xiezhaoxuan.top:8080 二、 环境准备 1. 阿里云服务器,ubuntu22系统 2. http代理(可访问外网) 3. openai API Key 三、实际操作记录(阿里云服务器端) 1. 根据官方文档安装open-webui服务端(看完这节再操作): 🚀 Getting Started | Open WebUI 1. 如果服务器配置比较…...

Vue3简介和快速体验

文章目录 前言1. Vue3介绍2. Vue3快速体验(非工程化方式) 前言 本次主要用VScode开发代码&#xff0c;vscode的安装很简单&#xff0c;不会的可以查询一下网上的资料 1. Vue3介绍 Vue (发音为 /vjuː/&#xff0c;类似 view) 是一款用于构建用户界面的 JavaScript 框架。它基于…...

LeetCode98 验证二叉搜索树

前言 题目&#xff1a; 98. 验证二叉搜索树 文档&#xff1a; 代码随想录——验证二叉搜索树 编程语言&#xff1a; C 解题状态&#xff1a; 对中序遍历理解不到位 思路 了解了中序遍历会返回一个有序数组后&#xff0c;本题就可以迎刃而解。只需要判断&#xff0c;返回的数组…...

llama的神经网络结构;llama的神经网络结构中没有MLP吗;nanogpt的神经网络结构;残差是什么;残差连接:主要梯度消失

目录 解释代码 潜在问题和修正 结论 llama的神经网络结构 神经网络结构概述 举例说明 llama的神经网络结构中没有MLP吗 nanogpt的神经网络结构 1. 词嵌入层(Embedding Layer) 2. Transformer编码器层(Transformer Encoder Layer) 3. 层归一化(Layer Normalizat…...

函数的常量引用入参const saclass sdf,可否传入一个指向saclass对象的指针 shared_ptr<saclass>

不可以直接将一个指向 saclass 对象的 shared_ptr<saclass> 作为参数直接传入一个期望 const saclass& 类型参数的函数。原因是类型不匹配&#xff1a;shared_ptr<saclass> 是一个智能指针类型&#xff0c;它封装了对 saclass 对象的指针&#xff0c;并提供了一…...

数据库:SQL——数据库操作的核心语言

数据库&#xff1a;SQL——数据库操作的核心语言 SQL&#xff08;结构化查询语言&#xff09;是关系型数据库管理系统中的标准语言&#xff0c;广泛用于数据的定义、操作、控制和查询。SQL 包含多个子语言&#xff0c;分别用于不同的数据库操作任务&#xff0c;包括数据定义&a…...

Unity + HybridCLR 从零开始

官方文档开始学习,快速上手 | HybridCLR (code-philosophy.com)是官方文档链接 1.建议使用2019.4.40、2020.3.26、 2021.3.0、2022.3.0 中任一版本至于其他2019-2022LTS版本可能出现打包失败情况 2. Windows Win下需要安装visual studio 2019或更高版本。安装时至少要包含 使…...

C++小总结

C小总结 接口 对外暴露头文件中&#xff0c;只需要声明接口函数即可&#xff0c;其他不暴露的函数不需要进行声明。接口的参数使用指针形式比较好&#xff0c;因为外部使用时可以对实参进行创建和析构&#xff0c;如果非接口函数使用new开辟&#xff0c;不太好进行析构。在使…...

告别adb shell:用Python脚本一键搞定Android屏幕截图与导出

Python自动化&#xff1a;告别adb shell&#xff0c;一键搞定Android屏幕截图与导出 每次调试Android应用时&#xff0c;手动敲adb命令截图、导出、重命名&#xff0c;是不是让你感到效率低下&#xff1f;作为一名长期与Android设备打交道的开发者&#xff0c;我深知这种重复劳…...

AI Agent(智能体)的输出格式应该从 Markdown 转向 HTML吗?

在近期&#xff08;2026年5月&#xff09;的技术圈和AI社区引发了非常热烈的讨论。提出这个观点的是 Anthropic&#xff08;Claude背后的公司&#xff09;负责 Claude Code 团队的工程师 Thariq Shihipar&#xff0c;他最近发表了一篇题为《使用 Claude Code&#xff1a;HTML 极…...

JSON数据同步利器:深度解析ogre-software/json-synchronizer的核心原理与应用

1. 项目概述&#xff1a;一个被低估的JSON数据同步利器如果你经常和JSON数据打交道&#xff0c;尤其是在前后端分离、微服务架构或者多数据源集成的场景下&#xff0c;你肯定遇到过这样的烦恼&#xff1a;手头有两份甚至多份JSON数据&#xff0c;它们结构相似&#xff0c;但内容…...

DIY红外热像仪进阶:手把手教你用C语言实现7种伪彩色编码(附完整代码)

DIY红外热像仪进阶&#xff1a;手把手教你用C语言实现7种伪彩色编码&#xff08;附完整代码&#xff09; 当32x24的温度矩阵在屏幕上呈现为单调的灰度图像时&#xff0c;你是否想过如何让它焕发生机&#xff1f;伪彩色编码技术正是打开这扇门的钥匙。本文将带你深入探索七种经…...

Claude技能生态构建指南:从Awesome清单到实战开发

1. 项目概述&#xff1a;为什么我们需要一个“Claude技能”的Awesome清单&#xff1f;如果你最近也在深度使用Claude&#xff0c;尤其是Claude Desktop或者API&#xff0c;你可能会和我有一样的感受&#xff1a;这家伙的能力边界&#xff0c;似乎每天都在被开发者们用各种“技能…...

树莓派4B + MPU9250:从零到一搭建你的第一个姿态传感器(附完整代码与避坑指南)

树莓派4B与MPU9250实战&#xff1a;从硬件连接到姿态解算的全流程指南 1. 准备工作与环境搭建 1.1 硬件清单与连接指南 在开始之前&#xff0c;我们需要准备以下硬件组件&#xff1a; 树莓派4B&#xff08;建议4GB内存版本&#xff09;MPU9250九轴传感器模块杜邦线&#xff08;…...

手把手复现1G通话:用Python模拟FM调制、FSK信令与FDMA多用户通信

手把手复现1G通话&#xff1a;用Python模拟FM调制、FSK信令与FDMA多用户通信 在移动通信的演进史中&#xff0c;1G系统如同数字时代的罗塞塔石碑&#xff0c;用模拟信号承载了人类首次无线对话的自由。今天我们将穿越回1983年摩托罗拉DynaTAC 8000X面世的年代&#xff0c;用Py…...

从强化学习视角看HDP:ADP中的Actor-Critic框架到底怎么工作的?

从强化学习视角看HDP&#xff1a;ADP中的Actor-Critic框架到底怎么工作的&#xff1f; 在控制理论与机器学习交叉领域&#xff0c;自适应动态规划&#xff08;ADP&#xff09;与强化学习&#xff08;RL&#xff09;的融合正催生新一代智能控制范式。当我们以RL从业者熟悉的Act…...

CTF新手必看:用010Editor和CRC校验,5分钟揪出被篡改的PNG图片宽高

CTF新手实战&#xff1a;5分钟掌握PNG图片宽高篡改检测技巧 当你第一次参加CTF比赛&#xff0c;面对一张无法正常显示的PNG图片时&#xff0c;是否感到无从下手&#xff1f;这很可能是题目设计者修改了图片的宽高参数。作为MISC方向的基础题型&#xff0c;掌握快速检测PNG图片…...

sqlite-utils与Datasette集成:构建数据驱动的Web应用终极指南

sqlite-utils与Datasette集成&#xff1a;构建数据驱动的Web应用终极指南 【免费下载链接】sqlite-utils Python CLI utility and library for manipulating SQLite databases 项目地址: https://gitcode.com/gh_mirrors/sq/sqlite-utils sqlite-utils是一款强大的Pytho…...