網(wǎng)站首頁 編程語言 正文
內(nèi)存簡(jiǎn)單介紹
大家肯定經(jīng)常聽說內(nèi)存這個(gè)詞,內(nèi)存到底是什么呢? 在計(jì)算機(jī)中,進(jìn)程都要加載進(jìn)內(nèi)存中,也是我們各種數(shù)據(jù)的流通途徑,C語言中,大家肯定都知道指針變量,指針變量中保存的就是內(nèi)存的地址,那么,什么是內(nèi)存的地址呢?
內(nèi)存的單位是字節(jié)
對(duì)于32位的機(jī)器,有32根地址線,每根地址線在尋址時(shí),產(chǎn)生的高低電壓分別為0/1,那么32根地址線產(chǎn)生的地址就會(huì)是
00000000000000000000000000000000
00000000000000000000000000000001
00000000000000000000000000000010
…
11111111111111111111111111111111
這里就有2^32次方個(gè)地址
大家應(yīng)該知道,還有64位的機(jī)器,64根地址線又有多少個(gè)地址呢,大家可以計(jì)算一下
在32位的機(jī)器上,地址是32個(gè)0或者1組成二進(jìn)制序列,那地址就得用4個(gè)字節(jié)的空間來存儲(chǔ),
所以一個(gè)指針變量的大小就應(yīng)該是4個(gè)字節(jié)。 那如果在64位機(jī)器上,有64個(gè)地址線,那一個(gè)指針變量的大小是8個(gè)字節(jié),才能存放一個(gè)地址。
這串編號(hào)就是內(nèi)存單元的地址,就像酒店的房間號(hào)一樣,對(duì)應(yīng)著內(nèi)存中的一個(gè)字節(jié)大小的房間
我們?cè)趘s中來看一下
這里是以十六進(jìn)制的方式展示的,大家也知道,32個(gè)數(shù)字看起來太長(zhǎng)了。
整數(shù)與字符在內(nèi)存中的存儲(chǔ)
關(guān)于c語言中的數(shù)據(jù)類型,大家在寫了這么多代碼后肯定也很清楚了,C語言中有整型、浮點(diǎn)型、字符型、等等
我們來研究一下整數(shù)在內(nèi)存中是如何存儲(chǔ)的
大家都知道,定義變量,會(huì)在內(nèi)存中開辟空間來存儲(chǔ)
int a = 20;
int類型在vs中占據(jù)4個(gè)字節(jié)的空間,那么如何存儲(chǔ)呢?
這就涉及到原碼反碼補(bǔ)碼的概念
- 計(jì)算機(jī)中的整數(shù)有三種2進(jìn)制表示方法,即原碼、反碼和補(bǔ)碼。
- 三種表示方法均有符號(hào)位和數(shù)值位兩部分,符號(hào)位都是用0表示“正”,用1表示“負(fù)”,剩下數(shù)值位
- 正數(shù)的原、反、補(bǔ)碼都相同。
- 負(fù)整數(shù)的三種表示方法各不相同。
原碼:
直接將數(shù)值按照正負(fù)數(shù)的形式翻譯成二進(jìn)制就可以得到原碼
反碼:
將原碼的符號(hào)位不變,其他位依次按位取反就可以得到反碼
補(bǔ)碼:
反碼+1就得到補(bǔ)碼。
對(duì)于整型變量來說,內(nèi)存中存放的其實(shí)是補(bǔ)碼
使用補(bǔ)碼,可以將符號(hào)位和數(shù)值域統(tǒng)一處理,加法和減法也可以同一處理,因?yàn)镃PU只有加法器
eg:
int a = 20;
//原碼 : 直接寫二進(jìn)制 00000000000000000000000000010100
//反碼--補(bǔ)碼 -- 正數(shù)的原反補(bǔ)相同
int b = -5;
//原碼: 10000000000000000000000000000101
//反碼:符號(hào)位不變,按位取反
// 11111111111111111111111111111010
//補(bǔ)碼:反碼+1
// 11111111111111111111111111111011 -- -5的補(bǔ)碼
上邊可以看見-5的補(bǔ)碼是11111111111111111111111111111011 ,我們?nèi)绾未_認(rèn)呢?
轉(zhuǎn)換成16進(jìn)制為fffffffb
大家可以看到確實(shí)是使用補(bǔ)碼存儲(chǔ)的,但是為什么是倒著存儲(chǔ)的,后邊再來說,這是由于大小端的問題
給大家舉幾個(gè)例子,不知道存儲(chǔ)無法分析出結(jié)果代碼
//1.
#include <stdio.h>
int main()
{
char a = -128;
printf("%u\n",a);
//-128 原碼: 10000000 00000000 00000000 10000000
//-128 反碼: 11111111 11111111 11111111 01111111
//-128 補(bǔ)碼: 11111111 11111111 11111111 10000000
//存在a里面的,因?yàn)橹挥幸粋€(gè)字節(jié) 10000000
//所以會(huì)當(dāng)做無符號(hào)整數(shù)打印 --整形提升
//提升為 11111111111111111111111110000000
//所以結(jié)果是一個(gè)很大的整數(shù),大家可以嘗試一下
return 0;
}
#include <stdio.h>
int main()
{
char a = 128;
//128的原碼反碼補(bǔ)碼 : 00000000000000000000000010000000
//存在a里的:10000000
//整形提升 :11111111111111111111111110000000
//所以結(jié)果還是那個(gè)很大的整數(shù)
printf("%u\n",a);
return 0;
例子就簡(jiǎn)單給大家舉到這里,大家一定要記住整數(shù)在內(nèi)存中是以補(bǔ)碼的形式存儲(chǔ)的
字符存儲(chǔ)的是ASCII碼,所以和整數(shù)同
浮點(diǎn)數(shù)在內(nèi)存中的存儲(chǔ)
我們來看一下浮點(diǎn)數(shù)在內(nèi)存中的存儲(chǔ)
拋磚引玉:
#include <stdio.h>
int main()
{
int n = 9;
float *pFloat = (float *)&n;
printf("n的值為:%d\n",n);
printf("*pFloat的值為:%f\n",*pFloat);
*pFloat = 9.0;
printf("num的值為:%d\n",n);
printf("*pFloat的值為:%f\n",*pFloat);
return 0;
}
上面代碼的打印結(jié)果到底是什么呢?
是不是非常出乎大家的意料呢,這里就可以看出,浮點(diǎn)數(shù)的存儲(chǔ)肯定和整數(shù)是不同的。那浮點(diǎn)數(shù)到底咋存的呢?
根據(jù)國(guó)際標(biāo)準(zhǔn)IEEE(電氣和電子工程協(xié)會(huì)) 754,任意一個(gè)二進(jìn)制浮點(diǎn)數(shù)V可以表示成下面的形式:
- (-1)^S * M * 2^E
- (-1)^S表示符號(hào)位,當(dāng)S=0,V為正數(shù);當(dāng)S=1,V為負(fù)數(shù)。
- M表示有效數(shù)字,大于等于1,小于2。
- 2^E表示指數(shù)位。
看得很迷糊,直接上例子
v = 5.5
= 101.1 --二進(jìn)制表示
= 1.011 * 2 ^2 – 科學(xué)記數(shù)法表示
因?yàn)槭钦龜?shù) s = 0
m = 1.011
e = 2
IEEE 754規(guī)定:
對(duì)于32位的浮點(diǎn)數(shù),最高的1位是符號(hào)位s,接著的8位是指數(shù)E,剩下的23位為有效數(shù)字M。
IEEE 754對(duì)有效數(shù)字M和指數(shù)E,還有一些特別規(guī)定
1=<M <2 ,所以M可以寫成1.xxxxx 所以可以舍去1 ,只存儲(chǔ)xxxxxx
IEEE 754規(guī)定,在計(jì)算機(jī)內(nèi)部保存M時(shí),默認(rèn)這個(gè)數(shù)的第一位總是1,因此可以被舍去,只保存后面的xxxxxx部分。比如保存1.01的時(shí)候,只保存01,等到讀取的時(shí)候,再把第一位的1加上去。這樣做的目的,是節(jié)省1位有效數(shù)字。以32位浮點(diǎn)數(shù)為例,留給M只有23位,將第一位的1舍去以后,等于可以保存24位有效數(shù)字
對(duì)于指數(shù)E,E是一個(gè)無符號(hào)整數(shù),但是科學(xué)記數(shù)法指數(shù)是可以出現(xiàn)負(fù)數(shù)的,所以
IEEE 754規(guī)定了偏移量,如果E為8位,則加上127 ,如果E為11位,則加上1023
我們舉個(gè)例子
float f = 8.5f;
//二進(jìn)制 : 1001.1
//科學(xué)計(jì)數(shù)法表示: 1.0011*2^3
//S = 0 M = 1.0011 ?E = 3
//存儲(chǔ)進(jìn)去的應(yīng)該是:
? ? 0 10000010 00110000000000000000000
//我們可以驗(yàn)證一下
? ? ? ? 轉(zhuǎn)換成16進(jìn)制
?0100 0001 0001 1000 0000 0000 0000 0000
//41 18 00 00
我們來看一下代碼
和我們想的一樣
我們?cè)賮砼e一個(gè)負(fù)數(shù)的例子
float t = -3.5f;
//二進(jìn)制: 11.1
//科學(xué)記數(shù)法: 1.11*2^1
//S = 1 M = 1.11 E = 1
//存儲(chǔ):
1 ?10000000 11000000000000000000000
//轉(zhuǎn)換成16進(jìn)制
1100 0000 0011 0000 0000 0000 0000 0000
c0 60 00 00
那我們從內(nèi)存中讀取出來的二進(jìn)制位如何解析成浮點(diǎn)數(shù)呢
關(guān)于E,有三種情況
E不全為0或不全為1
這時(shí),浮點(diǎn)數(shù)就采用下面的規(guī)則表示,即指數(shù)E的計(jì)算值減去127(或1023),得到真實(shí)值,再將有效數(shù)字M前加上第一位的1
2.E全為0
這時(shí),浮點(diǎn)數(shù)的指數(shù)E等于1-127(或者1-1023)即為真實(shí)值,
有效數(shù)字M不再加上第一位的1,而是還原為0.xxxxxx的小數(shù)。這樣做是為了表示±0,以及接近于 0的很小的數(shù)字。
3.E全為1
這時(shí),如果有效數(shù)字M全為0,表示±無窮大(正負(fù)取決于符號(hào)位s);
我們來分析一下最開始的題目
浮點(diǎn)數(shù)和整數(shù)的存儲(chǔ)就介紹到這里了,有哪里不清楚的朋友可以私信我
大小端存儲(chǔ)模式及簡(jiǎn)介
上邊有一個(gè)懸念,為什么是倒序存儲(chǔ)的
那什么是大端小端呢?
大端(存儲(chǔ))模式: 數(shù)據(jù)的低位存儲(chǔ)在內(nèi)存的高地址中,數(shù)據(jù)的高位存儲(chǔ)在內(nèi)存的低地址中
小端(存儲(chǔ))模式: 數(shù)據(jù)的低位存儲(chǔ)在內(nèi)存的低地址中,數(shù)據(jù)的高位存儲(chǔ)在內(nèi)存的高地址中
那為什么會(huì)有大小端呢?
內(nèi)存中以字節(jié)為單位,但是比如int 是4個(gè)字節(jié),那如何安排這個(gè)4個(gè)字節(jié)呢?就導(dǎo)致了大小端存儲(chǔ)模式
例如:一個(gè) 16bit 的 short 型 x ,在內(nèi)存中的地址為 0x0010 , x 的值為 0x1122 ,那么 0x11 為高字節(jié), 0x22 為低字節(jié)。對(duì)于大端模式,就將 0x11 放在低地址中,即 0x0010 中, 0x22 放在高地址中,即 0x0011 中。小端模式,剛好相反。我們常用的 X86 結(jié)構(gòu)是小端模式,而 KEIL C51 則 為大端模式。很多的ARM,DSP都為小端模式。有些ARM處理器還可以由硬件來選擇是大端模式 還是小端模式。
那我們?nèi)绾螠y(cè)試當(dāng)前電腦是哪種存儲(chǔ)模式呢?
int main(void)
{
int a = 0x11223344;
return 0;
}
我們調(diào)試打開內(nèi)存看一下
很明顯,數(shù)據(jù)的低位存儲(chǔ)在內(nèi)存低地址中,所以為小端存儲(chǔ)模式。
我們能不能寫一個(gè)程序,直接告訴我們大小端呢?
我們來分析一下
我們來看一下代碼
int decide_byte_orde()
{
int i = 1;
return *(char *)&i;
}
我們來測(cè)試一下
總結(jié)
深入理解數(shù)據(jù)的存儲(chǔ)是非常有必要的,我們之后碰到很多問題都會(huì)豁然開朗,大家一定要好好研究一下
原文鏈接:https://blog.csdn.net/weixin_54580407/article/details/126373774
相關(guān)推薦
- 2023-03-11 Python基礎(chǔ)教程之增加和去除數(shù)字的千位分隔符_python
- 2022-03-29 C#算法之無重復(fù)字符的最長(zhǎng)子串_C#教程
- 2022-05-08 python函數(shù)裝飾器構(gòu)造和參數(shù)傳遞_python
- 2022-09-30 C#?wpf?Grid中實(shí)現(xiàn)控件拖動(dòng)調(diào)整大小的示例代碼_C#教程
- 2022-07-11 Jenkins修改端口號(hào), jenkins容器修改默認(rèn)端口號(hào)
- 2022-09-14 Python深入分析@property裝飾器的應(yīng)用_python
- 2022-11-09 PostgreSQL索引掃描時(shí)為什么index?only?scan不返回ctid_PostgreSQ
- 2024-03-14 ThreadLocal使用,配合攔截器HandlerInterceptor使用
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支