摘要:大家想過沒有我們用keil寫單片機(jī)的代碼,你的函數(shù)啊、變量啊最終都放在了哪里?我們一直說的內(nèi)存五區(qū),到底是哪五區(qū)?到底放在芯片的哪個(gè)地方呢?還有為什么你學(xué)完C語言指針和結(jié)構(gòu)體,32單片機(jī)里面的關(guān)于結(jié)構(gòu)體指針的內(nèi)容還是搞不清楚呢?如果你有這些問題,今天就帶你研究研究!
這張圖學(xué)過STM32單片機(jī)的小伙伴應(yīng)該都不陌生,我們看到的STM32芯片已經(jīng)是已經(jīng)封裝好的成品,主要由內(nèi)核和片上外設(shè)組成。若與電腦類比,內(nèi)核與外設(shè)就如同電腦上的CPU與主板、內(nèi)存、顯卡、硬盤的關(guān)系。芯片和外設(shè)之間通過各種總線連接。連接被控總線的是FLASH,RAM和片上外設(shè),這些功能部件共同排列在一個(gè)4GB的地址空間內(nèi)。上面這些張圖是STM32F40XXX系列單片機(jī)的內(nèi)存地址映射圖。
我們的代碼就是放在Flash里面(0x8000000~0x80FFFFF)。代碼就是你寫得各種函數(shù),而在程序中聲明的各種變量都放在RAM中,局部變量就是在函數(shù)運(yùn)行完空間釋放,全局變量就是程序運(yùn)行完了再釋放,可以這樣簡單的理解。
CPU使用的變量是存儲(chǔ)在RAM里面的,要問我RAM是啥,RAM就是個(gè)芯片。就是上圖的Block1的SRAM區(qū)。CPU是通過導(dǎo)線和RAM芯片連接的,然后可以通過導(dǎo)線往RAM芯片里面存儲(chǔ)數(shù)據(jù)和讀數(shù)據(jù)。首先RAM需要有個(gè)一開始的地址,對于STM32單片機(jī)來說開始地址是0x20000000,要問我為啥要規(guī)定地址。只有規(guī)定了地址CPU才好對數(shù)據(jù)進(jìn)行存儲(chǔ),要是沒有地址,瞎幾把存,瞎幾把取。。.。。.
1、變量1.定義一個(gè)int型的變量,通過打印可以看到這個(gè)變量存儲(chǔ)的地址是:0x20000000。這也證明了我們內(nèi)存的首地址是0x20000000。我們定義的value變量就放在這里。
2.再定義一個(gè)變量
通過打印可以看到這個(gè)變量存儲(chǔ)的地址是:0x20000004。因?yàn)閕nt類型在內(nèi)存中占據(jù)4個(gè)字節(jié),所以第二個(gè)變量就存放在0x20000004這個(gè)地方。
綜上所述,定義的兩個(gè)變量在內(nèi)存里面是下面這樣子。
0x2000 0000地址里面存儲(chǔ)的是 0
0x2000 0004地址里面存儲(chǔ)的是 1
2、指針變量定義指針其實(shí)和定義變量一樣的,只不過變量名前頭有個(gè)*
下面就定義一個(gè)int型的指針變量,變量的名字是p。然后有人會(huì)問,為啥變量名字前面加個(gè)*就是指針了?
答:搞C語言那幫家伙們規(guī)定的。
定義指針和定義變量一樣,然后可以定義各種類型的。
然后記住一句話:
“指針這個(gè)變量是存變量的地址的! 指針這個(gè)變量是存變量的地址的! 指針這個(gè)變量是存變量的地址的!”所以給指針賦值自然是把變量的地址給它。
#include “sys.h”#include “led.h”#include “delay.h”#include “usart.h”int value = 0;
int value2 = 1;
int *p;
int main(void)
{
uart_init(115200);
delay_init();
p=&value;//把變量value的地址復(fù)制給這個(gè)指針
printf(“Address of a: %p
”,p);//打印下這個(gè)指針指向的地址
while(1)
{
}
}
一般什么類型的指針變量就應(yīng)該賦值什么類型變量的地址。如再定義個(gè)char型
#include “sys.h”#include “l(fā)ed.h”#include “delay.h”#include “usart.h”int value = 0;
int value2 = 1;
int *p;//定義一個(gè)指針char value3=1;
char *q;
int main(void)
{
uart_init(115200);//串口初始化
delay_init();
p=&value;//把變量value的地址復(fù)制給這個(gè)指針
q=&value3;//把變量value的地址復(fù)制給這個(gè)指針
printf(“Address of a: %p
”,q);//打印下這個(gè)指針指向的地址
while(1)
{
}
}
那些規(guī)定C語言的大佬弄出來指針這個(gè)玩意有啥用?
3、指針有啥用?1.咱先使用下指針,然后具體有啥用就自己體會(huì)了。前面咱把一個(gè)變量的地址賦值給了指針了,然后搞C語言的那幫家伙們又規(guī)定。*{指針變量名} :代表了這個(gè)指針?biāo)赶虻淖兞俊?/p>
啥意思呢?
對照下面的程序p=&value,p記錄的就是變量value的地址, 然后*p就代表value。
#include “sys.h”#include “l(fā)ed.h”#include “delay.h”#include “usart.h”int value = 0;
int *p;//定義一個(gè)指針
int main(void)
{
uart_init(115200);//串口初始化
delay_init();
p=&value;//把變量value的地址復(fù)制給指針變量p
printf(“Address of a: %d
”,value);
printf(“Address of b: %d
”,*p);
while(1)
{
}
}
有人會(huì)想。。.。。.就這?
有人覺得多此一舉?
其實(shí)我一開始也是這樣想的。。.。。.
既然 * p就代表value,那么* p=XXXX
不就是相當(dāng)于value=XXXX
看看下面這個(gè)例子
#include “sys.h”#include “l(fā)ed.h”#include “delay.h”#include “usart.h”int value = 0;
int *p;//定義一個(gè)指針 int main(void)
{
uart_init(115200);//串口初始化
delay_init();
p=&value;//把變量value的地址復(fù)制給指針變量p
printf(“value of a: %d
”,value);
*p=520;
printf(“value of b: %d
”,value);
while(1)
{
}
}
還是沒感覺到指針有啥用?別著急,先把基本的知識(shí)點(diǎn)學(xué)完哈。沒有最基本的知識(shí)儲(chǔ)備是不可以的,因?yàn)楹穹e而薄發(fā)!
見過返回值是指針的函數(shù)沒?
4、函數(shù)指針先看一下,如果感覺不理解就接著往下看
#include “sys.h”#include “l(fā)ed.h”#include “delay.h”#include “usart.h”int value = 0;
int *p;//定義一個(gè)指針
int *function(void)
{
return &value;//把value的地址返回
}
int main(void)
{
uart_init(115200);//串口初始化
delay_init();
p=function();//調(diào)用函數(shù),其實(shí)就是把value的地址賦值給了p
printf(“Address1 of a: %p
”,&value);//打印value的地址
printf(“Address2 of a: %p
”,p);//打印p所代表的地址
while(1)
{
}
}
很多人用過返回值是int、char的函數(shù),但是在int,char 后面加個(gè)*
估計(jì)對于初學(xué)者沒有用過。其實(shí)就是指針之間賦值。下面就是把p(int*類型的指針) 代表的地址賦值給q
變量之間可以互相賦值吧,指針之間也一樣,可以互相之間賦值。
其實(shí)和上面是一樣的道理,那個(gè)函數(shù)function返回值是一個(gè)int*類型的指針,然后賦值給了p而已
#include “sys.h”#include “l(fā)ed.h”#include “delay.h”#include “usart.h”int value = 0;
int *p;//定義一個(gè)指針int *q;//定義一個(gè)指針
int main(void)
{
uart_init(115200);//串口初始化
delay_init();
p=&value;//把value的地址賦值給了p
q=p;//把p代表的地址給q
printf(“Address1 of a: %p
”,&value);//打印value的地址
printf(“Address2 of a: %p
”,q);//打印p所代表的地址
while(1)
{
}
}
姑且再問一句,函數(shù)名字是啥?
咱們都知道這樣調(diào)用函數(shù)
#include “sys.h”#include “l(fā)ed.h”#include “delay.h”#include “usart.h”void function()
{
printf(“zhiguoxin
”);
}
int main(void)
{
uart_init(115200);//串口初始化
delay_init();
function();
while(1)
{
}
}
但是這樣的見過沒
#include “sys.h”#include “l(fā)ed.h”#include “delay.h”#include “usart.h”void (*fun)();
void function()
{
printf(“zhiguoxin
”);
}
int main(void)
{
uart_init(115200);//串口初始化
delay_init();
fun = function;
fun();
while(1)
{
}
}
這里采用了函數(shù)指針
先記住一句話
“函數(shù)名就是這個(gè)函數(shù)的地址! 函數(shù)名就是這個(gè)函數(shù)的地址! 函數(shù)名就是這個(gè)函數(shù)的地址!”既然是地址,那么這個(gè)地址應(yīng)該可以賦值給一個(gè)指針。因?yàn)槭呛瘮?shù)的地址,所以咱定義的指針也一定是一個(gè)函數(shù)類型的。
上面的函數(shù)void function()是一個(gè)沒有返回值,沒有形參的函數(shù)。那么咱需要定義一個(gè)這種的指針類型,其實(shí)就是void (*指針變量名字,隨意寫) ()。上面寫的是 void (*fun)(); fun就是一個(gè)函數(shù)類型的指針,是一個(gè)沒有返回值,沒有形參的函數(shù)指針。
咱可以把這種函數(shù)賦值給這個(gè)指針變量。就是上面的fun=function。那么這個(gè)函數(shù)指針便代表了那個(gè)函數(shù)fun就等同于function。所以調(diào)用 fun(); 就等同于調(diào)用function()。
如果函數(shù)有形參怎么辦? 好辦,它有咱就+
#include “sys.h”#include “l(fā)ed.h”#include “delay.h”#include “usart.h”void (*fun)(int a);
void function(int value)
{
printf(“value= %d
”,value);
}
int main(void)
{
uart_init(115200);//串口初始化
delay_init();
fun = function;//把function賦值給fun
fun(520);//fun就等同于function
while(1)
{
}
}
如果函數(shù)有返回值怎么辦?照+不誤
#include “sys.h”#include “l(fā)ed.h”#include “delay.h”#include “usart.h”int res;
int (*fun)(int a);
int function(int value)
{
return value;
}
int main(void)
{
uart_init(115200);//串口初始化
delay_init();
fun = function;//把function賦值給fun
res = fun(520);//fun就等同于function
printf(“res = %d”,res);
while(1)
{
}
}
總結(jié)一下
指針呢其實(shí)基本的也就是上面那些,指針就是用來記錄變量的地址的。或是做地址之間的傳遞的。
&代表取地址符。
*代表取數(shù)據(jù)。
&{變量名} :就是把這個(gè)變量的地址取出來。
*{指針變量名} :就是把這個(gè)指針?biāo)淼牡刂防锩娴拇娴闹等〕鰜怼毕旅婵匆恍┍容^常見的應(yīng)用。把數(shù)組的地址賦值給指針,然后用指針操作數(shù)組
#include “sys.h”#include “l(fā)ed.h”#include “delay.h”#include “usart.h”char temp[3]={1,2,3};
char *p;
int main(void)
{
uart_init(115200);//串口初始化
delay_init();
p=temp;//將數(shù)組名賦值給指針變量p,p就指向數(shù)組temp的首地址
printf(“value0 = %d
”,*p); //p就代表數(shù)組的第一個(gè)數(shù)據(jù)的地址
printf(“value1 = %d
”,*(p+1));//p+1就代表數(shù)組的第二個(gè)數(shù)據(jù)的地址
printf(“value2 = %d
”,*(p+2));//p+2就代表數(shù)組的第三個(gè)數(shù)據(jù)的地址
printf(“temp[0] = %d
”,p[0]);//p[0]等同于temp[0]
printf(“temp[1] = %d
”,p[1]);//p[1]等同于temp[1]
printf(“temp[2] = %d
”,p[2]);//p[2]等同于temp[2]
while(1)
{
}
}
5、函數(shù)的形參是一個(gè)指針#include “sys.h”#include “l(fā)ed.h”#include “delay.h”#include “usart.h”char temp[3]={1,2,3};
void function(char *value)
{
printf(“value0 = %d
”,value[0]);
printf(“value1 = %d
”,value[1]);
printf(“value2 = %d
”,value[2]);
}
int main(void)
{
uart_init(115200);//串口初始化
delay_init();
function(temp);
while(1)
{
}
}
以上的指針的基本知識(shí),多練習(xí)幾遍就可以。指針真正的應(yīng)用是在于代碼的封裝??赡軐τ诔鯇W(xué)者感受不到其作用,但是當(dāng)你成為真正的開發(fā)人員。你會(huì)發(fā)現(xiàn)把好多功能函數(shù)封裝起來,然后留出接口來調(diào)用是以后必不可少的。
封裝的時(shí)候會(huì)大量的使用指針、函數(shù)指針、結(jié)構(gòu)體指針等,怎么說呢!90%的程序員敲的是字母,寫的是代碼。當(dāng)你開始封裝的時(shí)候,你寫的便是思想,但是需要一定的基礎(chǔ)知識(shí)儲(chǔ)備才能達(dá)到。
本文編輯轉(zhuǎn)載,轉(zhuǎn)載目的在于傳遞更多信息,并不代表本網(wǎng)贊同其觀點(diǎn)和對其真實(shí)性負(fù)責(zé)。版權(quán)歸原作者所有,如涉及作品內(nèi)容、版權(quán)和其它問題,請聯(lián)系我們第一時(shí)間刪除內(nèi)容!
編輯:jq
-
單片機(jī)
+關(guān)注
關(guān)注
6040文章
44594瀏覽量
636967 -
RAM
+關(guān)注
關(guān)注
8文章
1369瀏覽量
114826 -
STM32
+關(guān)注
關(guān)注
2270文章
10915瀏覽量
356788 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4344瀏覽量
62813 -
代碼
+關(guān)注
關(guān)注
30文章
4810瀏覽量
68830
原文標(biāo)題:干貨|手把手教你寫單片機(jī)的指針
文章出處:【微信號(hào):FANYPCB,微信公眾號(hào):凡億PCB】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論