C語言零基礎第9講:指針基礎

目錄

1.內存和地址

2.指針變量和地址

2.1 取地址操作符(&)

2.2 指針變量

2.3?解引用操作符(*)

2.4 指針變量的大小

3.指針變量類型的意義?

3.1 指針的解引用

3.2 指針 + - 整數

3.3 void*指針?

4.指針運算

4.1 指針 + - 整數?

4.2 指針 - 指針

4.3 指針的關系運算


正文開始

1.內存和地址

? ? ? ? 在這里,我們先提一下存儲器的概念。在計算機中,存儲器可以分為:

  1. 主存儲器:又稱主存、內存。用來存放計算機運行期間所需的程序和數據,CPU可以直接隨機地對其進行訪問。特點是容量小、存取速度快、造價高。
  2. 輔助存儲器:又稱輔存、外存。用來存放當前暫時不用的程序和數據,以及一些需要永久性保存的信息。外存的內容需要調入主存后才能被CPU訪問。特點是容量大、存取速度慢、造價低。我們常見的硬盤就屬于外存。
  3. 高速緩沖存儲器:又稱cache。位于主存和CPU之間,用來存放當前CPU經常使用的指令和數據,以便CPU能夠高速地訪問它們。特點是容量小、價格高。現代計算機一般將cache制作到CPU中。

? ? ? ? 可知,內存其實是電腦上的存儲設備,程序在運行的過程中,需要向內存申請空間來使用。

? ? ? ? 我們可以舉一個生活中的例子,在大學里,同一個專業的同學,一般是在同一棟宿舍樓中的。而在一棟宿舍樓中,有不同的房間號,比如:1樓有101、102、103...,2樓有201、202、203...,3樓有301、302、303...。如果你想找到你班上的某個同學,要是一層層樓,挨個房間去找,效率會非常低。但是,如果我們知道某個同學的地址,也就是所在宿舍的門牌號,就可以根據地址直接找到這個同學了。

? ? ? ? 如果把上面的例子推廣到計算機中,是怎么樣的呢?

? ? ? ? 我們知道,在計算機中,CPU處理數據的時候,所需的數據是從內存中讀取的,處理后的數據也會放回到內存中。我們日常使用的電腦,有8G、16G、32G......,這些內存空間是如何高效管理的呢?

? ? ? ? 其實就是把內存劃分為一個個的內存單元,每個單元的大小為1個字節,1個字節含有8個比特位,每個比特位可以存放1個二進制的0或1。

? ? ? ? 計算機中常見的單位如下:

  1. bit:比特位,簡稱位,縮寫為b。是計算機中最小的存儲單位,1bit表示1個二進制位(0或1)。
  2. Byte:字節,縮寫為B。1Byte = 8bit。
  3. KB:千字節。1KB =? 1024B,? ?即2的10次方B。
  4. MB:兆字節。1MB = 1024KB,即2的20次方B。
  5. GB:吉字節。1GB = 1024MB,即2的30次方B。
  6. TB:太字節。1TB? = 1024GB,即2的40次方B。
  7. PB:拍字節。1PB = 1024TB, 即2的50次方B。

? ? ? ? 如上圖,從下往上,給每一個內存單元編了號:0,1,2,3...,用16進制來表示。

? ? ? ? 每個內存單元,都有1個字節的空間,也就是8個比特位的空間,相當于1個宿舍里,有8個床位的空間。

? ? ? ? 每個內存單元,都有一個編號,相當于宿舍的門牌號。有了這個門牌號,就可以快速找到一個同學。有了這個內存單元的編號,CPU就可以快速找到一個內存空間。

? ? ? ? 生活中, 我們把門牌號也叫作地址。在計算機中,我們把內存單元的編號也叫地址。在C語言中,給地址起了新的名字:指針。

? ? ? ? 可以這樣理解:在C語言中, 內存單元的編號 = 地址 = 指針,三種說法是一樣的。

? ? ? ? 那么,我們怎么理解編址呢?

? ? ? ? 如上圖,我們知道, CPU和內存之間,有大量的數據交互,為了實現這種交互,兩者之間是用線連接起來的。

? ? ? ? CPU是完成計算工作的,首先要從內存中讀數據,計算完畢后,又將結果的數據寫入內存。

? ? ? ? 這個過程是:首先,控制總線發出Read指令,然后CPU會給出一個地址,通過地址總線向內存傳遞一個地址信號,從內存中找到該空間,再將該空間中的數據通過數據總線傳遞給CPU。在CPU利用所讀的數據進行計算,得出結果之后,控制總線發出Write指令,然后CPU給出一個寫入地址,通過地址總線向內存傳遞有一個地址信號,在內存中找到該空間,把結果的數據寫入這個空間中。

? ? ? ? 我們再來討論一下編址:

? ? ? ? CPU要訪問內存中的某個字節空間,必須知道這個字節空間在內存中的什么位置, 而內存中有很多個字節,就需要給內存進行編址了。就好比,一棟樓中有很多個宿舍,需要給每個宿舍進行編號。

? ? ? ? 計算機中的編址,并不是把每個字節的地址記錄下來,而是通過硬件設計完成的。

? ? ? ? 就好比,吉他上面沒有寫:哆、來、咪、發、梭、拉、西。但是演奏者也能準確找到每一個音所在的位置,這是為什么呢?因為制造商已經在樂器的硬件層面上設計好了,并且所有的演奏者也知道,本質上是一種約定的共識。硬件編址也是如此。

? ? ? ? 我們可以簡單理解為:32位機器有32根地址總線,每根線只有2種狀態,電脈沖的有無代表0和1。那么1根線能表達2種含義,2根線能表達4種含義,......,32根線能表達2的32次方種含義。每一種含義都代表一個地址。

? ? ? ? 地址信息被下達給內存,在內存中,通過硬件的設計,就可以直接找到該地址中對應的數據,這個數據再通過數據總線傳給CPU內的寄存器。

2.指針變量和地址

2.1 取地址操作符(&)

? ? ? ? 在了解了內存和地址之后,我們回到C語言。在C語言中,創建1個變量,其實就是在向內存申請空間。比如:對于int a = 10;,實質就是,向內存申請4個字節的空間,把10存放進去。如下圖:

? ? ? ? 如上,創建了整型變量a,向內存申請了4個字節的空間,用于存放整數10。

? ? ? ? 我們使用取地址操作符(&),取出了4個字節中地址較小的那個地址。在printf打印時,對應的占位符是%p。

? ? ? ? int變量占4個字節,我們只要知道了第一個字節的地址,就可以順藤摸瓜訪問到剩下3個字節的數據,因為相鄰地址之間相差1。

2.2 指針變量

? ? ? ? 剛剛,我們使用取地址操作符(&),取出了變量a的地址,如果我們想把這個地址存到一個變量里面去,該怎么操作呢?如下:

? ? ? ? 指針就是地址,指針變量是專門用來存放地址的變量。應該知道的是,上面的pa,在日常生活中,指針變量有時會被口頭語叫做指針,而指針實際上是地址,而非變量

? ? ? ? 對于int類型的變量a,它的地址存放在int*類型的pa中。那么舉一反三,如果有一個char類型的變量c,它的地址應該存放在什么類型的指針變量中呢?如下:

2.3?解引用操作符(*)

? ? ? ? 指針變量是用來存放地址的,存起來的地址有啥用呢?當然是用來使用的,怎么使用呢?我們可以看下面的例子:

? ? ? ? 如上,變量pa中存放了變量a的地址。*pa的意思就是 ,通過pa中存放的地址,找到這個地址所指向的空間,也就是找到變量a。其實,*pa就是a,*pa=20這個操作就是把a改成了20。我們可以直接通過a=20來修改a的值,但是用指針來修改,就多了一種途徑,寫起代碼來會更靈活,而且在某些情況下,指針是會起到它的作用的。

2.4 指針變量的大小

? ? ? ? 我們知道,各種類型的變量,都是有其大小的,比如:int的大小是4個字節,char的大小是1個字節。那么,指針變量的大小是多少呢?我們來看如下代碼:

? ? ? ? 如上圖,在x64環境,即64位環境下,int*和char*的長度都是8。在x86環境,即32位環境下,int*和char*的長度都是4。這是為什么呢?

? ? ? ? 在前面,我們提到過,對于32位機器,一般有32根地址總線,每根線傳遞的電信號轉換成數字信號,是0或1。我們把32根地址線產生的二進制序列當做一個地址,那么地址在總線上傳輸的時候,長度就是32個bit位,即4個字節,在內存中存儲時,和它傳遞時的長度是一樣的,也是4個字節。

? ? ? ? 同理,對于64位機器,一般有64根地址總線,存儲的地址就有64個bit位的空間, 也就是8個字節,在總線上傳輸和內存中存儲的長度就是4個字節了。

? ? ? ? 結論:

  1. 32位平臺下,地址是32個bit位,指針變量的大小是4個字節。
  2. 64位平臺下,地址是64個bit位,指針變量的大小是8個字節。
  3. 指針變量的大小,與指針變量的類型無關。

3.指針變量類型的意義?

? ? ? ? 剛剛我們發現,無論什么類型的指針變量,在相同的平臺上,它的大小都是相同的,和類型沒有關系。那么,指針變量的類型有什么意義呢?

3.1 指針的解引用

? ? ? ? 請看代碼:

? ? ? ? 如上,在創建變量a之后,我們取a的地址,此時4個字節中依次存放著:44、33、22、11。

? ? ? ? 如上,在使用*pa修改a的值為0之后,內存中的4個字節全部被修改為0,呈現出:00、00、00、00、00。

? ? ? ? 在上面,我們是用int* pa來接受a的地址的,如果換成char*類型會怎么樣呢?

? ? ? ? 如上,我們發現,如果指針變量pa的類型為char*,那么在執行*pa=0后,內存中只有第1個字節被修改為00,呈現出:00、33、22、11。

? ? ? ? 如上,運行結果顯示,變量a的值確實沒有修改為0,而是為第1個字節修改為0之后的結果。

? ? ? ? 其實,指針的類型決定了,在指針解引用的時候,一次能操作幾個字節。

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

? ? ? ? 我們還可以用下面的方式驗證一下,pa由于是char*的類型,所以在打印的時候,也只能打印出第1個字節的內容,只有使用int*才能完整地打印出4個字節所表示的值。如下:

3.2 指針 + - 整數

? ? ? ? 請看代碼:

? ? ? ? 如上,a、pa、pc的地址都是一樣的,這當然應該一樣。

? ? ? ? 但是,pa+1后, 地址增加了4,而pc+1后,地址只增加了1。

? ? ? ? 我們來看看圖示:

? ? ? ? 結論:

  1. 指針的類型決定了指針向前或向后走一步,有多大(距離)。
  2. 指針+n,其實就是跳過n個指針所指向元素的長度。
  3. 指針可以+n,也可以-n。

3.3 void*指針?

? ? ? ? 在前面我們已經了解到, 指針變量是有很多種類型的,比如:int*表示指針指向的是int類型的變量、char*表示指向char類型的變量、short*表示指向short類型的變量、double*表示指向double類型的變量......

? ? ? ? 其實,在指針類型中,有一種特殊的類型叫void*類型,可以理解為,無具體類型的指針(或泛型指針)。

? ? ? ? void*指針可以接收任意類型的地址

? ? ? ? void*指針的局限性在于,不能直接進行指針的解引用操作和+-整數操作

? ? ? ? 我們可以看到,void*指針可以接收不同類型的地址,但是無法直接進行指針運算。那么,void*指針到底有什么用呢?

? ? ? ? 一般,void*指針是在函數的參數部分使用的,用來接收不同類型的數據,這樣的設計可以實現泛型編程的效果,使得一個函數可以處理多種類型的數據

4.指針運算

? ? ? ? 指針的基本運算有3種,如下:

  1. 指針+-整數
  2. 指針-指針
  3. 指針的關系運算

4.1 指針 + - 整數?

? ? ? ? 假如要遍歷一個數組,打印出每一個元素,我們首先想到的可能是如下方法:

? ? ? ? 其實,我們還可以使用指針的方式來遍歷數組。

? ? ? ? 我們知道,數組在內存中是連續存放的,只要知道了第一個元素的地址,就能順藤摸瓜找到后面的所有元素。我們可以將首元素的地址存放在變量p中,*p訪問的是第1個元素,*(p+1)訪問的是第2個元素,*(p+2)訪問的是第3個元素......循環下去,我們就能遍歷數組了,請看下圖:

? ? ? ? 當然了,我們也可以用指針實現倒序打印數組,把數組的尾元素地址交給指針變量p,*p訪問的是倒數第一個元素,*(p-1)訪問的是倒數第二個元素......循環下去即可,請看代碼:

4.2 指針 - 指針

? ? ? ? 關于指針減去指針,我們要注意的是:

  1. 指針 - 指針的前提是:2個指針指向同一塊空間。
  2. 指針 - 指針得到的是:2個指針之間的元素個數。?

? ? ? ? 請看代碼:

? ? ? ? 我們再來看看圖示:

? ? ? ? 我們可以聯想到前面所提到的指針 + - 整數:

? ? ? ? 那么,指針 - 指針有什么用呢?我們來看看它的應用:

? ? ? ? 如上,為了統計字符串中,\0之前的字符個數,我們使用了strlen這個庫函數。那么,我們能不能不使用庫函數,而是自己寫一個函數,來統計字符個數呢?

? ? ? ? 我們先來看看指針+1的方式:

? ? ? ? 我們再來思考一下,用指針 - 指針的方式來解決這個問題。如果我們能夠找到'\0'的指針,用'\0'的指針減去首元素的指針,就可以得到元素個數了。請看代碼:

4.3 指針的關系運算

? ? ? ? 指針的關系運算,其實就是地址比較大小。我們知道,地址其實就是一種編號,是數值,當然可以比較大小了。

? ? ? ? 我們還是以遍歷數組為例子:


完結

?

????????

????????

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

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

相關文章

013 HTTP篇

3.1 HTTP常見面試題 1、HTTP基本概念: 超文本傳輸協議:在計算機世界里專門在「兩點」之間「傳輸」文字、圖片、音頻、視頻等「超文本」數據的「約定和規范」HTTP常見的狀態碼 [[Pasted image 20250705140705.png]]HTTP常見字段 Host 字段:客戶…

每日面試題20:spring和spring boot的區別

我曾經寫過一道面試題,題目是為什么springboot項目可以直接打包給別人運行?其實這涉及到的就是springboot的特點。今天來簡單了解一下springboot和spring的區別, Spring 與 Spring Boot:從“全能框架”到“開箱即用”的進化之路 …

ClickHouse數據遷移

ClickHouse實例是阿里云上的云實例,想同步數據到本地,本地部署有ClickHouse實例,下面為單庫單表 源實例:阿里云cc-gs5xxxxxxx.public.clickhouse.ads.aliyuncs.com:8123 目標實例:本地172.16.22.10:8123 1、目標實例建…

sqli-labs-master/Less-41~Less-50

Less-41這一關還是用堆疊注入,這關數字型不需要閉合了。用堆疊的話,我們就不爆信息了。我們直接用堆疊,往進去寫一條數據?id-1 union select 1,2,3;insert into users (id,username,password) values(666,zk,180)--看一下插進去了沒?id-1 u…

Tiger任務管理系統-10

十是個很好美好的數字,十全十美,確實沒讓人失望,收獲還是很大的。 溫習了前端知識,鞏固了jQuery,thymeleaf等被忽視的框架,意外將之前的所學所用的知識都連起來了,感覺有點像打通了任督二脈一樣…

ora-01658 無法為表空間 users中的段創建initial區

ora-01658 無法為表空間 users中的段創建initial區 參考1 參考2 參考3 參考4 給用戶新增表空間 alter tablespace system add datafile D:\APP\ADMINISTRATOR\ORADATA\ORCL\SYSTEM03.DBF size 5G autoextend on next 10M;設置表空間文件自動擴展 ALTER DATABASE DATAFILE /…

lodash的替代品es-toolkit詳解

一、es-toolkit簡介 es-toolkit 是一款先進的高性能 JavaScript 實用程序庫,體積小巧,并支持強類型注釋,典型特征包括: 提供各種日常實用函數并采用現代實現,例如: debounce、delay、chunk、sum 和 pick 等 設計充分考慮了性能,在現代 JavaScript 環境中實現了 2-3 倍…

【原創】基于gemini-2.5-flash-preview-05-20多模態模型實現短視頻的自動化二創

畫面和解說保持一致,這個模型就是NB[16:57:37] [*] 正在從視頻中提取幀和時長 (頻率: 1.0 幀/秒)... [16:57:55] [] 提取完成。視頻時長: 83.40秒, 提取了 84 幀。 [16:57:55] [*] 使用AI供應商: gemini [16:57:55] [*] 正在進行視覺分析... [16:57:55] L-> 正…

數倉架構 數據表建模

數倉架構 主要用來描述 數據加工的實時鏈路 和 離線鏈路之間的關系,即 流批 關系; lamda 架構, 是兩條路, 實時計算式的, 維護數據的實時性。然后每天經過批計算后, 覆蓋實時的計算結果。 保證數據準確性。 kappa架構, 即流批一體了 數據建模 星型模型是數據倉庫中最…

vscode調試python腳本時無法進入函數內部的解決方法

只需在launch.json配置文件中添加“justMyCode”:false.

Python day37

浙大疏錦行 python day37. 內容: 保存模型只需要保存模型的參數即可,使用的時候直接構建模型再導入參數即可 # 保存模型參數 torch.save(model.state_dict(), "model_weights.pth")# 加載參數(需先定義模型結構) mod…

ORACLE進階操作

1 事務 事務的任務便是使數據庫從一種狀態變換成為另一種狀態,這不同于文件系統,它是數據庫所特用的。 所有的數據庫中,事務只針對DML(增刪改),不針對select select只能查看其他事務提交或回滾的數據,不能查…

Modbus 的一些理解

疑問:(使用的是Modbustcp)我在 Modbus slave 上面設置了slave地址為1,位置為40001的位置的值為1,40001這個位置上面的值是怎么存儲的,存儲在哪里的?他們是怎么進行交互的?在Modbus協…

【運動控制框架】WPF運動控制框架源碼,可用于激光切割機,雕刻機,分板機,點膠機,插件機等設備,開箱即用

WPF運動控制框架源碼,可用于激光切割機,雕刻機,分板機,點膠機,插件機等設備,考慮到各運動控制硬件不同,視覺應用功能(應用視覺軟件)也不同,所以只開發各路徑編…

RabbitMQ-日常運維命令

作者介紹:簡歷上沒有一個精通的運維工程師。請點擊上方的藍色《運維小路》關注我,下面的思維導圖也是預計更新的內容和當前進度(不定時更新)。中間件,我給它的定義就是為了實現某系業務功能依賴的軟件,包括如下部分:Web服務器代理…

【Linux基礎知識系列】第九十篇 - 使用awk進行文本處理

在Linux系統中,文本處理是一個常見的任務,尤其是在處理日志文件、配置文件和數據文件時。awk是一個功能強大的文本處理工具,廣泛用于數據提取、分析和格式化。它不僅可以處理簡單的文本文件,還可以處理復雜的結構化數據&#xff0…

第二十七天(數據結構:圖)

圖:是一種非線性結構形式化的描述: G{V,R}V:圖中各個頂點元素(如果這個圖代表的是地圖,這個頂點就是各個點的地址)R:關系集合,圖中頂點與頂點之間的關系(如果是地圖,這個關系集合可能就代表的是各個地點之間的距離)在頂點與頂點…

數據賦能(386)——數據挖掘——迭代過程

概述重要性如下:提升挖掘效果:迭代過程能不斷優化數據挖掘模型,提高挖掘結果的準確性和有效性,從而更好地滿足業務需求。適應復雜數據:數據往往具有復雜性和多樣性,通過迭代可以逐步探索和適應數據的特點&a…

什么是鍵值緩存?讓 LLM 閃電般快速

一、為什么 LLMs 需要 KV 緩存?大語言模型(LLMs)的文本生成遵循 “自回歸” 模式 —— 每次僅輸出一個 token(如詞語、字符或子詞),再將該 token 與歷史序列拼接,作為下一輪輸入,直到…

16.Home-懶加載指令優化

問題1:邏輯書寫位置不合理問題2:重復監聽問題已經加載完畢但是還在監聽