STM32
直播中

河神大人

8年用户 1481经验值
擅长:电源/新能源
私信 关注
[问答]

为什么C语言要避免使用Null指针呢

为什么C语言要避免使用Null指针呢?
指针作为数组的应用有哪些呢?

回帖(1)

李桂兰

2021-12-20 14:29:19
1、常量溢出

u16  adc_temp = 0;   //应为: u32  adc_temp = 0;
for(i=0;i<100;i++)
{
    adc_temp += usADC_CS1;  //usADC_CS1数组里面每个元素大于1000,导致adc_temp溢出
}
adc_temp /= 100;


1.2、数据范围溢出
将一个数拆分为N个随机数

for(i=0;i<200;i++)
{
                buffer = Random(); //申请一个随机数
                val -= buffer;     //当申请得到的 buffer > val; 结果将溢出
                if(val<0x100)         
                {
                                if(val)           //排除 val = 0;
                                {
                                        buffer[i+1] = val;
                                }
                                break;
                }
}


可更正为:

for(i=0;i<200;i++)
{
                if(val>255) //大于0xff分解
                {
                                buffer = Random();
                                val -= buffer;
                }
                else        //小于或等于0xff直接记录并退出
                {
                                buffer = val;
                                break;
                }
}
2、头文件中宏定义与声明

#ifndef __bsp_11_H
#define __bsp_11_H               

#include "stm32f10x.h"

#define  Data_CodeLine       140
//u8 Data_CodeLine = 140;   //宏定义Data_CodeLine 为140是可以的,但是当
                            //声明为u8类型的变量时,会报"多重定义"的错误

#endif


原因是当此头文件被不同C文件“Include”时,两个C文件都分别声明Data_CodeLine,导致多重定义。
采用宏定义可以避免此问题。
3、数据的进制

                  STMFLASH_Write(0x8019000,FlashBuff,10);
                  STMFLASH_Write(0x8019050,FlashBuff,10);
               
                         for(i=0;i<2;i++)
                        STMFLASH_Read(0x8019000+i*50,FlashBuff,10);  //应为i*0x50

原本第二次想读取0x8019050开始,连续10个半字大小的地址的数据。但是“i*50‘这里错误,应为”i*0x50“。否则将导致从0x8019032这个地址开始读取。

4、避免使用Null指针
举个例子,编译环境为visual studio 2015:

int main()
{
        int *p1, Num = 1;

        *p1 = Num;

        printf("*p1 = %d",*p1);

        getchar();
}


咋一看没问题,其实会报错:





这是因为,声明的指针“p1”,没有指向任一内存地址。在编译器里它默认指向“Null”,所以从逻辑上来说,对“p1”指向的内存地址取值,这本身不可取。但如果是MDK编译,是不会报错的,可见VS的编译器还是比较强大的。
延伸一下,链表的创建问题。
      链表的创建,一般是先声明链表结构体的指针变量,再通过“malloc(); ”申请链表结构体大小的内存空间,并将空间起始地址赋值给这个指针变量。然后便能通过指针变量,操作它指向的内存空间的数据。这里通过“malloc(); ”申请内存赋值给指针变量的过程,就相当于给指针指定一个内存地址,这样它就不会是一个指向“Null”的空指针了。
      除了使用“malloc(); ”,还有别的办法使链表结构体指针变量指向某一地址,完成链表的创建吗。有的,方法如下:

#include
#include


//创建链表结构体
typedef struct Node
{
        int Data;
        struct Node *Next;
}Node;

//声明链表结构体变量以及指针变量
Node  Head, Node1, Node2 ,Tail, *p4;

int main()
{
        //给链表结构体变量成员赋值
        Head.Data = 3;
        Head.Next = &Node1;

        Node1.Data = 11;
        Node1.Next = &Node2;

        Node2.Data = 22;
        Node2.Next = &Tail;
       
        Tail.Data = 0;
        Tail.Next = 0;

        //头结点地址赋值给链表结构体指针变量p4
        p4 = &Head;
        printf("p4->Data = %drn",p4->Data);

        p4 = p4->Next;
        printf("p4->Data = %drn", p4->Data);

        p4 = p4->Next;
        printf("p4->Data = %drn", p4->Data);

        p4 = p4->Next;
        printf("p4->Data = %drn", p4->Data);

        getchar();
}
编译运行结果为:





声明链表结构体变量的时候,变量的地址已经由系统分配完毕。
p4 = &Head;

当链表结构体指针变量指向头结点时,我们便能通过操作指针变量来操作头结点。
p4 = p4->Next;

当它改为指向指针域时,因为头结点指针域指向Node1,所以此时它指向Node1。
由“malloc(); ”申请内存创建的链表存储在动态存储区,使用灵活可删除。
而这种方法创建的链表存储在静态存储区,无法删除链表释放Ram。
5、char类型表示负数
在MDK下运行以下代码:

int main(void)
{

        volatile char  a1 = -1;
        volatile short a2 = 100;
        volatile int   a3 = 250;
       
       
        a3 -= a2*a1;
       
        while(1);
}


进入仿真,查看变量的值:





然后将“a1”的类型“char”改为“short”,问题解决。





后面找到问题了,是MDK编译器将“char”类型默认为“unsigned char”类型,可在MDK中修改:





6、指针作为数组的应用

uint8_t *pBuf;

pBuf[0] = NRF24L01_RW(*pBuf++);
pBuf[1] = NRF24L01_RW(*pBuf++);
pBuf[2] = NRF24L01_RW(*pBuf++);
pBuf[3] = NRF24L01_RW(*pBuf++);
pBuf[4] = NRF24L01_RW(*pBuf++);       
      
原意是想,每发送一个数据,将发送缓冲数组中对应元素改为接收返回值。
但这里将指针当作数组使用出了问题。
分析  “pBuf[0] = NRF24L01_RW(*pBuf++);”
执行  1、“NRF24L01_RW(*pBuf);”
         2、“pBuf++;”
         3、 “pBuf[0]”等于 “NRF24L01_RW(*pBuf);”执行完毕的返回值

这里的问题是,“pBuf[0]”的地址是”pBuf“吗?
并不是的,因为第二步中,执行了“pBuf++;”,所以“pBuf[0]”的地址是”pBuf+1“
也就是说“pBuf[0]”变成了”“pBuf[1]“

7、"i++" 与 "++i" 的问题

int main() {

        int arr[] = { 3,4,5 }, *p1 = arr,y;

        y = *p1++ + (*(p1+++1)<<1);
        printf("%drn",y);

        y = *p1;
        printf("%drn", y);

        getchar();
}
以上程序输出结果:
11
5
第一个等式,“*(p1+++1)”,"++"优先级大于"+",先计算“(*p1+1)”,整个等式计算完毕后,p1再自增
由于第一个等式“p1”自增2次,所以第二个等式结果为5。
所以第一个等式等价于:y = arr[0] + (arr[1]<<1);

6、LKT4106加密芯片的坑
一个小小的加密芯片也能遇到很多坑。。。在这里慢慢填上
(1)

   u8  aa = 0x01, bb = 0xf4;
   u16 sum = 0;
               
   sum = ((aa<<8)|bb)/10;

   while(1)
  {

  }
上述代码“sum”结果应该是“0x01f4”,但是LKT4106运行出来变成了0xf4。解决办法是分开一步步运行,不要写在一起。。。
(2)

   unsigned char aa = 0x11, bb = 0x22;
   unsigned long sum = 0;

   sum  = aa;
   sum |= bb<<8;

   while(1)
  {

  }
理论上sum的值为“0x00002211”,实际上变成了“0xffff2211”.解决办法:

         sum  = aa;
         sum |= (bb<<8)&0x0000ffff;
举报

更多回帖

发帖
×
20
完善资料,
赚取积分