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 MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...