当前位置: 代码迷 >> 单片机 >> C语言中动态分配内存的有关问题
  详细解决方案

C语言中动态分配内存的有关问题

热度:131   发布时间:2016-04-28 14:57:11.0
C语言中动态分配内存的问题
各位好!

我现在有一个单片机项目,使用stm32f103系列单片机,并使用RS485和无线2.4G(nRF24L01)两种通信方式和客户端通信,一台MCU作服务端,其他作为客户端(每个客户端都有一个唯一识别的设备ID)。
我的设计是这样的:

服务端使用lwip网络协议栈和上位机通信,接收上位机的指令,然后服务端将上位机发来的数据发送到RS485总线或者通过无线2.4G直接发送到客户端。服务端保存了每个客户端设备的信息,包括:设备ID,通信方式,通道地址。其中设备ID用于唯一标识某个客户端设备,通信方式表示客户端设备是RS485方式通信还是无线2.4G方式通信,通道地址表示无线2.4G设备地址。每个客户端设备的信息都保存在服务端MCU的AT24C04中,客户端设备信息采用了一个结构体来保存,定义如下:


/**************** Device Structure *******************/
struct  device {
    u16 DeviceID;     //Device Identifier 
//char Name[10]; //Device Name which can be customized
    u8      IsRS485;  //Is this device using RS485 bus to send out data? 1:RS485 bus,0:nRF24L01 wireless 2.4G
u8 Address[5]; //Device address, for each nRF24L01 has a unique device address which consists of 5 bytes
};

typedef struct device DEVICE; //Define a new type replacing "struct device"

并且我使用宏定义了客户端设备最大数量(默认不超过30个):

#define MAX_DEVICE_CNT 0x1E /* Maximum Devices Available */


我定义了一个全局指针变量用来保存所有的客户端设备,在程序初始化时从AT24C04得到所有的设备:
全局指针变量和实际客户端设备的总数量:

u8 DEVICE_NBR=0; //全局变量保存已有的保存在AT24C04中的设备数量
DEVICE* ALL_DEVICE; //全局变量保存所有的设备信息


在初始化得到所有的客户端设备:

/*
 * 函数名:Get_Devices
 * 描述  :从AT24C02中得到全部的设备信息
 * 输入  :无
 * 输出  :无
 * 调用  :外部调用
 */
void Get_Devices(void)
{
u8 dev_num=0; //从AT24C04读取设备数量
I2C_EE_BufferRead(&dev_num, EEPROM_BASE_ADDR_DEVICE, 1);
if (dev_num!=0xff && dev_num<=MAX_DEVICE_CNT){//判断是否超出最大数量
u8 * result;
u8 i=0, j=0;
DEVICE *device;
DEVICE_NBR = dev_num; //保存总的设备数量

//DEVICE_DATA_LEN是结构体的数据长度,应该就是sizeof(DEVICE)

//动态分配内存保存从AT24C04中读取出来的所有设备数据
result=(u8*)malloc(dev_num * DEVICE_DATA_LEN); //最大30个设备,每个8字节数据,就是DEVICE_DATA_LEN
//result=(u8*)calloc(dev_num, DEVICE_DATA_LEN); //为一维数分配dev_num个元素,每个元素占8个字节

//分配ALL_DEVICE所需要的内存空间
ALL_DEVICE=(DEVICE*)malloc(dev_num * sizeof(DEVICE));
device=ALL_DEVICE;

//根据设备数量从AT24C04读取指定长度的数据并保存在result中
I2C_EE_BufferRead(result, EEPROM_BASE_ADDR_DEVICE+1, dev_num*DEVICE_DATA_LEN);

for(i=0;i<dev_num * DEVICE_DATA_LEN;i=i+DEVICE_DATA_LEN){
device->DeviceID=((result[i+1]&0x00FF)<<8)|(result[i]&0x00FF); //设备序号
device->IsRS485=result[i+2]; //通讯方式
for(j=0;j<5;j++){ //无线2.4G通道地址,5个字节
device->Address[j]=result[i+3+j];
}
device++;
}

free(result); //释放分配的空间
free(ALL_DEVICE);
}
}


我没有使用数组,是使用的动态分配内存的方式,原因是:AT24C04中保存了多少条设备信息就开辟多少内存来保存,而不用定义一个固定大小的数组,数组大小为设备最大总数量,也就是MAX_DEVICE_CNT。另外使用 ALL_DEVICE全局变量来保存所有的设备信息,需要用时就可以直接访问它。
可是问题来了,为什么我使用了free(ALL_DEVICE)之后,(ALL_DEVICE)还可以使用呢?
调用它的代码如下:

u8 i=0; //循环计数器
u8 rs485=0; //设备通信方式1:RS485总线,0:nRF24L01无线2.4G
u8 *addr=NULL; //保存设备地址
u16 device_id=0; //保存网络传来的设备信息
DEVICE * d=ALL_DEVICE; //根据网络传来的设备信息从列备列表中查找
device_id=((arg[2]&0x00FF)<<8)|(arg[1]&0x00FF); //得到要操作的设备ID
//for(d=ALL_DEVICE;d<ALL_DEVICE+DEVICE_NBR;d++)
for(i=0;i<DEVICE_NBR;i++)
{
if (device_id==d->DeviceID) //如果找到匹配的设备信息
{
rs485=d->IsRS485; //得到当前设备的数据传输方式
addr=d->Address; //得到当前设备的nRF24L01无线2.4G模块地址
break;
}
d++;
}
                if (rs485==1)//使用RS485方式通信
               {
                            //服务端处理
                }
                else   //无线2.4G方式
               {
                          //服务端 处理
               }


麻烦各位帮我看下,这样使用动态分配内存有没有问题,如果我不想使用固定长度的数组,有其他更好的方法吗?为什么我使用了free函数后,ALL_DEVICE还能用呢?谢谢各位了!
------解决方案--------------------
free后只是标记内存空闲,这时候你的指针就是野指针,虽然可以访问,但是不是合法的。举个简单例子,火车上座位,你是站票,但是偶尔有座位你也可以坐下,但是来人你得让,不让打架就出事了。