C语言——小细节和小知识9

一、大小端字节序

1、介绍

在计算机系统中,大小端(Endianness)是指多字节数据的存储和读取顺序。它是数据在内存中如何排列的问题,特别是与字节顺序相关。C语言中的数据存储大小端字节序指的是在内存中存储的多字节数据类型(如整型、浮点型)的字节序排列方式,主要有两种:

  1. 大端字节序(Big-Endian):在大端字节序中,一个多字节数据的最高有效字节(即“大端”)存储在内存的最低地址处,其余字节按照在数值中的顺序依中次存储在连续的内存地址。例如,一个四字节的整数 0x12345678 在内存中的存储顺序(从低地址到高地址)为 12 34 56 78

  2. 小端字节序(Little-Endian):在小端字节序中,一个多字节数据的最低有效字节(即“小端”)存储在内存的最低地址处,其余字节按照在数值中的逆序存储在连续的内存地址中。采用同样的四字节整数 0x12345678 为例,在内存中的存储顺序(从低地址到高地址)将会是 78 56 34 12

大小端字节序通常由硬件决定,即由CPU的设计来规定。例如,Intel的x86架构是小端字节序,而网络协议通常采用大端字节序。在C语言编程中,通常不需要关心数据的字节序,除非你在进行底层的内存操作或者网络通信、跨平台数据传输等需要考虑字节序兼容性的场合。在这些情况下,你可能需要使用函数如 htonl()ntohl() 来在主机字节序和网络字节序之间转换整数类型的数据。

目前,大部分的个人电脑和服务器处理器采用小端(Little-Endian)字节序。这主要是因为Intel的x86架构处理器和后续的x86-64架构(也称为AMD64)都采用小端字节序,而这些处理器在个人电脑和服务器市场中占据主导地位。

除了Intel和AMD之外,许多基于ARM架构的处理器也通常配置为小端模式,尤其是在智能手机和平板电脑等移动设备中。ARM架构是可切换的,即可以在大端和小端之间切换,但在实际应用中,小端模式更为普遍。

大端(Big-Endian)字节序相对来说较少见,但在某些应用和处理器设计中仍然使用,例如在一些嵌入式系统、网络设备和早期的IBM、Sun等公司的系统中。网络协议,如IP协议,使用的是大端字节序,这通常称为网络字节序

随着市场的发展和技术的演进,小端字节序成为了主流,但在进行跨平台或网络编程时,处理字节序依然非常重要。在这些领域,开发者必须确保数据在不同字节序的系统间正确传输和解释。

2、例子

以下程序的运行结果:

#include <stdio.h>

int main()
{
	int arr[] = { 1,2,3,4,5 };
	short* p = (short*)arr;
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		*(p + i) = 0;
	}
	for (i = 0; i < 5; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

在运行后,我们发现运行结果是:

这就可以证明这里是小端字节序。

3、分析

数组中元素内容用十六进制表示是:

为什么可以表示成这样呢?实际上可以这样解释:

这是因为一个十六进制数的单个位可以表示4位二进制数的值。换句话说,十六进制数的每一位相当于二进制数的一个四位组(nibble),即:

  • 0 二进制表示为 0000
  • 1 二进制表示为 0001
  • 2 二进制表示为 0010
  • ...
  • E 二进制表示为 1110
  • F 二进制表示为 1111

十六进制的一位可以表示0 ~ 15这16个数字,而16是2 ^ 4,在二进制下,四位二进制数恰好可以表示0000 ~1111这16个数字,(这里我们可以这样理解,四位二进制数字,每一位有两种状数字,即0或1,而这里有四位,所以总共可表示的数字是2 * 2 * 2 * 2中,即2 ^ 4个数字,也就是16个数字),所以可以可以用一位十六进制数字表示四位二进制数字。

所以,一个两位的十六进制数,可以表示两个四位组,即8位二进制,这正好是一个字节(1 Byte)的大小。例如:

  • 十六进制的 00 表示二进制的 0000 0000
  • 十六进制的 01 表示二进制的 0000 0001
  • 十六进制的 FF 表示二进制的 1111 1111

最开始数组的存储是这样的:

由于int的大小为4字节,而short类型是2字节,在经过强制转换后,再通过一个循环对数组的内容进行更改:

从这里我们可以发现这里使用的是小端字节序,因为这里打印时第三个数据是0,如果是大端字节序,则第三个元素应当还是3。

二、整型的首地址

1、介绍

如果整型数据是以小端字节序(Little-Endian)存储的,那么该数据的首地址会指向这个整型数据的最低有效字节。换句话说,整型数据的首个字节(存储在首地址处的字节)包含了这个数值的最低位部分。

这意味着,如果我们有一个32位的整型数值 0x12345678,并且我们的系统是小端字节序,那么在内存中的布局将从首地址开始按照下列方式存储:

Memory Address   Value
0x0000           0x78  // 最低有效字节 (LSB)
0x0001           0x56
0x0002           0x34
0x0003           0x12  // 最高有效字节 (MSB)

在这种情况下,首地址 0x0000 指向的是值 0x78,这是这个整型数值的最低有效字节。

如果整型数据是以大端字节序(Big-Endian)存储的,那么该数据的首地址会指向这个整型数据的最高有效字节。换句话说,在大端字节序中,整型数据的首个字节(存储在首地址处的字节)包含了这个数值的最高位部分。

例如,考虑相同的32位整型数值 0x12345678。如果我们的系统采用大端字节序,那么在内存中的布局将从首地址开始按照如下方式存储:

Memory Address   Value
0x0000           0x12  // 最高有效字节 (MSB)
0x0001           0x34
0x0002           0x56
0x0003           0x78  // 最低有效字节 (LSB)

在这个例子中,首地址 0x0000 指向的是值 0x12,这是这个整型数值的最高有效字节。这和小端字节序相对,小端字节序的首地址指向最低有效字节。

所以对于两种字节序,实际上整型的首地址都是较低的地址。

2、例子

#include <stdio.h>

int main()
{
	int a = 0x11223344;
	char* pa = (char*)&a;
	*pa = 0;
	printf("%x\n", a);
	return 0;
}

这个程序的运行结果是:

3、分析

因为整型数据首地址是较低的地址,又因为这里是小端字节序,所以a在内存中的存储是:

由于char类型的数据是1字节,所以在用char *类型指针访问a的时候只能访问到a的首地址中的数据,所以只能更改a首地址指向的内存中的数据,这样就只有一个字节的数据被改动。

所以得到了那样的结果。

三、gets_s函数

1、介绍

我们在需要获取标准输入流中的内容时,一般是用scanf()函数,我们知道在要读取一个字符串时,可以用:

	char arr[10001];
	scanf("%s", arr);

但是我们也知道scanf在读取到空格和换行时会停止读取或进行下一个数据的读取不会将空格和数据读到一个缓冲区中。这就导致如果我们要读取一个完整的英文句子例如:

i love you.

就不能用scanf函数。

然而实际上我们可以用别的函数解决这个问题,那就是gets,这里会有人问了,你的标题不是gets_s么,怎么又变成了gets了?

实际上gets是gets_s的老版本,gets 函数因为安全性问题已经在C11标准中被废弃,并在C17标准中被彻底移除。gets 函数不检查目标缓冲区的长度,因此非常容易造成缓冲区溢出,这是一个严重的安全漏洞。

gets_sgets 的一个安全版本,定义在 <stdio.h> 头文件中,并且它要求调用者提供缓冲区的大小,以避免超出缓冲区边界的写入,因为超出缓冲区可能导致缓冲区溢出攻击或程序崩溃。

函数原型如下:

char *gets_s(char *str, rsize_t n);

这里:

  • str 是指向用来存储输入字符串的字符数组的指针。
  • n 是 str 中可以存储字符的最大数量,包括结尾的空字符('\0')。

如果读取成功,gets_s 会从标准输入读取一行直到遇到换行符或EOF(文件结束符)。换行符不会被复制到数组中,数组会以空字符结尾。

注意,gets_s 函数是可选的,因此不是所有支持C11标准的编译器都实现了这个函数。在实际使用中应该检查你的开发环境是否支持它。

gets_s 的返回值为:

  • 如果成功,返回一个指向 str 的指针。
  • 如果遇到错误或文件结束而没有读取任何字符,返回 NULL

使用 gets_s 时需要特别小心,即使它比 gets 更安全。你需要确保你传递的 n 值不大于分配给 str 的实际内存大小。即使 gets_s 会检查这个大小,但如果你的大小参数错误,这可能会导致未定义行为。另外,建议避免使用 getsgets_s,而是使用 fgets,因为 fgets 在所有标准的C库中都是可用的,并且也允许你指定缓冲区大小。

2、例子

#include <stdio.h>

int main()
{
	char buffer[20] = { '\0' };
	gets_s(buffer, 20);
	printf("%s\n", buffer);
	return 0;
}

运行结果:

这里结果只有一个换行,是printf函数中的\n而不是gets_s读取的回车,因为gets_s函数不会将换行符复制到数组中。

四、fgets函数

1、介绍

上面提到了fgets函数使用更广泛,那具体是怎么使用的呢?

fgets 函数是一个在C语言中广泛使用的标准库函数,用于从文件流中读取一行。

fgets 函数的原型定义在 <stdio.h> 头文件中,如下所示:

char *fgets(char *str, int num, FILE *stream);

参数说明:

  • str:指向一个字符数组的指针,这个数组用来存储读取的字符串。
  • num:指定要读取的最大字符数,包括最后的空字符('\0')。简单来说,如果缓冲区大小为 n,那么最多读取 n-1 个字符,保证有空间放置字符串结尾的空字符。
  • stream:要读取的输入流,通常是文件指针。如果你想从标准输入(通常是键盘)读取,可以使用 stdin 作为这个参数。

又有人会问了,为什么上面的gets_s函数的字符串最大存储数(包括' \0 ')的类型是rsize_t,而这里的fgets函数的是int类型?

gets_sfgets 函数的参数类型不同,这主要是因为它们分别遵循了C的不同标准,并且设计上考虑了不同的安全性和可移植性问题。

gets_s 是在C11标准中引入的安全版本的gets函数。其参数类型 rsize_t 是一种在C11中定义的新的类型。这个类型用于表示对象的大小,是一个无符号的整数类型,并且是为了增强程序的安全性和可移植性。rsize_t 的使用意味着gets_s函数的缓冲区大小参数不应该为负数。

char *gets_s(char *s, rsize_t n);

另一方面,fgets 函数存在的时间要比gets_s长得多,它是在之前的C标准中定义的,包括ANSI C和C99,这些标准中没有rsize_t类型。在fgets的定义中,其缓冲区大小参数是int类型,这已经被广泛使用并且在各种C编译器和平台中都得到了支持。

char *fgets(char *str, int n, FILE *stream);

虽然从理论上讲,int类型可以接受负数,但在fgets的上下文中,传递一个负数没有逻辑意义,因为它代表了缓冲区的大小。实际上,如果调用fgets时传入了一个负数,函数的行为将是未定义的。

总的来说,rsize_t的使用提供了更强的类型安全性,强调了函数参数应当是一个合理的大小值。而fgets使用int是因为它遵循了旧的标准,而那时候没有为了表示大小而专门设立的无符号类型。在实际使用中,你应该总是传入正数作为这些函数的大小参数。

fgets 会从指定的 stream 读取字符,直到发生以下三种情况之一:

  1. 读取了 num-1 个字符。
  2. 读取到了一个换行符,换行符会被存储在字符串中。
  3. 遇到了文件结束符(EOF)。

在字符串的末尾,无论是因为读取到了换行符还是因为达到了字符数量限制,fgets 总是会在最后添加一个空字符('\0')来表示字符串的结束。

fgets 的返回值:

  • 成功:返回 str 的指针。
  • 失败或遇到文件结束符而没有读取任何字符:返回 NULL

由于 fgets 包括换行符在内的读取方式,因此通常在使用 fgets 后需要检查并处理字符串末尾可能存在的换行符。

2、例子

#include <stdio.h>

int main()
{
	char buffer[20] = { '\0' };
	fgets(buffer, 20, stdin);
	printf("%s\n", buffer);
	return 0;
}

运行结果:

这里结果有两行换行,因为fgets函数会将换行符复制到数组中,再加上printf中的\n,刚好有两个换行。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

蓝桥杯练习题-穷举模拟

&#x1f4d1;前言 本文主要是【穷举模拟】——蓝桥杯练习题-穷举模拟的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 &#x1f304;…

人工智能 | ChatGPT 和文心一言哪个更好用?

github&#xff1a;https://github.com/MichaelBeechan CSDN&#xff1a;https://blog.csdn.net/u011344545 ChatGPT 和文心一言哪个更好用&#xff1f; ChatGPT 和文心一言哪个更好用&#xff1f;方向一&#xff1a;ChatGPT主要优势局限性和挑战如何克服chatGPT的局限性和挑战…

Docker Consul详解与部署示例

目录 Consul构成 Docker Consul 概述 Raft算法 服务注册与发现 健康检查 Key/Value存储 多数据中心 部署模式 consul-template守护进程 registrator容器 consul服务部署&#xff08;192.168.41.31&#xff09; 环境准备 搭建Consul服务 查看集群信息 registrato…

P9847 [ICPC2021 Nanjing R] Crystalfly 题解 (SPJ)

[ICPC2021 Nanjing R] Crystalfly 传送门&#xff1f; 题面翻译 给定一个 n ( 1 ≤ n ≤ 1 0 5 ) n(1\le n\le10^5) n(1≤n≤105) 个节点的树&#xff0c;每个节点上有 a i a_i ai​ 只晶蝶。派蒙最初在 1 1 1 号节点&#xff0c;并获得 1 1 1 号节点的所有晶蝶&#xf…

信驰达科技参与《汽车玻璃集成UWB数字钥匙发展研究白皮书》编制工作

为进一步探索汽车数字钥匙技术路线及开发思路&#xff0c;中国智能网联汽车产业创新联盟&#xff08;CAICV&#xff09;、福耀玻璃工业集团股份有限公司联合发起了《汽车玻璃集成UWB数字钥匙发展研究白皮书》研究工作。 2023年12月20日&#xff0c;由中国智能网联汽车产业创新…

【链路层】点对点协议 PPP

目录 1、PPP协议的特点 2、PPP协议的组成和帧格式 3、PPP协议的工作状态 目前使用得最广泛的数据链路层协议是点对点协议PPP(Point-to-Point Protocol)。 1、PPP协议的特点 我们知道&#xff0c;互联网用户通常都要连接到某个 ISP 才能接入到互联网。PPP 协议就是用户计算机…

企业网站建站源码系统:Thinkphp5内核企业网站建站模板源码 带完整的安装代码包以及搭建教程

随着互联网的快速发展&#xff0c;企业对于网站的需求日益增强。为了满足这一市场需求&#xff0c;小编给大家分享一款基于Thinkphp5内核的企业网站建站源码系统。该系统旨在为企业提供一套功能强大、易于使用的网站建设解决方案&#xff0c;帮助企业快速搭建自己的官方网站&am…

JMeter笔记(三)

个人学习笔记&#xff08;整理不易&#xff0c;有帮助点个赞&#xff09; 笔记目录&#xff1a;学习笔记目录_pytest和unittest、airtest_weixin_42717928的博客-CSDN博客 目录 一&#xff1a;参数化方法 1&#xff09;用户定义的变量 2&#xff09;函数助手 3&#xff09;…

【Docker构建MySQL8.0镜像】

Docker构建MySQL8.0镜像 部署流程1. 拉取docker镜像2. 创建数据卷&#xff0c;存放MySQL数据3. 启动MySQL镜像4. 初始化sql放入MySQL镜像5. 执行MySQL脚本6. MySQL镜像打包7. MySQL镜像迁移 部署流程 1. 拉取docker镜像 docker pull mysql:8.0.35拉取成功后就可以看到镜像了&…

python基础学习

缩⼩图像&#xff08;或称为下采样&#xff08;subsampled&#xff09;或降采样&#xff08;downsampled&#xff09;&#xff09;的主要⽬的有两个&#xff1a;1、使得图像符合显⽰区域的⼤⼩&#xff1b;2、⽣成对应图像的缩略图。 放⼤图像&#xff08;或称为上采样&#xf…

HCIA—15实验:规划与优化、检测。沉默接口、空接口。OSPF、认证 、汇总、沉默接口、加快收敛、缺省路由

学习目标&#xff1a; 实验&#xff1a;规划与优化、检测。沉默接口、空接口。OSPF、认证 、汇总、沉默接口、加快收敛、缺省路由 学习内容&#xff1a; 实验&#xff1a;规划与优化、检测。沉默接口、空接口。OSPF、认证 、汇总、沉默接口、加快收敛、缺省路由 1.要求——基…

Ubuntu系统默认的dash shell改成bash shell

在Ubuntu系统中&#xff0c;如果默认的/bin/sh链接指向了dash&#xff0c;而你希望将其更改为指向bash&#xff0c;可以通过以下步骤操作&#xff1a; sudo rm /bin/sh sudo ln -s /bin/bash /bin/sh 但是&#xff0c;这种做法并不推荐&#xff0c;因为某些系统服务和脚本依赖…

【动态规划】【C++算法】639 解码方法 II

作者推荐 【矩阵快速幂】封装类及测试用例及样例 涉及知识点 动态规划 字符串 滚动向量 LeetCode 639. 解码方法 II 一条包含字母 A-Z 的消息通过以下的方式进行了 编码 &#xff1a; ‘A’ -> “1” ‘B’ -> “2” … ‘Z’ -> “26” 要 解码 一条已编码的消息…

轻量应用服务器Lighthouse_香港轻量服务器_海外轻量服务器-腾讯云

腾讯云轻量应用服务器开箱即用、运维简单的轻量级云服务器&#xff0c;CPU内存带宽配置高并且价格特别便宜&#xff0c;大带宽&#xff0c;但是限制月流量&#xff0c;轻量2核2G3M带宽62元一年、2核2G4M优惠价118元一年&#xff0c;540元三年、2核4G5M带宽218元一年&#xff0c…

spring常见漏洞(4)

CVE-2018-1270 Spring Messaging 命令执行漏洞(CVE-2018-1270)&#xff0c;Spring框架中的 spring-messaging 模块提供了一种基于WebSocket的STOMP协议实现&#xff0c;STOMP消息代理在处理客户端消息时存在SpEL表达式注入漏洞&#xff0c;攻击者可以通过构造恶意的消息来实现…

汽车用螺纹紧固件的拧紧力矩规范主要考虑哪些方面——SunTorque智能扭矩系统

在汽车制造过程中&#xff0c;螺纹紧固件是连接和固定各个零部件的重要元件。为了保证汽车的可靠性和安全性&#xff0c;对于螺纹紧固件的拧紧力矩有着严格的规定和规范。SunTorque智能扭矩系统和大家一起掌握这一重要知识点。 拧紧力矩是指将螺纹紧固件拧紧到预定位置所需的力…

Vue创建项目配置情况

刚开始接触vue项目创建和运行因为node版本和插件版本不一致时长遇到刚装好插件&#xff0c;项目就跑不起来的情况&#xff0c;特此记录一下 vue -V vue/cli 5.0.8 node -v v12.22.12 npm -v 6.14.16 关闭驼峰命名检查、未使用语法检查 package.json文件内容&#xff1a; {&…

0基础学习VR全景平台篇第138篇:无人机航拍实操

上课&#xff01;全体起立~ 大家好&#xff0c;欢迎观看蛙色官方系列全景摄影课程&#xff01; 为了使全景的概念体现得更为广阔和大气&#xff0c;我们也需要在天空上运用无人机进行全景拍摄&#xff0c;而无人机的拍摄相对于地面来说也是较为简单&#xff0c;掌握其基本的拍…

LeetCode 算法题 1.两数之和(python版)

题目要求 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素在答案里不能重复出现。 代码 class…

电路原理1-线性电阻

前言&#xff1a;整理笔记基于清华大学于歆杰老师的《电路原理》&#xff0c;电路原理是基于无源负载和电源组成电路的分析方法。 1.基础数学知识 算术&#xff1a;数字之间的运算 代数&#xff1a;用变量和函数来代替数字 微积分&#xff1a;描述函数的累积效应&#xff0…