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

news2024/9/21 14:52:29

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 。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2037388.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Sentinel 滑动时间窗口源码分析

前言&#xff1a; Sentinel 的一个重要功能就是限流&#xff0c;对于限流来说有多种的限流算法&#xff0c;比如滑动时间窗口算法、漏桶算法、令牌桶算法等&#xff0c;Sentinel 对这几种算法都有具体的实现&#xff0c;如果我们对某一个资源设置了一个流控规则&#xff0c;并…

【upload]-ini-[SUCTF 2019]CheckIn-笔记

上传图片木马文件后看到&#xff0c;检查的文件内容&#xff0c;包含<? 一句话木马提示 检查的文件格式 用如下图片木马&#xff0c;加上GIF89a绕过图片和<?检查 GIF89a <script languagephp>eval($_POST[cmd])</script> .user.ini实际上就是一个可以由用…

交换机VLAN配置中Tagged与Untagged端口的差异和应用区别

VLAN&#xff08;虚拟局域网&#xff09;是一种将局域网设备从逻辑上划分为不同虚拟工作组的技术。它打破了传统局域网在物理位置上的限制&#xff0c;允许网络管理员根据功能、部门或安全需求等因素&#xff0c;将同一物理网络中的设备划分到不同的逻辑网络中。每个VLAN都像一…

使用Copilot辅助编程:我如何减少加班并提高工作效率

当我聘用了一个高级工程师给我写代码&#xff0c;我再也不加班了&#xff01; 很多医生朋友说写代码很难&#xff0c;学不会python。在这个AI时代&#xff0c;作为智慧的顶尖人类&#xff0c;你还在百度搜代码真的是out了。 学会站在巨人的肩膀上&#xff0c;让AI替你搬砖&am…

工业互联网边缘计算实训室解决方案

一、引言 随着物联网&#xff08;IoT&#xff09;、5G通信技术的快速发展&#xff0c;工业互联网已成为推动制造业转型升级的重要力量。边缘计算作为云计算的延伸和补充&#xff0c;在实时数据分析、降低数据传输延迟、提升处理效率及增强数据安全性方面展现出巨大潜力。在此背…

C语言——查漏补缺

前言 本篇博客主要记录一些C语言的遗漏点&#xff0c;完成查漏补缺的工作&#xff0c;如果读者感兴趣&#xff0c;可以看看下面的内容。都是一些小点&#xff0c;下面进入正文部分。 1. 字符汇聚 编写代码&#xff0c;演示多个字符从两端移动&#xff0c;向中间汇聚 #inclu…

无人机可以用来追黄蜂吗?

哈哈&#xff0c;这个问题真是挺有趣的&#xff01;不过&#xff0c;从实际应用和安全性角度来考虑&#xff0c;使用无人机来追黄蜂可能并不是一个好主意。 首先&#xff0c;黄蜂通常对突然出现的移动物体非常敏感&#xff0c;尤其是像无人机这样的“不明飞行物”。如果无人机…

【网站项目】SpringBoot679牙科诊所管理系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

Qt 窗口:对话框详解

目录 对话框 1. 对话框的基本概念 2. 对话框的内存释放问题 3. 自定义对话框界面 3.1 使用纯代码的方式定义 3.2 使用图形化的方式定义 4. 对话框的分类 4.1 模态对话框 4.2 非模态对话框 4.3 混合属性对话框 5. Qt 内置对话框 5.1 消息对话框 QMessageBox 示例1&…

生活生鲜超市小程序系统开发方案

生活生鲜超市小程序系统是集商品浏览、在线下单、支付结算、物流配送、会员管理等功能于一体。是为了满足现代消费者对新鲜食品购买的便利性与即时性需求。 适用于&#xff1a;生鲜超市、百货、连锁、水果、批发、便民、果蔬、食品、食材、鲜果、特产、海鲜等店铺。一、目标用户…

IP基础知识以及IP地址分类(A类 B类 C类 D类 E类)

IP地址是什么&#xff1f; IP 地址是互联网协议特有的一种地址&#xff0c;它是 IP 协议提供的一种统一的地址格式&#xff0c;为互联网上的每一个网络和每一台主机分配一个逻辑地址&#xff0c;以此来屏蔽物理地址的差异。 MAC和IP 在⽹络数据包传输中&#xff0c;源IP地址…

蜂鸣器(51单片机)

一、蜂鸣器介绍 1.蜂鸣器 2.蜂鸣器电路 3.芯片图示 二、蜂鸣器功能实现 1.蜂鸣器提示音代码 蜂鸣器函数 播放提示音功能实现 2.蜂鸣器播放音乐

Scrapy框架进行数据采集详细实现

摘要 本项目是python课程的课程项目&#xff0c;在简要学习完python和爬虫相关的Scrapy框架后&#xff0c;基于这两者的运用最终完成了对于北京链家网站新房页面的信息进行爬取&#xff0c;并将爬取的数据存放于excel之中&#xff0c;可使用excel或者wps进行查看。 1 引言 1…

论文分享 | Fuzz4All: 基于大语言模型的通用模糊测试

大语言模型是当前最受关注的研究热点&#xff0c;基于其生成和理解能力&#xff0c;对现有领域在提升性能和效果上做更多尝试。分享一篇发表于2024年ICSE会议的论文Fuzz4All&#xff0c;它组合多个大语言模型以非常轻量且黑盒的方式&#xff0c;实现了一种跨语言和软件的通用模…

【数学分析笔记】第1章第2节:映射与函数(2)

1. 集合与映射 1.12 函数&#xff08;实函数&#xff09; 函数是映射的一种特殊情况&#xff0c; f : X ⟶ Y f:\textbf{X}\longrightarrow \textbf{Y} f:X⟶Y x ⟼ y f ( x ) x\longmapsto yf(x) x⟼yf(x) 如果 X ⊂ R , Y R \textbf{X}\subset\mathbb{R},\textbf{Y}\ma…

OpenCV的Hello World

按照前文的步骤&#xff0c;我们已经在Windows机器上把OpenCV源代码编译成了DLL。接下来的问题自然是&#xff0c;我们怎么在自己的项目中使用OpenCV&#xff1f;我们将从零开始编写第一个OpenCV “Hello World”程序。通过本文的练习&#xff0c;大家将掌握&#xff1a; 在自…

你真的懂什么是串口吗?

你真的懂什么是串口吗&#xff1f; 文章目录 你真的懂什么是串口吗&#xff1f;问题什么是串口概念4种电平及其特性串口与电脑USB口通信 通讯分类常见的串行通信接口通信方式分类串行通信并行通信 传输方向分类同步方式分类PS&#xff1a;波特率和比特率 RS232 和 RS485RS232 引…

Django 中显示图片

在 Django 中显示图片的基本步骤包括&#xff1a;配置静态文件和媒体文件的处理、上传图片、以及在模板中显示图片。以下是详细步骤&#xff1a; 问题背景&#xff1a; 我在学习 Django 并在构建一个简单的网站&#xff0c;我尝试使用模板语言添加一些图片&#xff0c;但显示的…

升级阿里云linux服务器上的php版本

查看已安装的php软件包 [rootiZbp13pl2v34qj0thwq9aiZ ~]# rpm -qa|grep php php74-php-common-7.4.26-1.el7.remi.x86_64 oniguruma5php-6.9.7.1-1.el7.remi.x86_64 php74-php-gd-7.4.26-1.el7.remi.x86_64 php74-php-opcache-7.4.26-1.el7.remi.x86_64 php74-php-json-7.4.2…

阿里云Elasticsearch 企业级AI搜索方案发布

在AI技术日新月异的今天&#xff0c;尤其是大语言模型的兴起&#xff0c;企业智能化场景的解决方案正经历一场前所未有的革新。然而&#xff0c;大模型在实际应用中面临的挑战不容小觑&#xff0c;如何高效、精准地服务于企业的个性化需求成为关键。阿里云搜索产品团队通过阿里…