整数在内存中的存储和内存操作函数

目录

  • 整数在内存中的存储
    • 1. 整数在内存中的存储
    • 2. 大小端字节序和字节序判断
      • 2.1 什么是大小端?
      • 2.2 为什么有大小端
    • 3. 练习
      • 3.1 请简述大端字节序和小端字节序的概念,设计⼀个小程序来判断当前机器的字节序。(10分)-百度笔试题
      • 3.2 练习2
      • 3.3 练习3
      • 3.4 练习4
      • 3.5 练习5
  • 内存操作函数
    • 1. memcpy的使用和模拟实现
    • 2. memmove的使用和模拟实现
    • 3. memset函数的使用
    • 4.memcmp函数的使用


正文开始

整数在内存中的存储

1. 整数在内存中的存储

整数的2进制表示方法有三种,即 原码、反码和补码

有符号的整数,三种表式方法均有 符号位数值位 两部分,符号位都是用0表示“正”,用1表
示“负”,最高位的⼀位是被当做符号位,剩余的都是数值位。

正整数的原 , 反 , 补都相同
负整数的三种表示方法各不相同

对于整形来说: 数据的存放内存中其实存放的都是补码.

为什么呢?

在计算机系统中,数值⼀律⽤补码来表⽰和存储。
原因在于,使⽤补码,可以将符号位和数值域统⼀处理;
同时,加法和减法也可以统⼀处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程是
相同的,不需要额外的硬件电路。

2. 大小端字节序和字节序判断

当我们了解了整数在内存中的存储后, 我们调试看一个细节:

#include <stdio.h>
int main()
{
	int a = 0x11223344;

	return 0;
}

调试的时候 , 我们可以看到在a中的 0x11223344 这个数字是按照字节为单位 , 倒着存储的 , 这是为什么呢?

在这里插入图片描述
通过上图我们可以发现:

  1. 整数在内存中的存储是二进制的补码
  2. 在调试窗口中观察内存的时候, 为了方便展示, 显示的是16进制的值
  3. 存储的顺序是倒过来的!

接下来让我们一起揭晓大小端字节序

2.1 什么是大小端?

其实超过一个字节的数据在内存中存储的时候, 就有存储顺序的问题 , 按照不同的存储顺序 , 我们可以分为大端字节序存储和小端字节序存储, 下面是具体概念:

大端存储模式:

是指数据的低位字节内容保存在内存的⾼地址处,⽽数据的⾼位字节内容,保存在内存的低地址处

小端存储模式:

是指数据的低位字节内容保存在内存的低地址处,⽽数据的⾼位字节内容,保存在内存的⾼地址处。

上述概念需要记住, 方便分辨大小端

画图演示:

在这里插入图片描述

在这里插入图片描述

2.2 为什么有大小端

为什么要有大小端之分呢?

这是因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着⼀个字节,⼀个字节为8bit位,但是在C语⾔中除了8bit的 char 之外,还有16bit的 short 型,32bit的 long 型(要看具体的编译器),另外,对于位数⼤于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于⼀个字节,那么必然存在着⼀个如何将多个字节安排的问题。因此就导致了⼤端存储模式和⼩端存储模式。

例如:⼀个 16bit 的 short 型 x ,在内存中的地址为 0x0010 , x 的值为 0x1122 ,那么0x11 为⾼字节, 0x22 为低字节。对于⼤端模式,就将 0x11 放在低地址中,即 0x0010 中,0x22 放在⾼地址中,即 0x0011 中。⼩端模式,刚好相反。我们常⽤的 X86 结构是⼩端模式,⽽KEIL C51 则为⼤端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择是⼤端模式还是⼩端模式。

3. 练习

3.1 请简述大端字节序和小端字节序的概念,设计⼀个小程序来判断当前机器的字节序。(10分)-百度笔试题

题目解析:

我们知道 , 对于一个16进制的1来说 , 0x00000001 在内存内存中的存储模式无非为以下俩种, 分别为大端字节序和小端字节序 , 此时我们只需要判断这个数字在内存中存储的第一个字节里面的内容即可 , 若为0则为大端字节序, 低位放在大地址处 , 若为1则为小端字节序存储 , 低位放在低地址处 , 由此我们可以进行代码实现 .
在这里插入图片描述

代码如下:


//代码一
#include <stdio.h>
int check_sys()
{
	int i = 1;
	return (*(char*)&i);
}
int main()
{
	int ret = check_sys();
	if (ret == 1)
	{
		printf("⼩端\n");
	}
	else
	{
		printf("⼤端\n");
	}
	return 0;
}

//代码2 
int check_sys()
{
	union
	{
		int i;
		char c;
	}un;
	un.i = 1;
	return un.c;
}

3.2 练习2

#include <stdio.h>
int main()
{
	char a = -1;
	signed char b = -1;
	unsigned char c = -1;
	printf("a=%d,b=%d,c=%d", a, b, c);
	return 0;
}

试题分析:

第一步: 首先 -1在内存中如何存放的呢?

-1 原码为:
10000000 00000000 00000000 00000001
补码为(在内存中存放):
11111111 11111111 11111111 11111111

第二步: 放到a里面会发生截断只存储后八个bit位, 即
11111111 ->a
其实b也一样,c也一样
11111111 ->b
11111111 ->c

第三步:%d打印a , %d表示打印整数 , 而字符a需要进行整形提升之后进行打印 , a位-1 需要补符号位, 结果为
11111111 11111111 11111111 11111111 转化为原码打印结果为-1 , b也一样, 因为char变量在vs环境下默认为有符号char
而%d打印c , c也需要进行整形提升, 因为是无符号char所以默认为一个正数, 高位需要补0,结果为
00000000 00000000 00000000 11111111 , 因为是整数所以原码和补码结果一样 , 打印结果为255

代码运行:

在这里插入图片描述

对于有符号的char类型, 取值范围是: -128 ~ 127
在这里插入图片描述

对于无符号char的1取值范围是 0~255
在这里插入图片描述

3.3 练习3

#include <stdio.h>
int main()
{
 char a = -128;
 printf("%u\n",a);
 return 0;
}

试题分析:

有了刚才的基础作铺垫,
首先-128 的原码为 10000000 00000000 00000000 10000000 ,
反码为 11111111 11111111 11111111 01111111
补码为 11111111 11111111 11111111 10000000

存放在a里面要发生整形截断
10000000 ->a
%u表示打印无符号整形
此时需要整形提升 , 补符号位1
11111111 11111111 11111111 10000000
而%u表示打印一个无符号数, 默认原反补相同 , 所以打印结果仍然为
11111111 11111111 11111111 10000000
转换成十进制是一个很大的数

代码运行:

在这里插入图片描述

#include <stdio.h>
int main()
{
	char a = 128;
	printf("%u\n", a);
	return 0;
}

试题分析:

128 原码为:
00000000 00000000 00000000 10000000
因为是整数补码一样
存放在a中会进行截断, 存储内容为10000000

%u打印 先整形提升结果为
11111111 11111111 11111111 100000000
打印结果和上题一样

代码结果:

在这里插入图片描述

#include <stdio.h>
int main()
{
	char a = 128;
	printf("%d\n", a);
	return 0;
}

试题分析:
而这里我们采用%d打印结果 , 结果就是-128, 因为此时编译器会将整形提升的结果默认为补码, 而有符号为的补码需要转化为原码

代码结果:
在这里插入图片描述

3.4 练习4

#include <stdio.h>
int main()
{
	char a[1000];
	int i;
	for (i = 0; i < 1000; i++)
	{
		a[i] = -1 - i;
	}
	printf("%d", strlen(a));
	return 0;
}

试题分析:

求字符a的长度 , 也就是’\0’之前的内容 , 我们知道\0的ascll码值就是0, 也就是统计0之前的字符个数 , char型变量的存储范围为-128~127 , 首先我们来看下图 , 从00000000开始存储 , 到11111111的存储结果如下 , 而题目则是倒序, 即从-1倒序 ,
直到遇到0.其一个循环为255
在这里插入图片描述

代码运行:

在这里插入图片描述

3.5 练习5

#include <stdio.h>
//X86环境 ⼩端字节序 
int main()
{
	int a[4] = { 1, 2, 3, 4 };
	int* ptr1 = (int*)(&a + 1);
	int* ptr2 = (int*)((int)a + 1);
	printf("%x,%x", ptr1[-1], *ptr2);
	return 0;
}
//代码运行结构是啥?

试题分析: 指针+1 取决于指针类型
整数+1就是+1
此题便运用到了我们所学的整形的存储, 大小端字节序的问题,如图所示为小端字节序存储在内存中的分布, 首先&a即指向一个数组+1跳过一个数组, ptr1指向的位置如图所示, 强制转化为int* 类型, 而ptr1[-1], 即 * (ptr1-1),向前跳过一个字节, 指向的位置如图所示, 十六进制打印结果为4, 而a表示的是数组首元素的地址,转化为int整数类型,+1就是+1,即加上了一个字节, 而一个强制转化为int * , 而一个int占四个字节, 指向的位置如下图一所示, ptr2解引为0x02000000 ,因为为小端存储, 所以高地址为高位

在这里插入图片描述
代码运行:
在这里插入图片描述

内存操作函数

1. memcpy的使用和模拟实现

函数介绍:

void * memcpy ( void * destination, const void * source, size_t num );

• 函数memcpy从source的位置开始向后复制num个字节的数据到destination指向的内存位置。
• 这个函数在遇到 '\0' 的时候并不会停下来。
• 如果source和destination有任何的重叠,复制的结果都是未定义的。
#include <stdio.h>
#include <string.h>
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[10] = { 0 };
	memcpy(arr2, arr1, 20);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr2[i]);
	}
	return 0;
}

运行结果:

在这里插入图片描述

模拟实现:

#include<stdio.h>

void* memcpy(void* dst, const void* src, size_t count)
{
	void* ret = dst;
	assert(dst);
	assert(src);
	
	while (count--) {
		*(char*)dst = *(char*)src;
		dst = (char*)dst + 1;
		src = (char*)src + 1;
	}
	return(ret);
}

2. memmove的使用和模拟实现

函数介绍:

void * memmove ( void * destination, const void * source, size_t num );

• 和memcpy的差别就是memmove函数处理的源内存块和⽬标内存块是可以重叠的。
• 如果源空间和⽬标空间出现重叠,就得使⽤memmove函数处理
#include <stdio.h>
#include <string.h>
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	memmove(arr1 + 2, arr1, 20);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr1[i]);
	} 
	return 0;
}

运行结果:

在这里插入图片描述

模拟实现:
分为以下两种情况: 从前往后复制和从后往前复制

在这里插入图片描述

#include<stdio.h>

void* memmove(void* dst, const void* src, size_t count)
{
	void* ret = dst;
	if (dst <= src || (char*)dst >= ((char*)src + count)) {
		while (count--) {
			*(char*)dst = *(char*)src;
			dst = (char*)dst + 1;
			src = (char*)src + 1;
		}
	}
	else {
		dst = (char*)dst + count - 1;
		src = (char*)src + count - 1;
		while (count--) {
			*(char*)dst = *(char*)src;
			dst = (char*)dst - 1;
			src = (char*)src - 1;
		}
	}
	return(ret);
}

3. memset函数的使用

函数介绍:

void * memset ( void * ptr, int value, size_t num );

memset是⽤来设置内存的,将内存中的值以字节为单位设置成想要的内容。
#include <stdio.h>
#include <string.h>
int main ()
{
 char str[] = "hello world";
 memset (str,'x',6);
 printf(str);
 return 0;
}

代码结果:

1 xxxxxxworld

4.memcmp函数的使用

函数介绍:

1 int memcmp ( const void * ptr1, const void * ptr2, size_t num );

• ⽐较从ptr1和ptr2指针指向的位置开始,向后的num个字
• 返回值如下

在这里插入图片描述

#include <stdio.h>
#include <string.h>
int main()
{
	char buffer1[] = "DWgaOtP12df0";
	char buffer2[] = "DWGAOTP12DF0";
	int n;
	n = memcmp(buffer1, buffer2, sizeof(buffer1));
	if (n > 0)
		printf("'%s' is greater than '%s'.\n", buffer1, buffer2);
	else if (n < 0)
		printf("'%s' is less than '%s'.\n", buffer1, buffer2);
	else
		printf("'%s' is the same as '%s'.\n", buffer1, buffer2);
	return 0;
}

如有错误恳请指正, 感谢各位的点赞 收藏 !!!

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

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

相关文章

科研学习|科研软件——如何使用SmartPLS软件进行结构方程建模

SmartPLS是一种用于结构方程建模&#xff08;SEM&#xff09;的软件&#xff0c;它可以用于定量研究&#xff0c;尤其是在商业和社会科学领域中&#xff0c;如市场研究、管理研究、心理学研究等。 一、准备数据 在使用SmartPLS之前&#xff0c;您需要准备一个符合要求的数据集。…

Vector - CAPL - XCP介绍_02

前面我们介绍了关于使用vector XCP License后&#xff0c;通过CAPL对XCP协议进行连接、断开和获取当前XCP连接状态的函数&#xff0c;本篇文章不做过多的其他赘述&#xff0c;我们继续介绍CAPL控制XCP相关的其他函数。 目录 xcpActivate 代码示例 xcpDeactivate xcpActiva…

【周总结】

周总结 完成工单脚本的编写以及解决操作问题 完成相关 jira 问题修改 学习了 sentinel 整合项目使用流控&#xff0c;熔断&#xff0c;热点等功能的使用 2024/4/14 晴 1. 这周只办两件事&#xff0c;吃&#xff01;喝&#xff01;&#xff01;&#xff01; 2.忙忙忙&am…

开源博客项目Blog .NET Core源码学习(15:App.Hosting项目结构分析-3)

本文学习并分析App.Hosting项目中前台页面的关于本站页面和点点滴滴页面。 关于本站页面 关于本站页面相对而言布局简单&#xff0c;与后台控制器类的交互也不算复杂。整个页面主要使用了layui中的面包屑导航、选项卡、模版、流加载等样式或模块。   面包屑导航。使用layui…

Web前端 Javascript笔记3

1、垃圾回收机制 内存中的生命周期 1、内存分配 2、内存使用&#xff08;读写&#xff09; 3、内存回收&#xff0c;使用完毕之后&#xff0c;垃圾回收器完成 内存泄漏&#xff1a;该回收的&#xff0c;由于某些未知因素&#xff0c;未释放&#xff0c;叫做内存泄漏 栈&#xf…

PostgreSQL入门到实战-第二十九弹

PostgreSQL入门到实战 PostgreSQL中数据分组操作(四)官网地址PostgreSQL概述PostgreSQL中CUBE命令理论PostgreSQL中CUBE命令实战更新计划 PostgreSQL中数据分组操作(四) 如何使用PostgreSQL CUBE生成多个分组集 官网地址 声明: 由于操作系统, 版本更新等原因, 文章所列内容不…

Linux配置程序后台运行(前后台来回切换)

Linux配置程序后台运行 在日常开发过程中&#xff0c;会遇到我们在前台运行程序&#xff0c;此时我们临时有事&#xff0c;但不能关闭终端&#xff0c;否则程序就会在电脑熄屏&#xff08;终端session断开后&#xff09;停止运行。 那么作为一个合格的开发&#xff0c;就必须要…

Matlab 2024安装教程(附免费安装包资源)

鼠标右击软件压缩包&#xff0c;选择“解压到MatlabR2024a“。 2.打开解压后的文件夹&#xff0c;鼠标右击“MATHWORKS_R2024A“选择装载。 鼠标右击“setup“选择”以管理员身份运行“。点击“是“&#xff0c;然后点击”下一步“。复制一下密钥粘贴至输入栏&#xff0c;然后…

数据结构——双向链表的实现

文章目录 一、双向链表的结构二、实现双向链表创建双向链表的项目双向链表的结构List.h申请结点初始化打印尾插头插尾删头删查找在pos位置之后插入数据删除pos结点销毁 三、所有源代码List.hList.ctest.c 一、双向链表的结构 带头双向循环链表 注意&#xff1a;这⾥的“带头…

数据交换格式

一、什么是数据交换格式 在计算机的不同程序之间&#xff0c;或者不同的编程语言之间进行交换数据&#xff0c;也需要一种大家都能听得懂得‘语言’&#xff0c;这就是数据交换格式&#xff0c;它通过文本以特定的形式来进行描述数据。 二、常用的几种数据交换格式 客户端常…

图像处理ASIC设计方法 笔记17 连通域的图像标记算法

目录 (一)主要环节图像初步标记 -等价表初始化与等价关系记录 -整理等价表 -图像标记代换 -(二)详细算法步骤:1 等价表的操作:2 整理等价表:3 图像标记代换:连通域的图像标记算法有若干步骤,除了图像初步标记,还有等价表初始化、等价关系记录、整理等价表、图像标记代…

【C++】深度解析---赋值运算符重载(小白一看就懂!!)

目录 一、前言 二、 运算符重载 &#x1f34e;运算符重载 ① 概念引入 ② 语法明细 ③ 练习巩固 ④ 代码展示 &#x1f347;赋值运算符重载 ① 语法说明及注意事项 ② 默认的赋值运算符重载 ③ 值运算符不能重载成全局函数&#xff01; 三、总结 四、共勉 一、前言…

前端console用法分享

console对于前端人员来讲肯定都不陌生&#xff0c;相信大部分开发者都会使用console来进行调试&#xff0c;但它能做的绝不仅限于调试。 最常见的控制台方法 作为开发者&#xff0c;最常用的 console 方法如下&#xff1a; 控制台打印结果&#xff1a; 今天我分享的是一些 co…

Qt快速入门(MV架构之TableView + QStandardItemModel + 自定义代理小案例)

Qt快速入门&#xff08;MV架构之TableView QStandardItemModel 自定义代理小案例&#xff09; 关于MV架构的简单介绍 在Qt框架中&#xff0c;代理&#xff08;Delegate&#xff09;、模型&#xff08;Model&#xff09;和视图&#xff08;View&#xff09;之间的关系构成了…

jvisualVM分析jvm内存使用快照dump

服务发生内存溢出&#xff0c;就需要查看服务器上Java服务的jvm堆内存使用情况&#xff0c;可以使用dump命令生成dump文件&#xff0c;然后下载到本地&#xff0c;然后使用jvisualVM工具打开&#xff0c;即可实现可视化分析。 生成dump文件常用的两种方式&#xff1a; 第一种…

linux fixmap分析

本文基于Linux-4.19.125&#xff0c; ARM V7&#xff0c;dual core, MMU采用2级页表&#xff08;未开启LPAE&#xff09;。 1 为什么需要fixmap Linux内核启动过程中&#xff0c;经过汇编阶段后&#xff0c;mmu功能已经开启&#xff0c;后续只能通过虚拟地址来访问DDR&#x…

RAG应用开发实战02-相似性检索的关键 - Embedding

1 文本Embedding 将整个文本转化为实数向量的技术。 Embedding优点是可将离散的词语或句子转化为连续的向量&#xff0c;就可用数学方法来处理词语或句子&#xff0c;捕捉到文本的语义信息&#xff0c;文本和文本的关系信息。 ◉ 优质的Embedding通常会让语义相似的文本在空…

Linux 添加启动服务--Service

1&#xff0c;服务配置service文件 Service 服务的实际作用是开启后自动启动服务&#xff0c;运行一些不须要登录的程序&#xff0c;任务。 实例1、上电自动连接WIFI热点 1.1 新建.service文件 /etc/systemd/system/wificonnect.service [Unit] DescriptionService [wifico…

记录linux从0部署java项目(宝塔)

目录 一、安装宝塔可视化界面 二、部署前端 三、部署后端 1、配置并连接Mysql数据库 2、配置并连接redis 3、安装jdk 这里先记录一个安装后遇到的问题 安装openJDK 四、检查 一、安装宝塔可视化界面 宝塔面板下载&#xff0c;免费全能的服务器运维软件 运行安装脚本 安…

MySQL 社区版 安装总结

很早就安装过MySQL&#xff0c;没有遇到过什么问题&#xff0c;直接next就行了&#xff0c;这次在新电脑上安装却遇到了一些问题&#xff0c;记录一下。 安装的是MySQL社区版&#xff0c;下载地址是www.mysql.com&#xff0c;进入后选择DOWNLOAD页面&#xff0c;选择MySQL Com…