C語言指針及占據記憶體空間

来源:https://www.cnblogs.com/l-hh/archive/2020/02/09/12288613.html
-Advertisement-
Play Games

第一、瞭解記憶體空間 本文章文字有點多,會有點枯燥,配合圖文一起看可以緩解枯燥,耐心閱讀哦!!! 先瞭解記憶體地址,才更好的理解指針! 我們可以把記憶體想象為成一列很長很長的貨運火車,有很多大小相同的車廂,而每個車廂正好相當於在記憶體中表示一個位元組。這些車廂裝著不同的貨物,就像我們的記憶體要存著各式各樣的數據 ...


第一、瞭解記憶體空間

本文章文字有點多,會有點枯燥,配合圖文一起看可以緩解枯燥,耐心閱讀哦!!!

先瞭解記憶體地址,才更好的理解指針!

我們可以把記憶體想象為成一列很長很長的貨運火車,有很多大小相同的車廂,而每個車廂正好相當於在記憶體中表示一個位元組。這些車廂裝著不同的貨物,就像我們的記憶體要存著各式各樣的數據。

多啰嗦一下

我們平時在電腦上能夠聽音樂、看視頻和文章,其實看到的這些東西就是記憶體中每個“車廂”裡面的數據,這些數據最終還是由二進位0/1演變而成。

雖然視頻、文章、音樂等這些信息在我們眼裡是不同的,但對於電腦來說它們在記憶體中都是以二進位的形式來表示。

因為我們要知道去哪存或取數據,所以記憶體中每個位元組都有對應的編號,就像火車上的車廂編號一樣。而這個記憶體中每個位元組的編號就是我們常說的記憶體地址,是按一個位元組接著一個位元組的次序進行編址。如下圖所示:

凡事多問幾個為什麼?

1. 為什麼記憶體地址都有0x開頭?

0x 開頭代表以十六進位來表示的意思。

2. 為什麼我們平時看到記憶體地址是這樣的呢?如圖:

因為記憶體容量很大,容量大位元組數自然也多了,所以需要更多位來編址記憶體地址。上圖的(0x00 ...)記憶體地址這裡只是便於理解!

3. 為什麼我那麼菜呢?

哈哈哈......你心裡沒點*數嗎?

關於記憶體位元組

  • 1個記憶體地址只存1個位元組 (Byte);
  • 1個位元組等於8位二進位,每一位二進位的0或1,叫“比特”(bit);
  • 比特是最小單位,位元組是比特的集合,也是一個單位;

記憶體給數據類型地址分配如下:

  • char:占一個位元組分配一個地址;
  • int: 占四個位元組分配四個地址;
  • 還有long、float、double等類型,等著你來動手測試。

可以使用sizeof進行驗證:

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

 結果如下:

第二、理解指針

不要把指針想得太複雜,指針的實質就是記憶體“地址”,可以說指針就是地址,其實指針就是保存地址的變數。

拿普通變數跟指針變數做比較:

char a;     // 定義一個變數a,用於保存char類型的數據;
char *b;    // 定義一個指針變數b,用於保存一個記憶體地址,這個記憶體地址上的數據必須是char類型的。

舉個例子,給指針變數進行賦值:

#include<stdio.h>
int main () {
    char a = 5;        // char 類型占一個位元組; 
    char *b = &a;    // “&”是取變數的地址,取出a在記憶體中的地址;
                    // 賦值給b指針,此時b變數存儲的就是a地址。
    printf("我是a變數的值:%d\n",*b);        // *b表示輸出b裡面存儲的地址上的數據; 
    // 證明b上存儲的是a的地址;
    printf("我是a的地址:%p\n",&a);
    printf("我是b變數的值:%p\n",b);
return 0;
}

輸出結果為:

我是a變數的值:5
我是a的地址:000000000062FE17
我是b變數的值:000000000062FE17

通過畫圖來理解:

通過指針間接性修改變數的值

char a = 5;    
char *b = &a;
printf("初始值:a=%d,*b=%d\n",a,*b);
*b = 12;    // 其實操作的就是變數a本身的值;
printf("修改後:a=%d,*b=%d\n",a,*b);
------------------------------------------
輸出結果為:
初始值:a=5,*b=5
修改後:a=12,*b=12

指針類型的概念

我們知道char類型的數據只占一個位元組,有很多類型是需要多個位元組來存儲的,像int類型的數據就需要四個位元組來存儲(根據平臺不同,長度也有可能不一致)。

對於int類型的指針從當前位元組(地址)開始共四個位元組(地址)都是屬於該變數的值, 而對於char類型則只表示當前位元組(地址)。代碼如下:

int a = 259;
int * p1 = &a;
char * p2 = (char *)&a; // 這裡需要強制轉換一下類型
printf("*p1=%d,*p2=%d\n",*p1,*p2);
-----------------------
輸出:*p1=259,*p2=3

通過畫圖來便於理解:

通過上文我們已經對int類型指針有所瞭解了,*p1的輸出是在我們預算範圍之內的,但是為什麼*p2輸出的值是3呢?

重點,敲黑板!!!

因為電腦是使用二進位來表示數字的,上面(259)十進位轉換二進位是 [100000011],由於一個int類型變數占用四個位元組,8位二進位為一個位元組,補齊高位的0後,則 [00000000 00000000 00000001 00000011],每8位二進位(一個位元組)換算為十進位,則 [0  0  1  3]。

此時你應該差不多明白*p2為什麼輸出的值為3了吧,但是記憶體地址中有個概念叫"大小端模式",就會有兩種不同的排序:[0  0  1  3] or [3  1  0  0]。

由於電腦讀取*p2的地址是0x00,所以直接輸出這個地址上的數據,你也可以試著改一下,把259換成258/257等,看看是否正如所說。

驗證它們存儲地址,代碼如下:

int a = 259;
int * p1 = &a;
char * p2 = (char *)&a;
printf("*p1=%d,*p2=%d\n",*p1,*p2);
printf("&a=0x%p\n",&a);
printf("p1=0x%p\n",p1);
printf("p2=0x%p\n",p2);

輸出結果正如我們預想的:

當你看到這裡的時候,你只是剛剛認識指針而已,以上是我們俗稱的一級指針,一級指針是比較簡單的,還有二級指針和多級指針,更繞、更難理解,接下來介紹一下二級指針。

在講二級指針前,我們是否有疑問:什麼是一級指針?什麼是二級指針呢?兩者有什麼區別呢?

  • 一級指針存儲變數的地址,通過這個地址"直接獲取"變數的數據。
  • 二級指針存儲一級指針的地址,二級指針通過一級指針"間接獲取"獲取變數的數據。
  • 多級指針以此類推,個人理解,講的不對歡迎指正。

再堅持一下,精彩在"下麵"!!![/滑稽]

二級指針

“指針的指針”也就是我們俗稱的二級指針。

什麼是“指針的指針”,例如下麵代碼:

char a = 5;    
char * p1 = &a;
char ** p2= &p1;
printf("*p=%d,**p2=%d\n",*p1,**p2);   // 輸出:*p1=5,**p2=5

通過畫圖來理解:

多級指針也就是指針的指針的指針.....,以此類推即可。

第三、指針運算問題

指針運算是根據指針的類型不同而進行運算的,因類型的不同,在加1/減1操作時,記憶體分配的空間也不同。

又拿int類型和char類型來作比較,代碼如下:

char類型+1:從輸出結果可以看出地址是遞增1的,正符合char類型占一個位元組的說法。

char c = 'h';
char *a = &c;
for (int i=0;i<3;i++){
    printf("a+1=0x%p\n",a + i);
}
--------------------------------
輸出結果:
a+1=0x000000000062FE0F
a+1=0x000000000062FE10
a+1=0x000000000062FE11

int類型+1:輸出的地址之間相差為4,正是int類型占據空間。

int c = 259;
int *a = &c;
for (int i=0;i<3;i++){
    printf("a+1=0x%p\n",a + i);
}
--------------------------------
輸出結果:
a+1=0x000000000062FE0C
a+1=0x000000000062FE10
a+1=0x000000000062FE14

char類型和int類型分別+1在記憶體中地址分配,如圖:

指針就介紹到這裡,這隻是指針的基礎,還有數組指針、指針數組、null指針、void指針等等知識,還需要學習,後續繼續更新。

以上有不恰當或者講得不對的地方,希望各位留言指正,謝謝!

站在巨人的肩膀上!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 我們通過package實現對類的管理,package的使用有兩個要點: 1. 通常是類的第一句非註釋性語句。 2. 包名:功能變數名稱倒著寫即可,再加上模塊名,便於內部管理類。 【示例4-13】package的命名舉例 com.sun.test; com.oracle.test; cn.sxt.gao.te ...
  • 包機制是Java中管理類的重要手段。 開發中,我們會遇到大量同名的類,通過包我們很容易對解決類重名的問題,也可以實現對類的有效管理。 包對於類,相當於文件夾對於文件的作用。 作者:流浪者 日期:2020-02-09 ...
  • Java中,方法中所有參數都是“值傳遞”,也就是“傳遞的是值的副本”。 也就是說,我們得到的是“原參數的複印件,而不是原件”。因此,複印件改變不會影響原件。 · 基本數據類型參數的傳值 傳遞的是值的副本。 副本改變不會影響原件。 · 引用類型參數的傳值 傳遞的是值的副本。但是引用類型指的是“對象的地 ...
  • 先來看一個圖: 這個春節,我同所有人一樣,不僅密切關註這次新型肺炎,還同時關註行業趨勢和企業。在家憋了半個月,我選擇給自己看書充電。因為在疫情之後,行業競爭會更加加劇,必須做好未雨綢繆,時刻保持充電。 看了今年的情況,突然想到大佬往年經典語錄: 馬雲:未來無業可就,無工可打,無商可務 李彥巨集:人工智 ...
  • 構造方法用於對象的初始化!靜態初始化塊,用於類的初始化操作!在靜態初始化塊中不能直接訪問非static成員。 註意事項: 靜態初始化塊執行順序(學完繼承再看這裡): 1. 上溯到Object類,先執行Object的靜態初始化塊,再向下執行子類的靜態初始化塊,直到我們的類的靜態初始化塊為止。 2. 構 ...
  • 在類中,用static聲明的成員變數為靜態成員變數,也稱為類變數。 類變數的生命周期和類相同,在整個應用程式執行期間都有效。它有如下特點: 1. 為該類的公用變數,屬於類,被該類的所有實例共用,在類被載入時被顯式初始化。 2. 對於該類的所有對象來說,static成員變數只有一份。被該類的所有對象共 ...
  • 首先考慮沒有限制的情況 當硬幣被限制數量,需要加入對硬幣情況的考慮 所以設dp[][] 記錄湊齊x的種類 代碼如下 #include <iostream>using namespace std;int main(){ int dp[251][101]={0},ans[251]={0}; int ty ...
  • 類的派生、多態、抽象類、介面 1:派生-extends 派生就是繼承已有類非私有的欄位和方法等創建新的類,還可以添加、重寫欄位和方法; 在類的派生中,構造函數不可以被繼承; 派生源的類-父類/基類/超類;派生的類-子類/派生類; 2:super(…)調用超類的構造函數,在子類構造函數的開頭; sup ...
一周排行
    -Advertisement-
    Play Games
  • 基於.NET Framework 4.8 開發的深度學習模型部署測試平臺,提供了YOLO框架的主流系列模型,包括YOLOv8~v9,以及其系列下的Det、Seg、Pose、Obb、Cls等應用場景,同時支持圖像與視頻檢測。模型部署引擎使用的是OpenVINO™、TensorRT、ONNX runti... ...
  • 十年沉澱,重啟開發之路 十年前,我沉浸在開發的海洋中,每日與代碼為伍,與演算法共舞。那時的我,滿懷激情,對技術的追求近乎狂熱。然而,隨著歲月的流逝,生活的忙碌逐漸占據了我的大部分時間,讓我無暇顧及技術的沉澱與積累。 十年間,我經歷了職業生涯的起伏和變遷。從初出茅廬的菜鳥到逐漸嶄露頭角的開發者,我見證了 ...
  • C# 是一種簡單、現代、面向對象和類型安全的編程語言。.NET 是由 Microsoft 創建的開發平臺,平臺包含了語言規範、工具、運行,支持開發各種應用,如Web、移動、桌面等。.NET框架有多個實現,如.NET Framework、.NET Core(及後續的.NET 5+版本),以及社區版本M... ...
  • 前言 本文介紹瞭如何使用三菱提供的MX Component插件實現對三菱PLC軟元件數據的讀寫,記錄了使用電腦模擬,模擬PLC,直至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1. PLC開發編程環境GX Works2,GX Works2下載鏈接 https:// ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • 1、jQuery介紹 jQuery是什麼 jQuery是一個快速、簡潔的JavaScript框架,是繼Prototype之後又一個優秀的JavaScript代碼庫(或JavaScript框架)。jQuery設計的宗旨是“write Less,Do More”,即倡導寫更少的代碼,做更多的事情。它封裝 ...
  • 前言 之前的文章把js引擎(aardio封裝庫) 微軟開源的js引擎(ChakraCore))寫好了,這篇文章整點js代碼來測一下bug。測試網站:https://fanyi.youdao.com/index.html#/ 逆向思路 逆向思路可以看有道翻譯js逆向(MD5加密,AES加密)附完整源碼 ...
  • 引言 現代的操作系統(Windows,Linux,Mac OS)等都可以同時打開多個軟體(任務),這些軟體在我們的感知上是同時運行的,例如我們可以一邊瀏覽網頁,一邊聽音樂。而CPU執行代碼同一時間只能執行一條,但即使我們的電腦是單核CPU也可以同時運行多個任務,如下圖所示,這是因為我們的 CPU 的 ...
  • 掌握使用Python進行文本英文統計的基本方法,並瞭解如何進一步優化和擴展這些方法,以應對更複雜的文本分析任務。 ...
  • 背景 Redis多數據源常見的場景: 分區數據處理:當數據量增長時,單個Redis實例可能無法處理所有的數據。通過使用多個Redis數據源,可以將數據分區存儲在不同的實例中,使得數據處理更加高效。 多租戶應用程式:對於多租戶應用程式,每個租戶可以擁有自己的Redis數據源,以確保數據隔離和安全性。 ...