• Moegirl.ICU:萌娘百科流亡社群 581077156(QQ),欢迎对萌娘百科运营感到失望的编辑者加入
  • Moegirl.ICU:账号认领正在试运行,有意者请参照账号认领流程

李皇谛>帮助页面>C语言的 指针

萌娘百科,萬物皆可萌的百科全書!轉載請標註來源頁面的網頁連結,並聲明引自萌娘百科。內容不可商用。
跳至導覽 跳至搜尋

C語言的 指針
C語言硬件/軟件通用編程

指針到底是個啥啊啊啊啊啊啊啊啊啊啊!!!
——剛學C語言沒多久的小白
指針是C語言的尚方寶劍,但是想要理解起來看起來最為困難。

C語言學到一半,突然看到「指針」這玩意,誰知道它有什麼用途?於是大家都跟着在課本找資料,但是課本太正式了難理解,就轉去上網查吧——

難道你不是看別人的文章覺得你根本就不懂所以才來這裡看的嗎?

你懂咋教別人什麼是 「指針」 ?

對編程語言的學習與教授,大部分人都會常犯最大的三個誤區,分別是「默認誰都懂」「默認別人跟你思維一致」「機翻痕跡明顯」。這三大誤區都會導致精通率不能達到100%,我管這叫「學習陷阱」。

什麼學習陷阱?
「默認誰都懂」你給不會電腦的玩家講組策略管理器
「默認別人跟你思維一致」對習慣了Python(以「對象」為核心思維的編程語言)學C語言(以「過程」為核心思維的編程語言)
「機翻痕跡明顯」南開大學的塗奉生、齊寅峰教授把「Robust」(程序運行剛性)翻譯成「魯棒性」
還有些人才為了讓講方言的人理解通透索性翻譯成了「耐操性」
「譯事三難:信、達、雅。求其信,已大難矣!顧信矣,不達,雖譯,猶不譯也,則達尚焉。」——嚴復 《天演論》譯例言

「指針」這個翻譯,同時觸犯了上面三種學習陷阱,你說你跟一個還沒把內存學透甚至連數學都不會的學生這麼科普,你能保證他完全理解嗎?

  • 首先,同學們會知道C語言的pointer可以理解為抽象層面上的「指針」,放在現實生活中,等同於道路上的路標、走廊上的安全出口指示箭頭嗎?
  • 其次,你把「指針」解讀成「存放內存地址的變量」,你能確定看到這裡的學生全部都知道目前的通用處理器里,內存地址選擇與對應變量操作的概念嗎?
  • 第三,別在我說話的時候翻你那幾乎全新的口袋版牛津詞典跟我說「pointer」就翻譯為「指針」,只要有人不能理解,那麼你的本地化進程就不合格!

既然你在這裡是教師,你寫的內容是以課程、百科、教科書甚至直接對話的形式傳輸給學生的,你不單只要讓學生知道這個新內容,還要讓他們明白這個內容是幹什麼用的、該怎麼用、要注意哪些特殊情形。當然,你還要做好「學生把你的知識原封不動教給下一批學生」的準備

名師出高徒,學生才會為你感到驕傲,如果你只是水任務水課程,要你這老師幹什麼!

「Pointer」到底是個啥 ?
好了不發牢騷了(^_^;)

C語言的Pointer,按照上文所說,確實是存放內存地址的變量,確實可被翻譯成「指針」,但是他們對「Pointer」的解釋不成功,導致很多人學得雲裡霧裡。這裡直接給出一個例子讓你們結合生活實際理解什麼是C語言的「Pointer」(指針),看過來。

在現實生活上舉例

你在一個電商網站上購買一個物品下了訂單,因為遠隔千里,你和賣家都不能進行現場交易,需要把貨品交給快遞員派送。
快遞員會收到來自賣家的貨物,包裝穩妥使其不被破壞,再根據你給出的收件地址,選擇最為快捷的交通工具派送這包裹
當你看到快遞員送上來的時候,這「物品」往往被一個文件袋或是一個盒子包裹着,上面還貼着一張紙寫着你的收貨地址

換在C語言裡面

存放物品的包裹盒子就是變量,確保包裹裡面的物品對得上訂單的產品描述,對買家、賣家、訂單和店鋪信譽來說都重要。
寫着收貨地址的紙條就是指針,確保紙條上的地址對得上你家門口的門牌號,對派送包裹到你家裡的快遞員來說特別重要。

結論

指針是一種特殊用途的變量,用於存放其他變量在內存上的地址,對於一些要把數據複製到其他位置的程序來說特別重要。

不難發現,凡是涉及到數據複製內容傳遞代碼位置跳轉的程序都會大量使用指針,這些程序會在內存中的海量數據里,按照指針上存放的地址精準定位對應的變量,用於存放新數據、導出原數據,或者把這些數據放在與前端顯示相關的變量里轉化為可被用戶理解或修改的數字、文本或各種媒體。

「喂,這裡是快遞員,快遞要送到哪裡?」

如果沒有什麼特殊情形,按照馬斯洛需求層次理論,但凡動物甚至植物都有自己的安全需求,所以就連自己的家裡都要注重家裡的門窗能不能關死防止不法分子進來。但如果你願意讓朋友進屋拜訪,或者快遞員需要知道怎麼把快遞放到你手上,你就需要把你家的詳細門牌號主動分享給你信任的人。

作為高級編程語言的C語言,其中的優勢就是不需要用戶刻意學習裡面的內存分配並保證精通,因為編譯器自己就能通過用戶程序自動分配內存;反過來說,如果沒有做好內存讀寫管理,長期保持固定的內存地址容易被不法分子逆向編譯成功。

但有些需要存放海量數據的硬件(直接存儲器存取,DMA)或軟件(USART突發事件處理程序,俗稱USART_IRQ)需要把數據準確複製到程序或功能要求的變量中,就需要向相關硬軟件提供目標變量的絕對內存地址

我們可以在變量前添加控制字&,用於獲取該變量在內存中的絕對地址而不是該變量所存儲的數值。

#include <stdio.h>
unsigned char val = 85;
//新建一个字节(8位)变量“val”,根据逆向编译,它被存放在了0x004F的绝对地址里面。
unsigned int a = val;
//新建单字(16位)变量“a”,初始值为“val”所存放的数值“85”
unsigned long b = &val;
//新建双字(32位)变量“b”,初始值为“val”在内存上的绝对地址“0x004F”
printf("变量 val 在内存上的地址是 %d, 其数值是 %x。\r\n", b, a);
/*输出结果:
“变量 val 在内存上的地址是 85,其数值是 0x004F。
”
*/

有了這樣的控制字,我們就能在編譯器保護所有程序不會越權訪問變量的情況下,把需要代為讀寫的變量所在地址放在可被程序操作的變量上。

但如果沒有特殊說明,編譯器只會默認所有的變量只被當做文本或數值使用,只對面向當前程序的讀寫起作用。如果這個程序需要跨空間訪問絕對地址,而被訪問的絕對地址被其他程序所使用,就需要用到指針變量

「馬都什麼?」「馬冬梅!」