搜索
bottom↓
回复: 18

[小贴士]初始化任意对象的方法

[复制链接]

出0入296汤圆

发表于 2008-3-3 17:39:51 | 显示全部楼层 |阅读模式
原理解析

    在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)[chCounter] = 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)[wCounter] = 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)[dwCounter] = 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))

出0入296汤圆

 楼主| 发表于 2008-3-3 17:43:56 | 显示全部楼层
应用实例

    假如,我们有一个结构体,这个结构体随着程序版本的发展,其长度和内容很容易发生变化。为了应对这种情况,我们通常将变化较少的部分放在结构体靠前的部分,对于扩展的内容,一律放在结构体的后面。对于这种结构体,使用上面的初始化方法就很方便了:

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)

出0入0汤圆

发表于 2008-3-3 17:51:37 | 显示全部楼层
有必要这么麻烦吗?

直接用
memset(addr,0,size)

出0入296汤圆

 楼主| 发表于 2008-3-3 17:58:41 | 显示全部楼层
我不是很喜欢用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、可以将内存块初始化为指定内容,果然很好很强大,值得学习!!!!!

出0入296汤圆

 楼主| 发表于 2008-3-3 18:15:32 | 显示全部楼层
感谢 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);

出0入296汤圆

 楼主| 发表于 2008-3-7 06:11:52 | 显示全部楼层
以下蓝色文字由版主:Gorgon Meducer 于:2008-03-07,06:11:52 加入。
<font color=black>请发贴人注意:
本贴放在这分区不合适,即将移走
原来分区:[1000]AVR (原ourAVR.com) 技术论坛
即将移去的分区:[1038]傻孩子(Gorgon Meducer)专栏
移动执行时间:自本贴发表0小时后

任何的疑问或咨询,请可随时联系站长。谢谢你的支持!
</font>

出0入0汤圆

发表于 2008-3-15 23:39:00 | 显示全部楼层
呵呵,我觉得为了节约函数堆栈中局部变量的一个?三个?字节:
size_t  变为  uint8_t  
把一段简短的代码变得那么复杂而且是用大量的flash空间换取的,有点不值得。

出0入296汤圆

 楼主| 发表于 2008-3-16 09:37:06 | 显示全部楼层
对于你说的情况,可以用条件编译的方法选择性的编译用到的函数。这样就不浪费FLASH了。
最主要的好处是通过统一的宏调用即换取了代码的可移植性,又提高了代码执行效率,如果将以上的代码编译为.a文件,那么系统会自动根据代码中的需要连接对应的函数,这样就能实现FLASH空间的节省。

出0入0汤圆

发表于 2008-3-16 09:41:03 | 显示全部楼层
请教:在8位的单片机的速度怎么样?一直感觉这东西好使但总觉得在单片机中使用的话,好象施展不开似的.

出0入296汤圆

 楼主| 发表于 2008-3-16 10:49:54 | 显示全部楼层
我所有的代码都是首先在8位单片机上使用的。^_^放心吧。不过如果你在1M的系统时钟下使用,估计还是有点困扰的。

出0入0汤圆

发表于 2008-3-16 13:26:14 | 显示全部楼层
# 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这样的条件编译来实现。

我说得对吗?

出0入296汤圆

 楼主| 发表于 2008-3-16 17:09:45 | 显示全部楼层
恩,是的,你观察的很细致。的确存在这个问题。我正在想办法解决。
推荐你去读一下《Object-Oriented Programming With ANSI-C》我觉得你已经是时候给自己一个大的提升了。

出0入0汤圆

发表于 2008-3-16 21:42:46 | 显示全部楼层
期待你的新的方法。
谢谢你推荐的书,这本书我的硬盘上有,我会会好好读一下,就是英文版读得很累,不知道有没有中文版的。

出0入296汤圆

 楼主| 发表于 2008-3-21 20:14:43 | 显示全部楼层
中文版我们正在翻译,6月份推出。

出0入0汤圆

发表于 2008-5-5 20:00:23 | 显示全部楼层
期待6月的新书!!!!
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-9 04:19

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表