摘要:大家想过没有我们用keil写单片机的代码,你的函数啊、变量啊最终都放在了哪里?我们一直说的内存五区,到底是哪五区?到底放在芯片的哪个地方呢?还有为什么你学完C语言指针和结构体,32单片机里面的关于结构体指针的内容还是搞不清楚呢?如果你有这些问题,今天就带你研究研究!
这张图学过STM32单片机的小伙伴应该都不陌生,我们看到的STM32芯片已经是已经封装好的成品,主要由内核和片上外设组成。若与电脑类比,内核与外设就如同电脑上的CPU与主板、内存、显卡、硬盘的关系。芯片和外设之间通过各种总线连接。连接被控总线的是FLASH,RAM和片上外设,这些功能部件共同排列在一个4GB的地址空间内。上面这些张图是STM32F40XXX系列单片机的内存地址映射图。
我们的代码就是放在Flash里面(0x8000000~0x80FFFFF)。代码就是你写得各种函数,而在程序中声明的各种变量都放在RAM中,局部变量就是在函数运行完空间释放,全局变量就是程序运行完了再释放,可以这样简单的理解。
CPU使用的变量是存储在RAM里面的,要问我RAM是啥,RAM就是个芯片。就是上图的Block1的SRAM区。CPU是通过导线和RAM芯片连接的,然后可以通过导线往RAM芯片里面存储数据和读数据。首先RAM需要有个一开始的地址,对于STM32单片机来说开始地址是0x20000000,要问我为啥要规定地址。只有规定了地址CPU才好对数据进行存储,要是没有地址,瞎几把存,瞎几把取。。.。。.
1、变量1.定义一个int型的变量,通过打印可以看到这个变量存储的地址是:0x20000000。这也证明了我们内存的首地址是0x20000000。我们定义的value变量就放在这里。
2.再定义一个变量
通过打印可以看到这个变量存储的地址是:0x20000004。因为int类型在内存中占据4个字节,所以第二个变量就存放在0x20000004这个地方。
综上所述,定义的两个变量在内存里面是下面这样子。
0x2000 0000地址里面存储的是 0
0x2000 0004地址里面存储的是 1
2、指针变量定义指针其实和定义变量一样的,只不过变量名前头有个*
下面就定义一个int型的指针变量,变量的名字是p。然后有人会问,为啥变量名字前面加个*就是指针了?
答:搞C语言那帮家伙们规定的。
定义指针和定义变量一样,然后可以定义各种类型的。
然后记住一句话:
“指针这个变量是存变量的地址的! 指针这个变量是存变量的地址的! 指针这个变量是存变量的地址的!”所以给指针赋值自然是把变量的地址给它。
#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的地址复制给这个指针
printf(“Address of a: %p
”,p);//打印下这个指针指向的地址
while(1)
{
}
}
一般什么类型的指针变量就应该赋值什么类型变量的地址。如再定义个char型
#include “sys.h”#include “led.h”#include “delay.h”#include “usart.h”int value = 0;
int value2 = 1;
int *p;//定义一个指针char value3=1;
char *q;
int main(void)
{
uart_init(115200);//串口初始化
delay_init();
p=&value;//把变量value的地址复制给这个指针
q=&value3;//把变量value的地址复制给这个指针
printf(“Address of a: %p
”,q);//打印下这个指针指向的地址
while(1)
{
}
}
那些规定C语言的大佬弄出来指针这个玩意有啥用?
3、指针有啥用?1.咱先使用下指针,然后具体有啥用就自己体会了。前面咱把一个变量的地址赋值给了指针了,然后搞C语言的那帮家伙们又规定。*{指针变量名} :代表了这个指针所指向的变量。
啥意思呢?
对照下面的程序p=&value,p记录的就是变量value的地址, 然后*p就代表value。
#include “sys.h”#include “led.h”#include “delay.h”#include “usart.h”int value = 0;
int *p;//定义一个指针
int main(void)
{
uart_init(115200);//串口初始化
delay_init();
p=&value;//把变量value的地址复制给指针变量p
printf(“Address of a: %d
”,value);
printf(“Address of b: %d
”,*p);
while(1)
{
}
}
有人会想。。.。。.就这?
有人觉得多此一举?
其实我一开始也是这样想的。。.。。.
既然 * p就代表value,那么* p=XXXX
不就是相当于value=XXXX
看看下面这个例子
#include “sys.h”#include “led.h”#include “delay.h”#include “usart.h”int value = 0;
int *p;//定义一个指针 int main(void)
{
uart_init(115200);//串口初始化
delay_init();
p=&value;//把变量value的地址复制给指针变量p
printf(“value of a: %d
”,value);
*p=520;
printf(“value of b: %d
”,value);
while(1)
{
}
}
还是没感觉到指针有啥用?别着急,先把基本的知识点学完哈。没有最基本的知识储备是不可以的,因为厚积而薄发!
见过返回值是指针的函数没?
4、函数指针先看一下,如果感觉不理解就接着往下看
#include “sys.h”#include “led.h”#include “delay.h”#include “usart.h”int value = 0;
int *p;//定义一个指针
int *function(void)
{
return &value;//把value的地址返回
}
int main(void)
{
uart_init(115200);//串口初始化
delay_init();
p=function();//调用函数,其实就是把value的地址赋值给了p
printf(“Address1 of a: %p
”,&value);//打印value的地址
printf(“Address2 of a: %p
”,p);//打印p所代表的地址
while(1)
{
}
}
很多人用过返回值是int、char的函数,但是在int,char 后面加个*
估计对于初学者没有用过。其实就是指针之间赋值。下面就是把p(int*类型的指针) 代表的地址赋值给q
变量之间可以互相赋值吧,指针之间也一样,可以互相之间赋值。
其实和上面是一样的道理,那个函数function返回值是一个int*类型的指针,然后赋值给了p而已
#include “sys.h”#include “led.h”#include “delay.h”#include “usart.h”int value = 0;
int *p;//定义一个指针int *q;//定义一个指针
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)
{
}
}
姑且再问一句,函数名字是啥?
咱们都知道这样调用函数
#include “sys.h”#include “led.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 “led.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)
{
}
}
这里采用了函数指针
先记住一句话
“函数名就是这个函数的地址! 函数名就是这个函数的地址! 函数名就是这个函数的地址!”既然是地址,那么这个地址应该可以赋值给一个指针。因为是函数的地址,所以咱定义的指针也一定是一个函数类型的。
上面的函数void function()是一个没有返回值,没有形参的函数。那么咱需要定义一个这种的指针类型,其实就是void (*指针变量名字,随意写) ()。上面写的是 void (*fun)(); fun就是一个函数类型的指针,是一个没有返回值,没有形参的函数指针。
咱可以把这种函数赋值给这个指针变量。就是上面的fun=function。那么这个函数指针便代表了那个函数fun就等同于function。所以调用 fun(); 就等同于调用function()。
如果函数有形参怎么办? 好办,它有咱就+
#include “sys.h”#include “led.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)
{
}
}
如果函数有返回值怎么办?照+不误
#include “sys.h”#include “led.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)
{
}
}
总结一下
指针呢其实基本的也就是上面那些,指针就是用来记录变量的地址的。或是做地址之间的传递的。
&代表取地址符。
*代表取数据。
&{变量名} :就是把这个变量的地址取出来。
*{指针变量名} :就是把这个指针所代表的地址里面的存的值取出来”下面看一些比较常见的应用。把数组的地址赋值给指针,然后用指针操作数组
#include “sys.h”#include “led.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;//将数组名赋值给指针变量p,p就指向数组temp的首地址
printf(“value0 = %d
”,*p); //p就代表数组的第一个数据的地址
printf(“value1 = %d
”,*(p+1));//p+1就代表数组的第二个数据的地址
printf(“value2 = %d
”,*(p+2));//p+2就代表数组的第三个数据的地址
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、函数的形参是一个指针#include “sys.h”#include “led.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)
{
}
}
以上的指针的基本知识,多练习几遍就可以。指针真正的应用是在于代码的封装。可能对于初学者感受不到其作用,但是当你成为真正的开发人员。你会发现把好多功能函数封装起来,然后留出接口来调用是以后必不可少的。
封装的时候会大量的使用指针、函数指针、结构体指针等,怎么说呢!90%的程序员敲的是字母,写的是代码。当你开始封装的时候,你写的便是思想,但是需要一定的基础知识储备才能达到。
上一篇:SM32MCU支持的MAC地址Hash过滤
下一篇:CANFD总线异构通讯简单实例
推荐阅读最新更新时间:2024-11-03 12:29
设计资源 培训 开发板 精华推荐
- STEVAL-ISV020V1,用于 SPV1050 ULP 能量收集器和电池充电器的评估板 - 降压-升压配置
- AR0238CSSC12SHRAH3-GEVB:2 MP Sunex DSL945D 1/3" iBGA CIS HB 评估板
- OP184ESZ-REEL 3V 单电源、50Hz 至 60Hz 有源陷波滤波器和假接地的典型应用
- 使用 Analog Devices 的 LTC6262ITS8 的参考设计
- LT6402-12 的典型应用 - 300MHz 低失真、低噪声差分放大器 / ADC 驱动器 (AV = 12dB)
- S32G2车联网参考设计
- 电子圣诞树
- LT3089EFE 线性稳压器的典型应用电路,用于宽安全工作区电源
- #第五届立创电子设计大赛#一款语音控制的显示屏挂灯
- 优联HHKB-K375S带F区和拨码
- 得捷第二季Follow me第2期来袭,一起解锁功能强大且灵活的【Arduino UNO R4 WiFi】
- Hercules DIY创意大搜集!比比看,谁的设计最“安全”?
- 用“芯”做好表|英飞凌智能手表解决方案
- 抢楼有礼:看直播,深入了解ST最新 MEMS气压计原理、操作、防水结构设计
- 下载文章:“深入了解信号发生器”丰富知识赢取好礼
- DEYISUPPORT TI 大咖级工程师在这里,等你来约!
- ADI 新一期《模拟对话》上线,免费下载ing!
- ADI 系统方案精选【用于多路抽头输出隔离电源的简易解决方案】
- 是德科技有奖直播:示波器基础培训
- 报名Keysight感恩月,天天抽示波器、直流电源、万用表……