[小贴士]初始化任意对象的方法
原理解析在C和C++中,对象的实例都占用一定的存储空间,对于任意的对象,可以利用sizeof来获取其大小,并通过强制类型转换,实现对任意对象的初始化。
进阶阅读
对于不同大小的对象,虽然可以统一采用UINT32型变量作为长度计数器来实现循环,但对于大部分应用场合来说,我们需要初始化的对象都是长度小于255的小对象。因此,我们可以考虑为不同大小的对象编写不同的对应函数:
/***********************************************************
* 函数说明:对象初始化函数(小) *
* 输入: 对象指针,对象大小 *
* 输出: 对象指针 *
* 调用函数:无 *
***********************************************************/
void *Small_Object_Initial(void *pData,UINT8 chLength)
{
if ((pData == NULL) || (chLength == 0))
{
return NULL;
}
{
UINT8 chCounter = 0;
for (chCounter = 0;chCounter < chLength;chCounter++)
{
((BYTE *)pData) = 0;
}
}
return pData;
}
/***********************************************************
* 函数说明:对象初始化函数(中) *
* 输入: 对象指针,对象大小 *
* 输出: 对象指针 *
* 调用函数:无 *
***********************************************************/
void *Normal_Object_Initial(void *pData,UINT16 wLength)
{
if ((pData == NULL) || (wLength == 0))
{
return NULL;
}
{
UINT16 wCounter = 0;
for (wCounter = 0;wCounter < wLength;wCounter++)
{
((BYTE *)pData) = 0;
}
}
return pData;
}
/***********************************************************
* 函数说明:对象初始化函数(中) *
* 输入: 对象指针,对象大小 *
* 输出: 对象指针 *
* 调用函数:无 *
***********************************************************/
void *Large_Object_Initial(void *pData,UINT32 dwLength)
{
if ((pData == NULL) || (dwLength == 0))
{
return NULL;
}
{
UINT32 dwCounter = 0;
for (dwCounter = 0;dwCounter < dwLength;dwCounter++)
{
((BYTE *)pData) = 0;
}
}
return pData;
}
如果我们编写一个统一的函数Object_Initial(),通过对输入对象的大小进行判断以后,选择性的调用不同的函数,就可以实现类似C++函数多态的调用效果。这里,有一个问题,调用函数需要消耗堆栈和额外的代码周期,这在嵌入式系统开发中是不划算的。
如果我们使用了C++,很容易想到将Object_Initial()声明为inline函数;对于C来说,不妨尝试以下的宏来达到类似的效果:
# define MEMORY_INITIAL(__ADDR,__LEN) \
{\
if ((__LEN) < 0xFF)\
{\
Small_Object_Initial((__ADDR),(__LEN));\
}\
else if ((__LEN) < 0xFFFF)\
{\
Normal_Object_Initial((__ADDR),(__LEN));\
}\
else\
{\
Large_Object_Initial((__ADDR),(__LEN));\
}\
}
# define OBJECT_INITIAL(__OBJECT) MEMORY_INITIAL(&(__OBJECT),sizeof(__OBJECT)) 应用实例
假如,我们有一个结构体,这个结构体随着程序版本的发展,其长度和内容很容易发生变化。为了应对这种情况,我们通常将变化较少的部分放在结构体靠前的部分,对于扩展的内容,一律放在结构体的后面。对于这种结构体,使用上面的初始化方法就很方便了:
typedef struct DisplayDeviceInterface DDI;
struct DisplayDeviceInterface
{
DEVICE_ID_TYPE DeviceID; //设备ID BIT15用于区别设备是否为虚拟设备
UINT32 dwDeviceAddress; //设备地址
UINT32 dwDeviceSize; //设备允许的区域大小
DDI_RESET fnReset;
DDI_WRITE fnWriteByte;
DDI_READ fnReadByte;
DDI_WRITE_STREAM fnWriteStream;
DDI_READ_STREAM fnReadStream;
DDI_SET_ADDRESS fnSetAddress;
DDI_CLS fnCLS;
};
代码片断:
DDI Example;
OBJECT_INITIAL(Example) 有必要这么麻烦吗?
直接用
memset(addr,0,size) 我不是很喜欢用stdlib.h中的库函数。很多情况下,我犯了程序员的老_毛病,什么都喜欢自己写哈——说好听,就是好奇心过强。
谢谢你给我指出哈。
附上ICCV7提供的库函数代码:
void *memset(void *p, int c, size_t n)
{
unsigned char *cp = p;
while (n--)
*cp++ = c;
return p;
}
忍不住多嘴评论一下:
1、单纯从代码习惯来说,没有对传入的指针进行有效性判断,应该加入以下内容:
if (cp == NULL)
{
return NULL;
}
2、使用标准的size_t类型,显然符合ANSI_C标准,不过呢,个人觉得,对于小于255以内的结构进行初始化时,可能就有点浪费了。
3、可以将内存块初始化为指定内容,果然很好很强大,值得学习!!!!! 感谢 ATmega32 cortex-m3 ,特将代码改进如下:
# define MEMORY_INITIAL(__ADDR,__LEN,__SYMBLE) \
{\
if ((__LEN) < 0xFF)\
{\
Small_Object_Initial((__ADDR),(__LEN),(__SYMBLE));\
}\
else if ((__LEN) < 0xFFFF)\
{\
Normal_Object_Initial((__ADDR),(__LEN),(__SYMBLE));\
}\
else\
{\
Large_Object_Initial((__ADDR),(__LEN),(__SYMBLE));\
}\
}
# define OBJECT_INITIAL(__OBJECT,__SYMBLE) MEMORY_INITIAL(&(__OBJECT),sizeof(__OBJECT),(__SYMBLE))
/***********************************************************
* 函数说明:对象初始化函数(小) *
* 输入: 对象指针,对象大小 *
* 输出: 对象指针 *
* 调用函数:无 *
***********************************************************/
void *Small_Object_Initial(void *pData,UINT8 chLength,UINT8 chSymble)
{
BYTE *p = pData;
if ((pData == NULL) || (chLength == 0))
{
return NULL;
}
while(chLength--)
{
*p++ = chSymble;
}
return pData;
}
/***********************************************************
* 函数说明:对象初始化函数(中) *
* 输入: 对象指针,对象大小 *
* 输出: 对象指针 *
* 调用函数:无 *
***********************************************************/
void *Normal_Object_Initial(void *pData,UINT16 wLength,UINT8 chSymble)
{
BYTE *p = pData;
if ((pData == NULL) || (wLength == 0))
{
return NULL;
}
while(wLength--)
{
*p++ = chSymble;
}
return pData;
}
/***********************************************************
* 函数说明:对象初始化函数(中) *
* 输入: 对象指针,对象大小 *
* 输出: 对象指针 *
* 调用函数:无 *
***********************************************************/
void *Large_Object_Initial(void *pData,UINT32 dwLength,UINT8 chSymble)
{
BYTE *p = pData;
if ((pData == NULL) || (dwLength == 0))
{
return NULL;
}
while(dwLength--)
{
*p++ = chSymble;
}
return pData;
}
使用方法改变为:
OBJECT_INITIAL(Example,NULL); 以下蓝色文字由版主:Gorgon Meducer 于:2008-03-07,06:11:52 加入。<font color=black>请发贴人注意:本贴放在这分区不合适,即将移走
原来分区:AVR (原ourAVR.com) 技术论坛
即将移去的分区:傻孩子(Gorgon Meducer)专栏
移动执行时间:自本贴发表0小时后
任何的疑问或咨询,请可随时联系站长。谢谢你的支持!</font> 呵呵,我觉得为了节约函数堆栈中局部变量的一个?三个?字节:
size_t变为uint8_t
把一段简短的代码变得那么复杂而且是用大量的flash空间换取的,有点不值得。 对于你说的情况,可以用条件编译的方法选择性的编译用到的函数。这样就不浪费FLASH了。
最主要的好处是通过统一的宏调用即换取了代码的可移植性,又提高了代码执行效率,如果将以上的代码编译为.a文件,那么系统会自动根据代码中的需要连接对应的函数,这样就能实现FLASH空间的节省。 请教:在8位的单片机的速度怎么样?一直感觉这东西好使但总觉得在单片机中使用的话,好象施展不开似的. 我所有的代码都是首先在8位单片机上使用的。^_^放心吧。不过如果你在1M的系统时钟下使用,估计还是有点困扰的。 # define MEMORY_INITIAL(__ADDR,__LEN,__SYMBLE) \
{\
if ((__LEN) < 0xFF)\
{\
Small_Object_Initial((__ADDR),(__LEN),(__SYMBLE));\
}\
else if ((__LEN) < 0xFFFF)\
{\
Normal_Object_Initial((__ADDR),(__LEN),(__SYMBLE));\
}\
else\
{\
Large_Object_Initial((__ADDR),(__LEN),(__SYMBLE));\
}\
}
我还是有点不同意见。你的这个宏展开后是if、else if、else等语句,不是#if等的条件编译。这样实际上三个函数调用都会在代码中出现。__LEN应该是个变量,编译器不会聪明到实现知道你这个__LEN是否小于255,所以不会优化掉后面两个函数调用。这样一来本来只需调用一个函数的代码,实际上“调用”了三个函数。
你说的“那么系统会自动根据代码中的需要连接对应的函数,这样就能实现FLASH空间的节省。”就不行了。除非编译时间就决定了__LEN的大小,但我想这需要#if #elif这样的条件编译来实现。
我说得对吗? 恩,是的,你观察的很细致。的确存在这个问题。我正在想办法解决。
推荐你去读一下《Object-Oriented Programming With ANSI-C》我觉得你已经是时候给自己一个大的提升了。 期待你的新的方法。
谢谢你推荐的书,这本书我的硬盘上有,我会会好好读一下,就是英文版读得很累,不知道有没有中文版的。 中文版我们正在翻译,6月份推出。 期待6月的新书!!!!
页:
[1]