[注意] : STM32固件库使用手册(中文版).pdf 这本手册是一本工具书,是用来查阅的
空指针:编译时不会报错,但是在执行程序是会报段错误
int *p = NULL;int *p = 0x0; *p = 100; //使用了空指针
空类型的指针(万能指针):可以接收任意类型的地址
void *p;
野指针:编译时不会报错,在执行程序时未知(不会报错)
int *p = NULL; //sizeof(int *) = ?
p = malloc(4); //0x12345678
*p = 100;
free(p);
printf("p = %pn", p); //p = 0x12345678
*p = 200; //使用了野指针 编译时不会报错,在执行程序时未知(不会报错)
//没有办法避免野指针,但是可以把野指针变为空指针
free(p);
p = NULL;
1、相关面试题:
1、sizeof 是一个函数吗? 不是一个函数,是一个运算符
2、C语言会把内存分为几个区? 5个
栈区 由编译器自动分配和释放,存放局部变量,函数的形参等,操作方式类似于数据结构中的栈,
堆区 需要程序员自己管理,手动开辟,手动释放,若程序员不释放,可能会造成内存泄漏,
全局区 全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域(.data),未初 始化的全局变量和未初始化的静态变量在相邻的另一块区域(.bss),程序结束后系统释放。
常量区 char *p = “hello”; 存放字符串常量,程序结束后由系统释放。
代码区 存放函数体的二进制代码。
栈的空间有限,堆是很大的自由存储区,程序在编译期对变量和函数分配内存都在栈上进行,且程序运行过程中函数调用时参数的传递也是在栈上进行。
2、位带位带:可以让程序在操作外设寄存器的地址时,以原子操作的方式操作
是把位带区的每一位都在一个地址别名,映射到位带别名区,将来程序员访问位带别名区的空间,就相当于访问位带区中的某一位
位带操作定义:CPU不能直接对位带区中的单个数据位位寻址,只能通过对位带别名区的访问(或读/写)实现对位带区单个数据位的访问(或读/写),这种操作被称为位带操作。
例如:想要把0x12345678这个地址的第9位置1
*(volatile unsigned int *)(0x12345678)|=(1<<9);
这条语句不是原子操作:不可中断的一个或者一系列操作, 也就是不会被线程调度机制打断的操作, 运行期间不会有任何的上下文切换
位带不是ST提供的功能,而是由ARM核心提供的,
打开
1MB 位带区 = 8M个位,而且在地址空间中会有4字节对齐所以,最后在位带别名区膨胀成了32MB的位带别名区。
注意:位域 (位段/位字段) 使用位域时一定是在无符号环境中使用
3、位带具体操作#define BITBAND(ADDR,BITNUM) ((ADDR&0xF0000000)+0x2000000+((ADDR&0xFFFFF)<<5)+BITNUM<<2) //通过上述公式可以把位带区的地址转换成位带别名区的地址 #define MEM_ADDR(ADDR) *(volatile unsigned int *)(ADDR) //通过上述转换,可以把十六进制数字转换成地址使用 #define BIT_BAND(ADDR,BITNUM) MEM_ADDR(BITBAND(ADDR,BITNUM)) //GPIO的输入和输出->串行接口的时序 #define GPIOA_IDR_ADDR (GPIOA_base + 0x08) #define GPIOA_ODR_ADDR (GPIOA_base + 0x0C) #define PAIn(n) BIT_BAND(GPIOA_IDR_ADDR, n) //输入 #define PAOut(n) BIT_BAND(GPIOA_ODR_ADDR, n) //输出 PAOut(9) = 1; //GPIOA_ODR |= (1 << 9); PAOut(10) = 0; //GPIOA_ODR &= ~(1 << 10); int ret = PAIn(7); //ret = (GPIOA_IDR >> 7) & 0x01;



