C語言——關于指針(逐漸清晰版)

為了更好地理解本篇文章的知識內容,讀者可以將以下文章作為補充知識進行閱讀?:
C語言————原碼 補碼 反碼 (超絕詳細解釋)-CSDN博客

C語言————二、八、十、十六進制的相互轉換-CSDN博客

C語言————斐波那契數列的理解和運用-CSDN博客

目錄

1. 內存和指針(地址)

1.1 內存的介紹

1.1.1內存的劃分

1.2 指針和地址

2. 指針變量和地址

2.1 取地址操作符&

2.2 指針變量

2.2.1 指針變量的定義

2.2.2 指針變量的類型

2.2.3 指針變量的大小?

2.3 解引用操作符*

?2.4 指針變量類型的意義

3. 指針計算

3.1 指針+-整數

3.2 指針-指針

3.3 指針的關系運算

4.野指針

4.1 野指針的成因和解決方法

4.1.1 指針未初始化

4.1.2 指針越界訪問

4.1.3 指針指向的空間釋放

5. void指針和assert斷言

5.1 void指針

5.2 assert斷言


1. 內存和指針(地址)

1.1 內存的介紹

在計算機中,有各種各樣的數據,他們的存儲需要在內存中劃分空間,計算機中的內存空間大小是有限的。如果把數據比作水,內存就是用以承載水的容器,而我們知道在生活中容器的大小都是有限的。因此我們可以 更好地理解內存之于數據的意義。

1.1.1內存的劃分

一個整型變量a= 10存儲在程序中需要占據4個字節的內存空間大小,而數據的單位是多種多樣的,那我們在內存中應該按照何種單位進行空間劃分呢?

為了內存空間的高效管理,內存被劃分為一個個的內存單元,而每個內存單元的大小為1字節。

?

其中,一個bit位可以存儲一個二進制的0或1,一個字節可以存儲8個bit位,即一個內存單元能存儲8個bit位。?

在內存中存儲的數據需要通過CPU的處理,那么CPU又是如何讀取這些數據的呢?

1.2 指針和地址

我們打個比方,當我們在入住一個酒店時,服務員會給我們對應的 房號和房卡,這樣我們就能快速找到對應的房間。CPU和內存中的數據傳輸也是同樣的道理,他們之間通過很多的地址總線進行連接,每根線只有兩態,表示0或1(聯想到二進制),那么通過地址總線不同的脈沖組合形成的這樣一個二進制數字,就是對應數據的地址編碼,即地址。

?在C語言中,我們將這樣的地址起名為指針。

所以我們可以理解為:

內存單元的編號 == 地址 == 指針

2. 指針變量和地址

2.1 取地址操作符&

我們在學習scanf函數時知道,scanf函數除格式化字符串以外的參數代表的都是地址。

當我們在創建變量的時候,他會向內存申請空間,我們想知道他具體的地址編號時就需要用到操作符&,示例如下:

?

?如圖創建的整型變量a,通過查看內存,我們知道他的地址即指針為0x00000099588FFB14-0x00000099588FFB17(x64環境下),共四個字節;但如果我們對a的地址進行打印的話(x86環境下,更加便于查看),結果又是怎樣的呢?

?

我們會發現,他只打印了一個地址編號,這是因為一個數據進行存儲時,他的內存空間都是連續的,打印的往往是最低的那個地址編號,進而根據數據的內存大小,從低往高訪問對應數據。

?

經過多次嘗試我們會發現,每一次變量的地址都是在發生變化的,這是因為在每次運行程序時,操作系統的內存分配情況存在差異,所以分配給變量的具體內存地址是不同的。

2.2 指針變量

2.2.1 指針變量的定義

那么通過取地址操作符&得到的地址我們又該將他存儲在哪呢?為了方便提取這些指針的數據,C語言中用指針變量作為他的容器。如:

#include <stdio.h>
int main()
{int a = 10;int * pa = &a;//取出a的地址并存儲到指針變量pa中return 0;

此時的pa就是一個指針變量,而他的類型為int *;?

指針變量也是?種變量,這種變量就是?來存放地址的,存放在指針變量中的值都會理解為地址

?在C語言中,地址就是指針,指針就是地址。

2.2.2 指針變量的類型

由上我們知道的一種指針變量類型為int *,我們應該怎么去理解他呢?

我們單獨看int * pa = &a這段語句可以知道,a為整型變量,pa存儲的是a的地址。由此知道:

int 代表pa存儲的指針所指向的數據a的類型(整型),* 表明pa為指針變量。

a和pa分別都在內存中劃分了屬于他們自己的空間。

?

那么字符類型的變量a,他的地址又該放在上面類型的指針變量中呢?

我們可以進一步推導如下:

int main()
{char a = '2';char* pc = &a;//字符指針pc,類型為char *return 0;
}
2.2.3 指針變量的大小?

在介紹內存中,我們知道地址的編號是由地址總線輸出的信號轉換得到的,32位機器假設有32根地址總線,他們產生的二進制序列作為一個地址,那么一個地址就是32個bit位,需要4個字節的存儲空間,指針變量的大小就是 4個字節。

同理64位機器,假設有64根地址線,一個地址就是64個二進制位組成的二進制序列,存儲起來就需要 8個字節的空間,指針變量的大小就是8個字節。

我們可以輸出如下的代碼進行測試:

#include <stdio.h>
//指針變量的??取決于地址的??
//32位平臺下地址是32個bit位(即4個字節)
//64位平臺下地址是64個bit位(即8個字節)
int main()
{printf("%zd\n", sizeof(char *));printf("%zd\n", sizeof(short *));printf("%zd\n", sizeof(int *));printf("%zd\n", sizeof(double *));return 0;
}

在x86環境下,即32位操作系統

?

在x64環境下,即64位操作系統?

?

結論:

1.?32位平臺下地址是32個bit位,指針變量大小是4個字節

2. 64位平臺下地址是64個bit位,指針變量大小是8個字節

注:指針變量的大小和類型是無關的,同樣指針類型的變量,在相同平臺下,大小都是相同的

2.3 解引用操作符*

那么對于指針變量,他們應該如何使用呢?這里我們將介紹一個關鍵的操作符——解引用操作符*

?他相當于是一把鑰匙,指針變量是對應的地址,指針變量指向的數據相當于被存儲在對應的地址,但我們無法直接操作他,因此需要通過鑰匙打開這道壁壘,這樣我們在不直接使用數據變量時,也能對數據進行相應的操作。示例如下:

#include <stdio.h>
int main()
{int a = 100;int* pa = &a;*pa = 0;//找到變量a,并通過*打開操作他的權限return 0;
}

?我們會發現,通過解引用,*pa就相當于變量a,我們能夠對他進行重新賦值

?2.4 指針變量類型的意義

由2.1中我們知道,指針變量會存儲數據空間中最小的地址編號,整型指針變量解引用時,他會向上訪問四個字節的內存空間,我們思考一下,如果我們使用字符指針變量對&a進行訪問,能得到正確的數據么?

int *整型指針下,我們打印讀取的整型變量數據是正確的(十六進制11223344轉為十進制為287454020?)

?當我們使用char *字符指針對整型變量n進行讀取時,我們發現,他僅讀取了n內存空間中的一個內存單元,數據為十六進制的44,轉為十進制為68。

在這里我們可以發現,不同的指針變量所訪問的內存空間大小也是不一樣的,因此學習指針變量的類型也是十分關鍵的。

注:在進行地址存儲時,指針變量的類型應該和地址的類型相對應,在代碼中我們可以看到(char*)&n,由于&n的類型為int *,我們用char *的指針變量接收他,兩者類型不同,為了使指針變量pc能夠順利存儲n的地址,我們需要對&n進行強制轉換,如果不進行強制轉換,編譯器會發出警告。(編譯器會進行隱式轉換類型

結論:指針的類型決定了對指針解引?的時候有多大的權限(?次能操作幾個字節)。

如: char* 的指針解引用就只能訪問?個字節,而?int* 的指針的解引用就能訪問四個字節。

3. 指針計算

3.1 指針+-整數

我們觀察如下代碼的運行結果:

我們可以發現,整型指針變量+1,他的地址跳過了4個字節;字符指針變量+1,他的地址跳過了1個字節。

指針+1,就是跳過1個指針指向的元素。指針可以+1,那也可以-1。

跳過一個指針的空間大小就取決于指針的類型

3.2 指針-指針

此運算的前提條件

  • 參與減法運算的兩個指針必須指向同一數組中的元素(或數組最后一個元素的下一個位置)
  • 兩個指針必須指向相同類型的數據(即指針變量類型一致)

運算結果

結果是一個ptrdiff_t類型(在<stddef.h>中定義的有符號整數類型),表示兩個指針所指向元素之間的元素個數差,而不是字節數差。

?我們觀察如下代碼:

int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int* p = arr;int* p1 = &arr[9];printf("%d\n", (int)(p1 - p));//p1 - p為ptrdiff_t類型,%d無法讀取,需要強制轉換return 0;
}

?指針求差他們的結果就是兩個指針之間內存的元素個數,如下圖,兩個箭頭之間的元素有1,2,3,4,5,6,7,8,9。共9個整型元素,故輸出9。

3.3 指針的關系運算

我們知道內存的地址編碼是從低到高依次排布的,因為指針是可以用來比較大小的。

常見的關系運算符包括:== 、!= 、< 、> 、<= 、>= 。

這些關系符構建的指針關系運算可以作為語句的判斷條件

指針的關系運算常用于數組遍歷內存區間判斷。

int arr[5] = {1,2,3,4,5};
int *p;// 遍歷數組:當p未超過數組最后一個元素時繼續循環
for (p = &arr[0]; p < &arr[5]; p++) {printf("%d ", *p);
}

4.野指針

概念: 野指針就是指針指向的位置是不可知的(隨機的不正確的沒有明確限制的
當一個程序存在野指針時,野指針的行為具有不確定性
  1. 可能立即觸發程序崩潰(如段錯誤)
  2. 可能暫時正常運行,但在后續操作中引發錯誤
  3. 可能修改無關內存區域,導致數據損壞或程序邏輯錯誤
  4. 可能觸發安全漏洞,被用于緩沖區溢出等攻擊

4.1 野指針的成因和解決方法

4.1.1 指針未初始化
#include <stdio.h>
int main()
{ int *p;//局部變量指針未初始化,默認為隨機值*p = 20;return 0;
}

局部變量p未進行初始化,此時的p就是野指針。?

解決方法

如果明確知道指針指向哪?就直接賦值地址,如果不知道指針應該指向哪?,可以給指針賦值NULL。NULL相當于給指針變量了一個固定的地方,不再“野”。
NULL 是C語?中定義的?個標識符常量,值是0,0也是地址,這個地址是?法使?的,讀寫該地址會報錯。
4.1.2 指針越界訪問
#include <stdio.h>
int main()
{int arr[10] = {0};int *p = &arr[0];int i = 0;for(i=0; i<=11; i++){//當指針指向的范圍超出數組arr的范圍時,p就是野指針*(p++) = i;}return 0;
}

當指針指向的范圍超出數組arr的范圍時,p就是野指針?。

?個程序向內存申請了哪些空間,通過指針也就只能訪問哪些空間,不能超出范圍訪問,超出了就是越界訪問。
4.1.3 指針指向的空間釋放
#include <stdio.h>
int* test()
{int n = 100;return &n;
}
int main()
{int*p = test();
printf("%d\n", *p);return 0;
}

當程序運行函數test()結束后,由于n為局部變量,他在內存中所占的空間就會被銷毀,導致指針p無法指向具體的變量,成為了野指針。

解決方法

1. 指針變量不再使?時,及時置NULL,指針使?之前檢查有效性

2. 如造成野指針的第3個例?,不要返回局部變量的地址。

———————————————————————————————————————————

持續更新中

5. void指針和assert斷言

5.1 void指針

5.2 assert斷言

打怪升級中.........................................................................................................................................

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

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

相關文章

SVG 在線編輯器

SVG 在線編輯器 引言 隨著互聯網技術的發展&#xff0c;矢量圖形在網頁設計和數據可視化中扮演著越來越重要的角色。SVG&#xff08;可縮放矢量圖形&#xff09;因其文件小、無限縮放不模糊的特性&#xff0c;成為了網頁設計中常用的圖形格式。SVG 在線編輯器的出現&#xff0c…

libpostproc 已經從 ffmpeg 中移除,導致編譯 ffmpeg 時沒有 libpostproc

今天編譯 ffmpeg 時突然發現 libpostproc 不見了&#xff0c;-enable-postproc 也變成了非法的選項。用搜索引擎搜索相關信息找不到一點&#xff0c;于是去 github 看。 從提交記錄可以看到 libpostproc 已經被移除了 鏈接 主線中已經看不到了 libpostproc 這個目錄了

基于 Dell PowerEdge T440 搭建的 Proxmox VE 配置 RTX 3060 顯卡直通虛擬機、切換直通

基于 Dell PowerEdge T440 搭建的 Proxmox VE 配置 RTX 3060 顯卡直通虛擬機、切換直通 文章目錄 基于 Dell PowerEdge T440 搭建的 Proxmox VE 配置 RTX 3060 顯卡直通虛擬機、切換直通 1. 前言 2. 前提條件 3. 配置步驟 3.1. 啟用 VT-d 3.2. 激活 IOMMU 3.3. 添加 VFIO 模塊 …

如何解決pip安裝報錯ModuleNotFoundError: No module named ‘voila’問題

【Python系列Bug修復PyCharm控制臺pip install報錯】如何解決pip安裝報錯ModuleNotFoundError: No module named ‘voila’問題 摘要 在開發過程中&#xff0c;我們常常會遇到pip安裝包時出現各種錯誤&#xff0c;特別是在使用PyCharm進行開發時。本文將詳細介紹如何解決安裝…

[spring6: @EnableWebMvc]-源碼分析

源碼 EnableWebMvc EnableWebMvc 是用于啟用 Spring MVC 的注解&#xff0c;它通過導入 DelegatingWebMvcConfiguration 來加載默認的 MVC 配置&#xff0c;同時允許開發者通過實現 WebMvcConfigurer 接口來自定義部分配置&#xff1b;若需更高階的控制&#xff0c;則可直接繼承…

Jmeter的元件使用介紹:(四)前置處理器詳解

Jmeter的前置處理器可以用來在取樣器執行前做一些數據準備操作&#xff0c;也需要注意使用的作用域問題。常用的前置處理器有&#xff1a;用戶參數、BeanShell預處理器、JDBC預處理器。一、用戶參數 【用戶參數】與前面介紹過的【用戶定義的變量】有相似之處&#xff0c;先來介…

十七、K8s 可觀測性:全鏈路追蹤

十七、K8s 可觀測性&#xff1a;全鏈路追蹤 文章目錄十七、K8s 可觀測性&#xff1a;全鏈路追蹤1、Skywalking 初識1.1 為什么需要全鏈路追蹤平臺1.2 全鏈路追蹤核心組件及工作原理1.2.1 全鏈路追蹤核心概念1.2.2 全鏈路追蹤工作原理1.3 什么是Skywalking&#xff1f;1.4 Skywa…

2025 Gitee vs. GitLab:全面對比與選擇指南

在軟件研發持續加速、合規要求日益嚴格的背景下&#xff0c;選擇合適的代碼托管平臺成為團隊數字化能力建設的關鍵環節。尤其在中國本土市場&#xff0c;Gitee正憑借其深度本地化能力、全面生態整合和開源社區支撐&#xff0c;成為國內團隊首選的開發協作平臺。 一、Gitee&…

期貨反向跟單忌諱問題(一): 不斷調整盤手交易規則

在期貨反向跟單領域&#xff0c;不少運營者在摸著石頭過河的過程中&#xff0c;容易陷入一個致命誤區——對盤手交易規則的頻繁調整。這種看似“優化策略”的舉動&#xff0c;往往會讓整個跟單體系陷入惡性循環&#xff0c;最終偏離盈利初衷。期貨反向跟單的核心邏輯是&#xf…

Effective C++ 條款07:為多態基類聲明virtual析構函數

Effective C 條款07&#xff1a;為多態基類聲明virtual析構函數核心思想&#xff1a;當通過基類指針刪除派生類對象時&#xff0c;如果基類沒有虛析構函數&#xff0c;會導致派生類資源泄漏。因為此時只會調用基類的析構函數&#xff0c;而不會調用派生類的析構函數。 ?? 1. …

C++進階—C++11

第一章&#xff1a;C11簡介 在2003年C標準委員會曾經提交了一份技術勘誤表(簡稱TC1)&#xff0c;使得C03這個名字已經取代了C98稱為C11之前的最新C標準名稱。不過由于C03(TC1)主要是對C98標準中的漏洞進行修復&#xff0c;語言的核心部分則沒有改動&#xff0c;因此人們習慣性…

把振動數據轉成音頻并播放

把振動數據轉聲音并播放 1、實現流程 安裝第三方庫: pip install numpy==1.23.5 pip install scipy==1.10.1 pip install sounddevice==0.4.6流程: 1、導入振動數據 2、數據歸一化到[-1, 1]范圍 3、重采樣到44.1kHz 4、播放音頻 5、保存音頻為WAV文件(可選)2、代碼示例 …

ServBay 1.15.0 更新,擁抱 Bun Deno 新生態

歷時一個月&#xff0c;ServBay迎來了1.15.0的更新。我們始終堅信&#xff0c;一個優秀的本地開發環境&#xff0c;不僅要穩定、高效&#xff0c;更要緊跟技術的演進脈搏。ServBay 的使命是為開發者掃清開發環境配置的障礙&#xff0c;讓您能聚焦于創造本身。 本次ServBay 1.1…

Java設計模式-通俗舉例

設計模式就像做菜的食譜&#xff0c;告訴我們遇到常見問題時該用什么"烹飪方法"。今天我就用最生活化的例子&#xff0c;帶大家輕松掌握23種設計模式的精髓。一、創建型模式&#xff08;5種&#xff09;&#xff1a;怎么"造東西"1. 單例模式&#xff1a;公…

【跟我學YOLO】YOLO12(3)訓練自己的數據集

歡迎關注『跟我學 YOLO』系列 【跟我學YOLO】&#xff08;1&#xff09;YOLO12&#xff1a;以注意力為中心的物體檢測 【跟我學YOLO】&#xff08;2&#xff09;YOLO12 環境配置與基本應用 【跟我學YOLO】&#xff08;3&#xff09;YOLO12 訓練自己的數據集 【跟我學YOLO】&…

【NLP輿情分析】基于python微博輿情分析可視化系統(flask+pandas+echarts) 視頻教程 - 微博輿情分析實現

大家好&#xff0c;我是java1234_小鋒老師&#xff0c;最近寫了一套【NLP輿情分析】基于python微博輿情分析可視化系統(flaskpandasecharts)視頻教程&#xff0c;持續更新中&#xff0c;計劃月底更新完&#xff0c;感謝支持。今天講解微博輿情分析實現 視頻在線地址&#xff1…

【C++】手搓一個STL風格的vector容器

TOC(手搓一個STL風格的vector容器) 手搓一個STL風格的vector容器 github地址 有夢想的電信狗 0. 前言&#xff1a;動態數組的工程實踐 ? 在C標準庫中&#xff0c;vector容器作為最核心的序列式容器&#xff0c;其設計融合了動態數組的高效性與安全性。本文將通過完整實現…

24. 了解過 webp 嗎

總結 一種圖片格式 一、什么是 WebP&#xff1f; WebP&#xff08;發音為 “weppy”&#xff09;是由 Google 推出的一種現代圖片格式&#xff0c;支持有損壓縮和無損壓縮&#xff0c;旨在提供更小的文件體積和更高質量的圖像顯示。 它兼容常見的圖片功能&#xff0c;如&#…

【Unity筆記】Unity Camera.cullingMask 使用指南:Layer 精準控制、XR 多視圖與性能提升

Unity Camera.cullingMask 使用指南&#xff1a;Layer 精準控制、XR 多視圖與性能提升 關鍵詞&#xff1a;Unity、Camera、Culling Mask、Layer 控制、XR 渲染分離、UI 顯隱、性能優化 特別說明&#xff1a; 本文為近期項目所遇問題的總結&#xff0c;僅純文字記錄&#xff0c;…

攜帶參數的表單文件上傳 axios, SpringBoot

頁面上的表單如上圖, 點擊確定按鈕需要把參數統一傳給后端.前端代碼:表單的提交方法const submit async () > {const formData new FormData();formData.append("bookName", bookForm.value.bookName);formData.append("author", bookForm.value.auth…