第十六講:數據在內存中的存儲

第十六講:數據在內存中的存儲

  • 1.整數在內存中的存儲
    • 1.1存儲方式
    • 1.2大小端字節序
    • 1.3大小端字節序排序規則
    • 1.4為什么要有大小端
    • 1.5練習
      • 1.5.1練習1
      • 1.5.2練習2
      • 1.5.3練習3
      • 1.5.4練習4
      • 1.5.5練習5
      • 1.5.6練習6
      • 1.5.7練習7
  • 2.浮點數在內存中的存儲
    • 2.1練習
    • 2.2浮點數的存儲
    • 2.3浮點數的存儲過程
      • 2.3.1符號位的存儲
      • 2.3.2對于有效數字M的存儲
      • 2.3.3對于指數E的存儲
        • 2.3.3.1E不全為0或不全為1
        • 2.3.3.2E全為0
        • 2.3.3.3E全為1
    • 2.4題目解析

這一講分別介紹了整數和浮點數在內存中的存儲方式,以及一些題目的解析

1.整數在內存中的存儲

1.1存儲方式

數據在內存中的存儲都是以其二進制位來表示的,而整數的二進制位的表示方法有三種:原碼、反碼、補碼,在內存中,存儲的是正數的補碼
正數的原碼、反碼、補碼相同
負數的三種表示方式各不相同

那么為什么整數存儲的是補碼呢?

1.CPU只有加法器,使用補碼能夠將字符位和數值位統一處理
2.原碼和補碼進行轉換的過程是相同的,不需要額外的硬件電路便可以實現

1.2大小端字節序

當我們對于一個整數變量進行內存監視時,常常會觀察到整數的存放順序和我們創建的變量值順序是不同的,例如:
在這里插入圖片描述
當我們創建了一個a變量時,它在內存中的存儲為(VS編譯器):
在這里插入圖片描述
可以看見,它是倒著存的,不是正著存的,這就涉及到了整數存儲順序的兩種方式:大端存儲和小端存儲

1.3大小端字節序排序規則

大端排序方式:

低位的字節放在高地址,高位的字節放在低地址

小端排序方式:

低位的字節放在低地址,高位的字節放在高地址

我們畫圖來解析:
在這里插入圖片描述

1.4為什么要有大小端

我們常?的 X86 結構是?端模式,?KEIL C51 則為?端模式。很多的ARM,DSP都為?端模式。有些ARM處理器還可以由硬件來選擇是?端模式還是?端模式。

但是為什么要有著大小端的存在呢?

1.C語言有著多種類型的數(float、 int、char),對于字節安排的問題,必然要被得到處理
2.不同的硬件設計可能導致不同的存儲方式,硬件的設計使用某種存儲方式可能會優化性能或簡化設計
3.不同的存儲方式可能對性能有不同的影響

1.5練習

總結:
1.整數在運算時(整型提升、加法、減法)都是補碼在進行運算
2.對于整數的打印,分為兩種情況:

1.打印有符號整數,如果需要整形提升,那么補的是符號位,將補碼轉換成原碼,進行計算之后將得出值進行打印
2.打印無符號整數,如果需要整形提升,如果最高位為1,補1,為0,補0,和有符號整數相同,但是計算結果時看的是補碼,因為無符號整數的原碼、反碼、補碼相同

1.5.1練習1

//設計?個?程序來判斷當前機器的字節序。
//
//設計思路:
//創建一個a變量,賦值為1,其在內存中的存儲應該為00 00 00 01
//①如果為小端存儲:存儲方式應該為01 00 00 00
//②如果為大端存儲:存儲方式為00 00 00 01
//分別拿出它們首個字節,如果值為1,就是小端存儲,如果值位0,就為大端存儲
//
//代碼1:
int main2()
{int a = 1;if (*((char*)&a))  //注意:這里為&a,因為只能將地址強轉成(char*)類型的指針,否則可能會出現越界訪問printf("小端存儲\n");elseprintf("大端存儲\n");return 0;
}//代碼2:
int DefA()
{int a = 1;return *((char*)&a);
}int main()
{int ret = DefA();if (ret)printf("小端存儲\n");elseprintf("大端存儲\n");return 0;
}

1.5.2練習2

//1.5.2練習2
int main()
{char a = -1;//對于char類型的變量,它可能時signed char類型,也可能是unsigned char類型,具體取決于編譯器,這里是有符號類型//char類型為一個字節,此時,-1的2進制表示為10000001,它的補碼為:11111111//因為-1為整形,所以它的補碼結果為:11111111111111111111111111111111,而a為char類型,char類型只能存儲11111111,所以對于a://補碼:11111111,由于要進行打印,發生整形提升,結果為11111111111111111111111111111111//原碼:10000000000000000000000000000001//所以結果為-1signed char b = -1;//char類型在此編譯器下就是有符號類型的,所以對于有符號類型的char分析和上面一樣//所以結果為-1unsigned char c = -1;//對于無符號類型,仍為1個字節,所以a還是11111111//整型提升結果為00000000000000000000000011111111,因為對于無符號整形,整形提升加0//此時符號位為0,所以原碼和補碼相同,計算的結果為255printf("a=%d,b=%d,c=%d", a, b, c);//以%d形式打印,表示打印有符號整數return 0;
}

1.5.3練習3

//1.5.3練習3
int main()
{char a = -128;//-128,原碼為10000000000000000000000001000000,反碼為11111111111111111111111110111111,補碼為11111111111111111111111111000000//所以a里存的是11000000//要打印的是無符號整形,進行整形提升,結果為11111111111111111111111111000000//所以結果為4294967168printf("%u\n", a);return 0;
}

1.5.4練習4

//1.5.4練習4
int main()
{char a = 128;//對于128,它的原碼為00000000000000000000000010000000//反碼:01111111111111111111111101111111//補碼:01111111111111111111111110000000//存儲到a里,結果為10000000//打印無符號整形,整形提升//補碼:11111111111111111111111110000000printf("%u\n", a);return 0;
}

但是,看練習4,當我們要將128這個值存到char類型中時,a為10000000,這顯然就是-128呀!這是因為char類型的取值范圍為-128 - 127,128根本存不下,這時存儲遵循一個規律:
在這里插入圖片描述
所以我們可以將他們看成一個循環,對于其他類型的整數(float、int)也是如此

1.5.5練習5

//1.5.5練習5
#include <string.h>int main()
{char a[1000];int i;for (i = 0; i < 1000; i++){a[i] = -1 - i;//strlen是求字符串長度的函數,遇到\0會停止//對于一個char類型的數組,里面放的元素為char類型//而我們已經了解到了,char類型的取值范圍為-128 - 127//所以a數組中放的值只能為:-1 -2 -3 ... -127 -128 127 126 ... 2 1 0這些ASCII碼值對應的字符//遇到\0停止,所以結果為255}printf("%zd", strlen(a));return 0;
}

1.5.6練習6

//1.5.6練習6
unsigned char i = 0;int main()
{for (i = 0; i <= 255; i++){//無符號char類型的取值范圍為0-255,所以會一直滿足循環條件,會一直循環進行打印printf("hello world\n");}return 0;
}
#include <windows.h>int main()
{unsigned int i;for (i = 9; i >= 0; i--){//對于無符號int類型,他所有的位都會被當成數值位,所以它不會出現負數的情況//所以它會一直滿足條件,一直進行打印printf("%u\n", i);Sleep(100);}return 0;
}

1.5.7練習7

//1.5.7練習7
//X86環境 ?端字節序
int main()
{int a[4] = { 1, 2, 3, 4 };int* ptr1 = (int*)(&a + 1);//&a取出的是整個數組的地址,+1表示緊挨著數組的那塊地址,將其強轉成int*類型的指針賦給ptr1//*(ptr-1)得到的就是4int* ptr2 = (int*)((int)a + 1);//a表示首元素的地址,將其轉換成int類型,表示的是一個數!,+1表示地址加1,直接+1就可以了//但是要注意:每一個字節都有一個指針,+1表示的是向后偏移一個字節//對于a,在內存中的存儲為0x 01 00 00 00 02 00 00 00 ...(因為為小端存儲),向后偏移一個字節,就變成了://00 00 00 02 00 00 00//對他解引用,訪問4個字節,所以找到了00 00 00 02,因為為小端存儲,所以結果為02000000printf("%x,%x", ptr1[-1], *ptr2);return 0;
}

2.浮點數在內存中的存儲

2.1練習

浮點數的存儲和整形的存儲是不一樣的,下面我們就通過一個練習來直觀地感受一下:

//2.1練習
int main()
{int n = 9;float* pFloat = (float*)&n;printf("n的值為:%d\n", n);//9printf("*pFloat的值為:%f\n", *pFloat);//0.000000*pFloat = 9.0;printf("num的值為:%d\n", n);//1091567616printf("*pFloat的值為:%f\n", *pFloat);//9.000000return 0;
}

2.2浮點數的存儲

既然知道了整形和浮點型的不同,那么浮點數是怎么存儲的呢?

根據國際標準IEEE(電氣和電子工程協會) 754,任意?個?進制浮點數V可以表示成下?的形式:
在這里插入圖片描述
我們通過舉例來說:

//2.2浮點數的存儲
int main11()
{float a = 5.5;//我們來探討5.5在內存中的存儲形式://5的二進制表示為101.1,寫成科學計數法的形式為1.011 * 10^2//所以符號位S為0(因為為整數)//指數位E = 2//數值位為M = 1.011return 0;
}

如果沒有看懂,我們通過圖像來直觀感受:
在這里插入圖片描述

我們可以簡單理解S、E、M這三個值如上

IEEE 745規定:
1.對于32位的浮點數,最高的一位存儲的是符號位,接著八位存儲指數E,剩下32位存儲有效數字M
2.對于64位的浮點數,最高的一位存儲的是符號位,接著十一位存儲指數E,剩下52位存儲有效數字M

在這里插入圖片描述

2.3浮點數的存儲過程

2.3.1符號位的存儲

符號位的存儲只占據一個字節,很簡單,是正數就是0,是負數就是1

2.3.2對于有效數字M的存儲

其實M的取值范圍為1<=M<2,也就是說,M總是可以表示成1…的形式,所以IEEE 754規定,在計算機保存M時,只保存小數點后邊的部分,前邊的1舍去,等到讀取的時候,再將1加上去,這樣就節省了一位有效數字,使得精度更高,比如:1.01在進行存儲時,只存儲01,讀取時再將1加上;0.10可以表示成1.0 * 10的負一次冪,所以說它在存儲時存儲0就可以了,需要注意的是:它要在后邊補0,也就是說對于1.1,在存儲時存儲的是01000000000000000000000

2.3.3對于指數E的存儲

對于指數的存儲比較復雜,分為三種情況討論:

2.3.3.1E不全為0或不全為1

因為E的值可能為負數,為了將負數表示出來,我們需要將E的值加上127(在32位機器上,偏移值為127,在64位機器上,偏移值為1023),再將其轉換成二進制存儲即可,這樣即可以通過比較指數的大小來判斷兩個浮點數的大小關系,同時也可以方便地進行加減乘除等計算操作

這種情況為正常情況,比如0.5的二進制表示形式為0.1,也就是1.0 * 10的負一次冪,在存儲數值位時要將數值位的一忽略,所以存儲時存儲的就是0,補齊23位,也就是00000000000000000000000,指數位值為-1,加上127為126,二進制表示為01111110,符號位為0,所以0.5的二進制表示為:

0 01111110 00000000000000000000000
2.3.3.2E全為0

當E全為0時,指數位的值為1-127(它是規定好的),而且此時數值位在進行復原時,補的不是1了,而是0,此時表示的是一個無限接近于0的一個小數

2.3.3.3E全為1

這時表示的是一個無窮大的數

2.4題目解析

//2.4題目解析
int main()
{int n = 9;float* pFloat = (float*)&n;printf("n的值為:%d\n", n);//n本來就是一個int類型的數,進行打印,結果為9printf("*pFloat的值為:%f\n", *pFloat);//對于9://原碼:00000000000000000000000000001001//對于一個float類型的數,因為要解引用,拿到的是原碼://符號位:0 - 正數//數值位:00000000000000000001001 - 0.00000000000000000001001//指數位:00000000 - 原碼為00000000 - 1-127 = -126//所以值為0.00000000000000000001001 * 10的-126次冪//他表示0.0000000000000000000...1001是一個很小的數//盡管要拿出來,拿出的也只是0.000000,所以結果為0.000000*pFloat = 9.0;//9的二進制表示1001.0 - 1.001 * 10 ^ 3//符號位:0 - 正數//數值位:1.001 - 00100000000000000000000 - 注意:要在后邊補0//指數位:3 + 127 = 130 - 10000010//全部 —— 0 10000010 00100000000000000000000 - 1,091,567,616printf("num的值為:%d\n", n);全部 —— 0 10000010 00100000000000000000000 - 1,091,567,616printf("*pFloat的值為:%f\n", *pFloat);//直接打印出9.000000即可return 0;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/15059.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/15059.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/15059.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

如何將數據從一部手機傳輸到另一部手機[安全快速]

概括 手機之間無需使用藍牙即可傳輸文件&#xff0c;配合專業的文件傳輸工具更高效。本文將向您介紹幾種使用不同的數據傳輸工具快速安全地將數據從一部手機傳輸到另一部手機的方法&#xff0c;特別是當有大文件或大量文件時。現在&#xff0c;我們來看看。 需要在朋友或家人之…

Rust 賦能前端 -- 寫一個 File 轉 Img 的功能

所有耀眼的成績,都需要苦熬,熬得過,出眾;熬不過,出局 大家好,我是柒八九。一個專注于前端開發技術/Rust及AI應用知識分享的Coder 此篇文章所涉及到的技術有 Rustwasm-bindgen/js-sys/web-sysWeb WorkerWebAssemblyWebpack/Vite配置WebAssemblyOffscreenCanvas腳手架生成項…

校園二手書交易|基于SprinBoot+vue的校園二手書交易管理系統(源碼+數據庫+文檔)

校園二手書交易管理系統 目錄 基于SprinBootvue的校園二手書交易管理系統 一、前言 二、系統設計 三、系統功能設計 1系統功能模塊 2管理員功能模塊 3 賣家用戶功能模塊 4 用戶功能模塊 四、數據庫設計 五、核心代碼 六、論文參考 七、最新計算機畢設選題推薦 八…

Linux:iptables防火墻部署優化之連接轉移(目的地地址轉化)

Linux&#xff1a;iptables防火墻部署優化之連接轉移&#xff08;目的地地址轉化&#xff09; 文章目錄 Linux&#xff1a;iptables防火墻部署優化之連接轉移&#xff08;目的地地址轉化&#xff09;node1操作檢測ip情況關閉firewalld防火墻服務&#xff0c;并鎖定該服務開啟ip…

什么是分布式會話

分布式會話是指在分布式系統中實現用戶會話管理的一種機制。在傳統的單服務器架構中&#xff0c;用戶的會話數據通常存儲在單個服務器或應用服務器的內存中。然而&#xff0c;隨著業務的發展和用戶量的增加&#xff0c;單服務器架構往往無法滿足高可用性和高并發的需求&#xf…

Minio WebUploader上傳文件的高級用法之進度條顯示、文件過濾、圖片預覽、圖片壓縮

系列文章目錄 第十章 Minio WebUploader上傳文件的高級用法之進度條顯示、文件過濾、圖片預覽、圖片壓縮 Minio WebUploader上傳文件的高級用法之進度條顯示、文件過濾、圖片預覽、圖片壓縮 系列文章目錄進度條顯示文件過濾圖片預覽圖片壓縮 進度條顯示 使用進程文件上傳時&a…

基于springboot+html的二手交易平臺(附源碼)

基于springboothtml的二手交易平臺 介紹部分界面截圖如下聯系我 介紹 本系統是基于springboothtml的二手交易平臺&#xff0c;數據庫為mysql&#xff0c;可用于畢設或學習&#xff0c;附數據庫 部分界面截圖如下 聯系我 VX&#xff1a;Zzllh_

java Iterable和 Iterator接口區別和聯系

Iterable 和 Iterator 是 Java 集合框架中用于遍歷集合元素的兩個接口,它們之間既有區別也有聯系。下面詳細介紹它們的區別和聯系。 Iterable 接口 定義 Iterable 接口位于 java.lang 包中,定義如下: public interface Iterable<T> {Iterator<T> iterator()…

在家庭影院音頻中應用的D類音頻放大器

家庭影院的主要組成部分包括顯示設備、音響設備、信號源和接線設備等。家庭影院的音響信號需要進行處理和輸出&#xff0c;以獲得高質量的音效。音響設備通常需要一臺功率適當的數字、模擬混合的處理器&#xff0c;對音源進行降噪、均衡、擴展等處理操作&#xff0c;以達到高品…

核心交換機與終端通信正常,接入交換機無法Ping通同一VLAN內終端

環境: 思科3560交換機 問題描述: 核心交換機與PC通信正常,接入交換機無法Ping通同一VLAN內PC h3c核心交換機配置vlan2 vlanif2 IP192.168.1.1 下掛接入交換機配置了vlan2 pc接到接入交換機25口這個端口配置access vlan2,pc的ip是192.168.1.3從 核心交換機上ping192.168.…

【智能算法應用】北方蒼鷹算法求解二維柵格路徑規劃問題

目錄 1.算法原理2.二維路徑規劃數學模型3.結果展示4.參考文獻5.代碼獲取 1.算法原理 【智能算法】北方蒼鷹優化算法&#xff08;NGO)原理及實現 2.二維路徑規劃數學模型 柵格法模型最早由 W.E. Howden 于 1968 年提出&#xff0c;障礙物的柵格用黑色表示&#xff0c;可通過的…

ping 探測網段哪些地址被用

#!/bin/bash# 遍歷192.168.3.1到192.168.3.254 for i in {1..254} doip"192.168.3.$i"# 對每個IP地址進行三次ping操作if ping -c 3 -W 1 $ip > /dev/null 2>&1thenecho "$ip: yes"fi done$ sh test.sh 192.168.3.1: yes 192.168.3.95: yes 192.…

使用Word表格數據快速創建圖表

實例需求&#xff1a;Word的表格如下所示&#xff0c;標題行有合并單元格。 現在需要根據上述表格數據&#xff0c;在Word中創建如下柱圖。如果數據在Excel之中&#xff0c;那么創建這個圖并不復雜&#xff0c;但是Word中就沒用那么簡單了&#xff0c;雖然Word中可以插入圖表&a…

014_C標準庫函數之<stdio.h>

【背景】 今天這個主要說的是<stdio.h>頭文件&#xff0c;大家眾所周知&#xff0c;這個是我們學習C語言時第一個接觸到的頭文件了&#xff0c;那么為什么我不一開始就介紹這個頭文件呢&#xff1f;我覺得有兩個原因&#xff0c;如下&#xff1a; 1.一開始大家的編程思…

LeetCode/NowCoder-鏈表經典算法OJ練習3

孜孜不倦&#xff1a;孜孜&#xff1a;勤勉&#xff0c;不懈怠。指工作或學習勤奮不知疲倦。&#x1f493;&#x1f493;&#x1f493; 目錄 說在前面 題目一&#xff1a;返回倒數第k個節點 題目二&#xff1a;鏈表的回文結構 題目三&#xff1a;相交鏈表 SUMUP結尾 說在前…

Pytorch: 解決因pytorch版本不同 導致訓練ckpt加載失敗

大家都會遇到在工程項目實施階段&#xff0c;如果訓練的模型文件在不同的torch版本環境下部署時&#xff0c;會報錯~。 報錯舉例 # 查看torch環境 import torch print(torch.__version__)# 訓練時環境&#xff1a;torch 1.8.2cu111 # 部署時環境&#xff1a;torch 1.4.0torch.…

dcatAdmin框架 使用phpword 生成word文件

下載phpword插件 composer require phpoffice/phpword 生成word文件接口 static public function word(){//接收傳值$order_id request()->get(order_id);$tpl_id request()->get(tpl_id);//查詢出對應的數據以及關聯數據$sale_order \App\Models\SaleOrder::with([…

Python異步編程之基礎概念

Python異步編程之基礎概念 在現代編程中&#xff0c;異步編程是一種重要的技術&#xff0c;尤其是在處理I/O密集型任務時&#xff0c;異步編程可以大大提高程序的性能和響應速度。本文將介紹Python異步編程的基礎概念&#xff0c;幫助你理解其原理和應用。 什么是異步編程&am…

【代碼隨想錄算法訓練營第37期 第十七天 | LeetCode110.平衡二叉樹、257. 二叉樹的所有路徑、404.左葉子之和】

代碼隨想錄算法訓練營第37期 第十七天 | LeetCode110.平衡二叉樹、257. 二叉樹的所有路徑、404.左葉子之和 一、110.平衡二叉樹 解題代碼C&#xff1a; /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *righ…

三、NVIDIA Jetson Orin開發板-GPU加速

一、NVIDIA Jetson Orin開發板的硬件情況 df -h#查看操作系統情況Filesystem Size Used Avail Use% Mounted on **/dev/nvme0n1p1** 234G 17G 208G 8% / none 7.4G 0 7.4G 0% /dev tmpfs 7.6G 0 7.6G 0% /dev/shm tmpfs …