【C语言】理解数据在内存中的存储(详解)_c语言 如何定义固定内存地址存放数据

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

三种表示方法均由符号位(首位)和数值位(除首位外)两部分组成,符号位都是用‘0’和‘1’表示,‘0’表示正,‘1’表示负。
fuhao
正数的原码、反码和补码相同。
负数的原码、反码和补码之间的转换规则如下:
原码:直接将数值按照正负数的形式翻译成二进制就可以得到原码。
反码:原码的符号位不变,其他依次按位取反就可以得到反码。
补码:反码+1得到补码。
例1:

int a = 20;
int类型的字节大小是4个字节(Byte),1个字节(Byte)=8个比特位(bit),比特位(bit)由二进制数字0和1表示,4Byte=32bit,所以20要用32个比特位来表示。

00000000000000000000000000010100 ---- 20的原码
正数的原码、反码和补码相同,所以20的反码、补码与原码相同
00000000000000000000000000010100 ---- 20的反码
00000000000000000000000000010100 ---- 20的补码
00 00 00 14 ---- 十六进制表示20的补码
在VS中,采用的是小端字节序存储。
f

int b = -10;
10000000000000000000000000001010 ---- -10的原码
11111111111111111111111111110101 ---- -10的反码
11111111111111111111111111110110 ---- -10的补码
ff ff ff f6 ---- 十六进制表示-10的补码
在VS中,采用的是小端字节序存储。
2

对于整型来说,数据存放在内存中其实存放的是补码。整型表达式计算时,使用的是内存中的补码计算的,打印的时候我们看到的是原码转换成的十进制数字。
正数的原码、反码和补码相同,负数的原码和补码的相互转换方式如下:
(1)原码到补码-----原码的符号位不变、数值位取反后+1得到补码。
(2)补码到原码-----补码的符号位不变、数值位取反后+1得到原码。从补码到原码另一种方式就是补码-1后,取反(符号位除外)得到原码。可以用-10的原码和补码进行验证。

10000000000000000000000000001010 ---- -10的原码
11111111111111111111111111110110 ---- -10的补码

扩展:为什么内存中存放的是补码呢?在计算机系统中,数值一律用补码来表示和存储,原因在于使用补码可以将符号位和数值位统一进行处理;同时,加法和减法也可以统一处理(cpu只有加法器),此外,补码和原码相互转换,其运算过程是相同的不需要额外的硬件电路。通过下面例子可能会对此有更好的理解:

int c = 1 - 1;
1-1可以转换为1-(-1)
00000000000000000000000000000001 ---- 1的原码
10000000000000000000000000000001 ----- -1的原码
假设原码相加得到:
10000000000000000000000000000010 ----- -2
取出他们的补码:
00000000000000000000000000000001 ---- 1的补码
11111111111111111111111111111111 ----- -1的补码
补码相加后得到:
100000000000000000000000000000000 ---- 总共33位,内存中存不下,要把最前面的1丢掉
丢掉后得到:
00000000000000000000000000000000 ---- 0

2.大小端字节序存储

在内存中数据都是以字节为单位存储的,当1个数据的大小超过一个字节的时候,要存储到内存中就会有顺序的问题,而在上述中我们已经知道VS中是小端字节序存储。
大端字节序存储:是指数据的低位保存在内存的高地址中,而数据的高位保存在内存的低地址中。
小端字节序存储:是指数据的低位保存在内存的低地址中,而数据的高位保存在内存的高地址中。
我们先了解一下数值的高位和低位,在十进制数字123中,1的权重是102 , 2的权重是101,3的权重是100,我们把权重大便是高位,权重小的便是低位。
权重
练习1:

请简述大端字节序存储和小端字节序存储的概念,并设计一个程序来判断当前机器的字节序。
解析:
大端字节序存储:是指数据的低位保存在内存的高地址中,而数据的高位保存在内存的低地址中。
小端字节序存储:是指数据的低位保存在内存的低地址中,而数据的高位保存在内存的高地址中。

#include <stdio.h>
int main()
{
	int a = 1;
	char\* p = (char\*)&a;//拿出a的地址,强制类型转换成char\*,取地址的时候只取一个字节判断存储方式
	if (\*(&a) == 1)
	{
		printf("小端字节序");
	}
	else
	{
		printf("大端字节序");
	}
	return 0;
}

3.练习题

练习题1

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

解析:
输出结果为:a=-1,b=-1,c=255。
-1是整型,存放到内存中的补码是32位bit。
10000000000000000000000000000001 ----- -1的原码
11111111111111111111111111111111 ----- -1的补码
a是char类型的,在内存中只占1个字节,即8bit,截断后得到a的补码
11111111 ------ a的补码
但是最后输出的时候是以%d的形式输出的,所以要进行整型提升,将a的补码整型提升后与-1的补码相同。
整型提升:有符号整型提升的时候高位补符号位,符号位为0,高位补0;符号位为1,高位补1。
无符号整型提升的时候,高位补0。
a进行整型提升后a的补码和-1的补码相同,转换成原码输出是a=-1,signed char b = -1char a = -1相同,故不做赘述。
unsigned char c = -1中c是无符号整型,在进行整型提升之后得到c的补码为
00000000000000000000000011111111 ----- c的补码
因为c是无符号整型,所以c的补码就是c的原码
00000000000000000000000011111111 ----- c的原码
转换成十进制数字之后就是255。

signed char和char的取值范围相同都是-128 ~ 127;unsigned char的取值范围是0 ~ 128,无符号数的最高位代表数值位。

signed char

-128的原码为110000000,转换成补码之后为110000000,存放到signed char中要发生截断,截断后为10000000。

练习题2

#include <stdio.h>
int main()
{
	char a = -128;
	printf("%u\n", a);//%u表示打印的是一个无符号整型,认为内存中存放的补码是一个无符号数
	return 0;
}

解析
输出结果为4294967168。
char类型的-128在内存中的补码是10000000,最后输出的是无符号整型,要进行整型提升,因为是有符号数,在进行整型提升的时候高位补1,整型提升后为
11111111111111111111111110000000 — 补码
%u打印的是无符号数,所以补码等于原码,打印出来的结果为4294967168。

练习题3

#include <stdio.h>
int main()
{
	char a = 128;
	printf("%u\n", a);//%u表示打印的是一个无符号整型,认为内存中存放的补码是一个无符号数
	return 0;
}

解析:
输出结果为4294967168。
128看做是int类型的话,原码是00000000000000000000000010000000,正数的原反补相同所以补码为
00000000000000000000000010000000 — 补码
但是a为char类型,截断后为10000000,最后输出的为无符号整型,按照符号位进行整型提升后补码为11111111111111111111111110000000,无符号数的原反补相同,所以原码也为11111111111111111111111110000000,最后输出结果为4294967168。

练习题4

#include <stdio.h>
int main()
{
	int i = -20;
	unsigned int j = 10;
	printf("%d", i + j);
	return 0;  
}

解析:
输出结果为-10。
10000000000000000000000000010100 — -20的原码
11111111111111111111111111101100 ----- -20的补码

00000000000000000000000000001010 — 10的原码
00000000000000000000000000001010 ---- 10的补码,因为10为正数所以原码和补码相同
在内存中是以补码进行计算的,所以i和j相加,是以补码计算。
11111111111111111111111111101100 ----- -20的补码
00000000000000000000000000001010 ---- 10的补码
计算之后得到
11111111111111111111111111110110 — 计算得到的补码
10000000000000000000000000001010 -----原码
将原码转换成十进制得到数字-10,所以最后结果为-10。

练习题5

#include <stdio.h>
#include <Windows.h>
int main()
{
	unsigned int i ;
	for (i = 9; i >= 0; i--)
	{
		printf("%u\n", i);
		Sleep(100);
	}
	return 0;
}

解析:
输出结果为无限循环。
unsigned int为无符号数,取值范围为0~4294967295;始终满足循环条件中i>=0,所以刚开始会从9开始输出,到0之后,i就会变成最大数开始输出,如此循环……

练习题6

#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;
}

解析:
输出结果为255。
char的取值范围为-128 ~ 127。strlen函数遇见\0停止,计算的是\0之前的字符个数。\0的ASCII值为0,所以strlen遇见0就会停止。i=0时,a[0]=-1;i=1时,a[i]=-1-1可以看成-1+(-1),-1与-1的补码相加得到的补码为10000000000000000000000000000010,转换成char类型的,要发生截断,截断后为10000010,转换成十进制为-2,a[1]=-2……当a[i]=0时,strlen函数停止,而a[i]为char类型的,取值范围为-128 ~ 127,所以最后输出结果为255。

练习题7

#include <stdio.h>
unsigned char i = 0;
int main()
{
	for (i = 0; i <= 255; i++)
	{
		printf("hello world\n");
	}
	return 0;
}

解析:
输出结果为无限循环hello world。
因为unsigned char的取值范围为0 ~ 255,相当于循环中i<=255恒成立。

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

ed char的取值范围为0 ~ 255,相当于循环中i<=255`恒成立。

[外链图片转存中…(img-JovTojnB-1714985303042)]
[外链图片转存中…(img-r2x3g45b-1714985303043)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值