c語言指針用法及實際應用詳解,通俗易懂超詳細!
\\\插播一條:文章末尾有驚喜喲~///
今天給大家來講解一下指針。
我會由淺到深,最後聯合實際應用講解,讓大家學會指針的同時,知道大佬們都用指針來幹嘛!
長文預警!全文大約5200多字,學指針看這篇文章就夠了!
很多人跟我剛進修c語言一樣,都怕指針。
我也是後面做了一些物聯網網關才知道,指針是c語言的靈魂這句話真正含義。
沒有指針,很多功能實現起來的確很不方便,假如做不到真正的模塊化編程。
Ok,廢話不多說,下面正式進入主題。
一、通過這篇文章你能掌握以下知識:
00001.指針的相關概念
00002.掌握指針與數組之間的關係
00003.掌握指針指向的指針
00004.掌握如何使用指針變量做函數參數
00005.掌握如何使用指針函數
00006.掌握如何使用數組指針函數
那麼這篇文章對應有視頻教程,假如不喜愛看文章的能夠去看視頻,教程在小破站能夠搜沒際單片機編程找到,也能夠找我們拿。
二、指針的作用:
指針是C語言中一個比較重要的東西,有人說指針是C語言的靈魂這句話說的一點也沒錯。
正確靈敏地運用它,能夠有效地表達一些複雜的數據構造,假如系統地動態分配內存、音訊機制、任務調度、靈敏矩陣定時等等。
掌握指針能夠使你的程序愈加簡潔、緊湊、高效。
那麼在單片機領域,假如是做稍微大一點的項目,須要把每個功能做成模塊化,硬體驅動層和應用層分別獨立運行。
即便更換單片機型號也不用修改應用層程序,即移植性非常強,這些都離不開指針。
甚至沒指針會很難實現,即便實現代碼的可移植性也很差。
三、指針的概念
前面講了指針的作用,這裡再強調一點,指針是一把雙刃劍。
用好了能十分靈敏而且提高程序的效率,但是假如使用不當,則會出現程序」死機」等致命問題。
而這些問題往往是由於錯誤地使用指針而造成的,最常見的就是內存溢出錯誤,指針指向未知地址。
1.地址與指針
指針是一個比較抽象地概念,假如想真正了解指針,那麼要先從數據是如何存儲的說起,我們通過一個圖來看一下數據在內存里存儲的情況。
在這個圖中,都是以16進位顯示。
紅色標註的0x00000400代表地址內存地址,綠色37,30代表數據,而橙色標註的00 01代表地址遞增量,即代表0x00000400和0x00000401,每個地址存儲1個位元組數據。
那麼我們把這個圖看作是數據在內存里的存儲形式,0x00000400這個內存地址存儲著數據37,0x00000401這個內存地址存儲著數據30。
當我們在程序里定義一個位元組的變量,那麼在編譯器編譯時就會給這個變量分配一個這樣的內存地址來存儲。
假設我們定義以下變量
unsigned char a;
a = 0x37;
對應這個圖就是,編譯器在編譯時會為變量a分配一個位元組的內存空間並把0x37這個數據存儲進去,並將變量名a改成地址0x00000400,以便CPU的訪問。
通過這個地址就能找到變量a數據的存儲位置,而這個地址0x00000400其實就是指針,通過這個指針能夠訪問變量a的數據。
2.指針變量
通過上面講解我們明白了通過地址能訪問內存的數據,這個地址啊就是指針。
那麼指針和指針變量呢是不一樣的概念,大家一定要記住了。
指針是概念、指針變量是這個概念的詳細應用之一,我們先來看一下C語言里怎麼定義指針變量。
指針變量定義的一般形式:
變量類型*變量名
unsigned char *p;
通過這種語法,我們就能夠定義一個指針變量p。
指針變量賦值
指針和指針變量是兩個概念,指針變量跟普通變量一樣,在使用前一定要定義和賦值(指向地址)。
給指針變量賦的值和普通變量不同,給指針變量賦值只能賦地址,而不能賦予其他任何值,否則會引起錯誤。
那麼怎麼獲取普通變量的地址呢,在C語言里能夠使用」&」來獲取普通變量的地址,一般用以下格式來表示:
&變量名
那麼通過&變量名取得變量地址後就能夠賦值給指針變量。
舉例:
unsigned char a;
unsigned char *p
int main()
{
p = &a;
}
這個代碼里,我們定義了一個變量a,定義了一個指針變量p。
我們通過運算符&把變量a的內存地址賦值給變量p,所以p指向了變量a的內存存儲地址。
上面說了指針變量賦值的問題,那麼怎麼獲取和變更指針變量指向那個內存地址的數據呢?我們能夠通過:
*指針變量 =數值
如:*p = 10;
這樣的操作能夠變更指針變量指向那個內存地址的數據。
通過:
a = *p;
來獲取指針變量指向那個內存地址的數據。
下面我們通過一個代碼實驗來舉例。
這裡我們定義了變量a和指針變量p,然後a的值初始化為10。
把a的地址賦值給指針變量p,接著我們輸出a的地址是0x60ff33。
由於前面我們把a的地址賦值給了指針變量p,所以p指向的地址也是0x60ff33。
那麼我們再來看一下,指針變量的在內存里的存儲地址是0x60ff2c。
所以大家這裡要注意了,我們定義指針變量時,即便指針變量是指向地址用的,但是編譯器也會分配一塊內存地址來存儲指針變量。
我們接著來看下變量a的輸出值。
a=10, *p是獲取指針指向內存地址的數據,所以也是10。
下面就是通過指針變量來變更變量a的值,由於指針變量p指向的是變量a的地址,所以變更指針變量p指向內存地址的數據就能夠變更變量a的值。
那麼通過這麼原理,我們是不是不用指針變量,也不用a即是多少來變更a的值呢?當然能夠!
我們看下面通過內存地址變更變量a的值,我們前面知道a的地址是0x60ff33,那我們能夠直接寫0x60ff33=12來變更變量a的值。
當然這裡要注意,編譯器編譯時並不知道0x60ff33是什麼東西,所以要把這個整形地址轉換成指針類型。
最後通過*+地址語法變更這個地址裡面的數據。
我們看輸出結果,能夠發現a的值已經成功被改成了12。
其實通過指針變量變更某個內存地址的數據就是這個原理,但是指針變量益處能夠任意起名字。
也不用像這樣先把變量a的地址讀出來,然後通過地址去變更它的值,用起來就很方便,所以通過指針變量來替代了這種做法。
四、數組與指針
一般系統或編譯器會分配不間斷地址的內存來存儲數組裡的元素,假如把數組地址賦值給指針變量,那麼就能夠通過指針變量來引用數組,讀寫數組裡的元素了。我們來做個實驗:
從這個代碼來看,定義了一個數組buff並初始化為1,2,3,4,5。
定義了2個指針變量p1和p1,分別指向buff, &buff[0]。
buff默認的是數組下標為0元素的存儲地址。
所以這里buff和&buff[0]是同一個內存地址,只是寫法不一樣。
我們從輸出結果能夠看的出來,數組和指針變量的地址都是一樣的,所以大家用這幾種寫法都是能夠的。
那麼我們來看下輸出結果,都是1,說明操作是對的。
指針自加自減運算
指針變量除了能夠用來獲取內存地址的值以外,還能夠用來進行加減運算。
但是這個加減呢跟普通變量加減不一樣,普通變量加減的是數值,而指針變量加減的是地址,我們來通過代碼來講解下。
同樣這裡定義了數組buff並初始化為1,2,3,4,5。
我們把指針變量p1指向數組第一個元素的地址,即0x402000。
然後我們直接看p1++的操作,p1++後我們看到p=0x402001,所以指針變量的加減等運算是指向地址的運算。
其他減法乘除法也是基於地址的運算。
如何攻克單片機下載程序失敗的問題
作為一個單片機開發人員,每天跟我們打交道的工具沒非兩種,一種是PC機上的開發環境,假如Keil MDK、IAR Embedded Workbench等等;而另外一種呢,沒非就是集程序下載、調試功能於一體的編程器、這個叫法比較混亂、也能夠叫編程器、下載器、仿真器(但我認為不叫燒錄器,燒錄器一般值批量燒錄階段所用的那種能夠快捷完成晶片批量燒錄的玩意)。後文統稱編程器。
常見的編程器有以下幾種:
1、自然是老牌Jlink了;
其實拆開市面上常見的jlink,能夠看到各種各樣的板子:
這都是從某寶上買的,標註著正版、永不掉固件的jlink,這樣的東西在我手裡壞了2個,一初始我還有毅力去找個XP系統刷一下固件,但是刷了沒幾天就又壞了,而且身邊的XP是越來越少了,想著再買一個也才幾十塊,完全沒必要浪費這個時長去刷了。當然很多人喜愛動手挑戰一下,但是當你的項目忙的不可開交,而這個時候你的編程器固件掉了,你還要跑去找個XP給他刷固件,想想都不爽。
有朋友告訴我,自己經常要去其他城市出差,調試自己的板子,SEGGER的原版太貴,只好從某寶買幾十塊的用,而且買一個又擔憂壞了耽擱事,所以每次出差至少都帶兩個,然而這個碩大的體積是在讓人不爽,出差的時候要在極其寶貴的行李空間裡放兩個jlink,也是夠了。
也有人埋怨,jlink的接口太不方便了,雖然大多數時候調試的時候,只用了四五根杜邦線截出來,然而他的接口卻保留了20腳,其中有一整排除了一個腳全是GND了。做個板子,入股只留幾個排針,那樣不好插線調試,假如想留一個和jlink一樣的接口,又覺得會顯得板子笨重,而且在一些對尺寸有嚴格要求的產品里,壓根沒有空間放下這個20腳的接口。
這時候有人提出了攻克方案,也就是下文的第二種編程器:
2、ST-LINK
可是仔細看看這個並沒有攻克JLINK的一些缺少點,接口還是笨,體積還是不小。那麼萬能的某寶上還有一種攻克方案;
各種各樣的顏色,而且價格比較便宜,十五塊左右就能搞到,嘗鮮買個試試,發現挺好用。和優盤一樣大小的身板,出差帶著也方便。但是某一天它忽然不工作了,插上之後電腦沒有人任何反應,我經過一系列的診斷,最後懷著沉重的心情給它下了死亡通知書。
然後再去買下載器,我又不想買太貴的,又想買個小體積的,想來想去,最終一次買了三四個這種小的STLINK,我知道他會壞,但是一次買好幾個就不怕他壞了,而且好幾個的價格也就是一個某版的jlink價格。
接下來沒意發現了淘寶上有一款叫做elink的玩意:
3、elink32-pro
這個東西看起來很棒,由於它集成了串口調試,而且貌似速度很快,正好我和合泰半導體這個公司有業務往來,就請合泰的相關負責人寄我一個樣品試用,試用效果很棒,唯二的缺少點就是體積太大(和jlink差不多)、價格太貴。
那麼有沒有可能和合泰達成一個協議,共有作出一款合乎以下需求的編程器(下載器、仿真器、調試器)呢?
a、能支持簡直所有32位ARM-CortexM內核單片機
b、小巧便攜
c、不掉固件
d、去掉多餘接口,只保留swd
e、增加虛擬串口功能
f、可對外供電、電流過大自動切斷
g、低老本,大多數人買得起
h、沒需驅動,即插即用
於是我和合泰的相關人員進行溝通(這個溝通大略是16年10月初始的)。
經過屢次溝通,最終確定:以elink32-pro為原型,去掉一些沒用的東西、增加相關功能、由合泰半導體提供固件,我提供硬體及出產。
溝通中爭議最多的一個問題是RST引腳是否保留。由於我們使用stlink,jlink等時經常會發生以下問題:
這個問題能夠通過一下方式攻克:
但是還有很多問題是通過開發環境配置修改所不能攻克的,這時候就須要保留RST,然後把RST接在開發板上,就能完成下載,這個功能主要是波及以下幾種使用場景:
1、swd引腳復用(例如STM32F103的PA13和PA14在程序中被設置為其他功能),這時候須要編程器能對開發板進行硬體復位。
2、晶片工作狀態為低功耗或者睡眠模式,這時候須要編程器能對開發板進行硬體復位。
所以最終探討結果是依照合泰半導體廠商提供的方案來,畢竟他們是半導體廠商,考慮問題很嚴謹、完善。
方案定稿之後,簽訂了軟體使用合同與保密合同,接下來是合泰提供帶固件的晶片,我來做整體設計並完成PCB設計、焊接調試等詳細的工作。經過一個月的時長、出了幾個樣品,經過測試發現使用不順手,等各種問題。假如第一次構思的是直接在板子上加一個usb公頭,然後就能夠插在電腦上了,但是後續使用發現,這樣不是很方便,由於這樣的話就會導致宗旨板距離電腦獨有杜邦線長度的距離,假如杜邦線太短,挪動不太方便,甚至此時有些電腦的USB口在後面,這樣不方便用戶下載程序後觀察現象。而杜邦線太長則會不穩定,很多情況下,程序沒法下載是與杜邦線連接不穩定有關係的(在使用優盤狀的STLINK時,經常由於杜邦線太長或者接觸不良導致下載失敗,後來換成航空線直接焊接上去就能夠下載,說明這種stlink的io口驅動才能欠佳)。
考慮以上的缺少點,我決定使用此時最常見的micro-USB線來連接e-Link32和PC機。於是經過改版,e-Link32變成了下面這樣(手工焊接調試版):
其中白色的SH1.0端子和右側的5P彎針都是SWD接口。為啥要增加一個SH1.0端子呢,由於在有些電路里,保留2.54的插針都是對空間的浪費,而且插針是要占用PCB的雙面,對其他布線造成了影響。所以增加了SH1.0端子接口,以後大家做板也能夠使用這樣的埠、減小板子面積,方便調試。
這個版本e-Link32我贈送給五個人進行測試,大略是從16年12月份初始測試的、到17年8月份沒有人任何問題,所以我才批量了1000個。選用的是最可靠的PCB提供商和貼片廠商,做出的板子如下:
一個產品必需有完善的文檔。在量產的階段我也進行了相關文檔的編寫
詳細文檔能夠在淘寶寶貝詳情下載。大略詳細內容如下:
另外清晰的闡述了elink32如何連接開發板和電腦:
以及如何用elink32給帶20腳jtag接口的開發板下載程序:
此時能夠說完成了當時提出的這個需求,實現了具有以下功能的調試器:
經測試,可用於但不局限於以下晶片的調試:
STM32F0,STM32F1,STM32F2,STM32F3,STM32F4,STM32F7
STM32L0,STM32L4
LPC800XX,LPC1100XXX,LPC1200XXX,LPC1300XXX,LPC1500XXX,LPC1700XXX,LPC1800XXX,LPC4000XXX,LPC4300XXX,LPC5400XXX,
K60
HT32F16XX,HT32F52XXX
NV32F100XXX
EFM32XXX
W7500
列表有待完善。
有人提出了:帶串口的東西其實早有了,那麼你能夠去看看,網絡上也有很多教程教大家做這樣的下載器的,我能夠花一兩天時長依據教程完成這麼一個下載器,但我肯定沒有勇氣把他批量了放在市場上給大家用。一款工具性的東西須要經過長期的穩定性測試、還須要對加工工藝進行測試、這些流程都增加了產品的老本,但是卻是保證用戶買到有保證的產品的一個必需的投入。
兼容市面上絕大多數ARM Cortex-M系列單片機。
500ma3.3v對外供電電流,加自恢復二極體保駕護航。
半導體原廠提供固件與技術支持。
一個USB口完成下載調試串口通訊,一遍單步一遍查看串口數據,為你的電腦節省一個寶貴的USB口。
從16年測試,到17年初始量產,此時已經銷售了3000多個了,經測試非常穩定。
01首先聊一聊大家都知道進行單片機編程和計算機編程有個最大的差別就是單片機的資源非常的有限,並且對於大局部低端單片機而言都沒有作業系統。除了一些嵌入式級的晶片用了Linux系統外,?
彩蛋:最近有同學跟我要單片機的資料,我特意花幾個月時間,總結了我10年產品研發經驗,資料包幾乎覆蓋了C語言、單片機、模電數電、原理圖和PCB設計、單片機高級編程等等,非常適合初學者入門和進階。除此以外,再含淚分享我壓箱底的22個熱門開源項目,包含源碼+原理圖+PCB+說明文檔,不是市面上打包賣的那種課程,我認為教程多未必是好事,10年前我自學快,除了自身執行力以外,還有就是教程少。不要害羞做伸手黨,等你一個小紅點。後期我也會組建一些純技術交流的小圈子,讓大家能認識更多的大佬,有個好的圈子,你對行業的認知一定是最前沿的。