打印

深入浅出: C 与 C++ 中“结构直接复制”的探讨

[复制链接]
楼主: highgear
手机看帖
扫描二维码
随时随地手机跟帖
61
本帖最后由 刘前辈 于 2011-5-13 19:39 编辑

感谢老乡,59楼的程序改过了,可行?

把下面的DATA存储区说明修改成EEROM 区就行了。
DBYTE[pSrc] = DBYTE[pDst];

要不我拿个STC的EEROM 程序帮你改?反正都是学生作业,学生做完了还可以写论文;这种实战程序,评委都不懂的,只好给论文优秀。

有点错更好啊,BUG越多越好,查错才增长能力呢。一帆风顺学生论文还没内容可写。


//

使用特权

评论回复
62
aihe| | 2011-5-13 19:52 | 只看该作者




实际效果出来了,你那个程序比我那个多用了20个CODE
因为我去掉了idata

在Keil下面如果去掉idata也会这样效果。。。
你自己试一下就知道了
我再重申一下,这个是写EEPROM的程序,没办法给你评优
你再想一下,如果数据长度是200,你你那个程序能正常运行吗?

使用特权

评论回复
63
123jj| | 2011-5-14 13:34 | 只看该作者
俺在51楼已说过了,aihe老师的程序已是最优化的了,有截图为证。

可惜,aihe老师不相信俺~~~  :L :L :L


43楼 aihe老师的程序已是最优化的了,赞一个~~~

......

123jj 发表于 2011-5-13 12:11

使用特权

评论回复
64
刘前辈| | 2011-5-14 13:40 | 只看该作者
本帖最后由 刘前辈 于 2011-5-14 15:07 编辑

62楼 aihe 朋友:没关系,小事一桩,很多方法,变通;
我原来不懂STM8,还不如我学生呢。学生说他查了一晚上资料就入门了。

1、你把下面的指针定义前面加上 @near ,也就是:

@near   unsigned char *pDstAddr = (unsigned char *)pDst;
@near   unsigned char *pSrcAddr = (unsigned char *)pSrc;

其它(你的程序)都不变,试试看什么效果;如果有效,一定告知。我好通知学生。——我实在不愿安装什么STM8开发环境。只能看看书了。这是把3字节指针化为2字节,编译结果按照C编译器规则,肯定简化。——这与Keil C51 使用idata 原理一样。

还有,书上都有的程序,我觉得还是参考书上的好;书上的标准程序是厂家提供的,时序上肯定测试多少遍了。咱们用户只管调用,恐怕不能随便化简吧。即使自己的程序好不容易通过,心里也没底。谁知道芯片时序是什么样?——换个环境程序又不灵了,怎么办?

     下面是书上的程序,要写200个字节更简单了;利用成块写入功能,按照128字节一个单位块来写,——又快又简单。厂家提供的现成程序,一天就完成了。
    不用自己下那么大工夫研究。

      下面是厂家提供的按字节写EEROM操作标准程序。



需要成块编写程序请告知。有必要把电子书发给你。(我今天看了一上午就大概明白了。)   
          我觉得你应该也有。而且肯定参考过比我更详细的资料。——不明白为什么要自己写。我绝对写不过厂家——除非我在厂家做测试。
      不过添加 @near 是我自己认为的,keil  C实验出来的。

        厂家程序写的真是无可挑剔,没有3字节指令定义的情况。——没地方需要加@near 。


  

       、、

使用特权

评论回复
65
aihe| | 2011-5-14 15:34 | 只看该作者
真是的还不清楚吗?
@near和XDATA一样的意思
idata只能用在RAM的最前256字节,超过这个数字出现的结果无法预料?!
再说一下,哪个MCU的EEROM在idata的取址范围里?!

使用特权

评论回复
66
刘前辈| | 2011-5-14 16:10 | 只看该作者

换 @tiny 试试 。

本帖最后由 刘前辈 于 2011-5-14 20:30 编辑

aihe你说得对,我的@near是加在你自己定义的指针pDstAddr前面的 ,仅仅是说明你这个指针是个指向xdata  区域 数据的指针,它只需要2字节分配(如果不加,那编译器默认指针3字节指针。)STM8编译器把它分配在哪,是编译器的事,与用户无关,这2个指针指向EEROM地址 pDst ,或 pSrc。 和EEROM 读写的数值没关系,它只是个地址值,例如4000H。——它指向4000H,* pDstAddr 从4000H取数值,和pDstAddr 分配放在哪(xdata / idata ) 没有关系,这和所有C语言概念一样。
     也许应该像下面这样:

unsigned char     *   @near     pDstAddr = (unsigned char *)pDst ;

如果你希望分配在256字节RAM区内,应该用@tiny ,关键是@near 或者 @tiny 的位置。keil C很简单,  如果形如
xdata  int  * idata  ptr;

说明ptr指向 xdata 区的 int ,而ptr在data 区。显然数据操作在Xdata区。 STM8  我无法实验,看书上意思一样。

还有,我理解的没什么偏差,我看过书上的标准程序,没错。

你需要发给你看看,我以为你有。所以没贴。

  @eeprom     unsigned char     * pDstAddr = (unsigned char *)pDst ;
  unsigned char     @near    * pDstAddr = (unsigned char *)pDst ;

都可能对,但意义不一样。

等一等,我明白了。主要EEROM是什么区的问题?好像书上讲明了。
还有一点。请问aihe ,你的2个指针都指向EEROM? 那你的程序好像没通过?A,B  俩个指针都指向EEROM,*A=*B;能行?可能还是要通过中间缓存。我认为至少pSrc不能是EEROM区。

@eeprom    unsigned char  * pDstAddr = (unsigned char *)pDst;
  @near   unsigned char *pSrcAddr = (unsigned char *)pSrc;

第二个@near 在64Kflash下是默认的,不用写也行。









、、

使用特权

评论回复
67
aihe| | 2011-5-14 21:05 | 只看该作者
*A=*B能行,但A[n]=B[n]就不一定行

你改的快,我在朋友家帮忙修东西,顺便腐败一下,没时间,截图都准备好了,只是没贴出来
你再看看我第二次说的,怎么调用这个程序,你就明白,我有可能从RAM写到EEROM,也有可能从FLASH写到EEROM,有可能是整个结构体写入,例如首次开机初始化,有可能只写1、2个字节用来修改部分参数还有可能接受外部的一次性修改,但是我并不需要从EEROM到EEROM,这个没必要

使用特权

评论回复
68
刘前辈| | 2011-5-14 23:12 | 只看该作者
本帖最后由 刘前辈 于 2011-5-14 23:15 编辑

明白了。书上3种方式都有范例。明天贴给你,我觉得加入下面语句很重要。你没加是有原因?

/* 设置编程时间,FIX =1:编程时间固定为标准编程时间tprog。*/
FLASH_CR1 &= (unsigned char)(~0x01);
FLASH_CR1 |= 0x01;


A[n]=B[n]; 是要修改前面形参才可以的。例如:DBYTE 初始指向4000H而不是0000H。

2个指针不是同时指向EEROM就好办了。按书上来,和你写的也差不多,书上是按照存储区不同来区分EEROM的;例如写入4000H一字节就至少要3ms 。优化不优化关系不大。能缩减50个字节,效率一样。


、、

使用特权

评论回复
69
aihe| | 2011-5-14 23:38 | 只看该作者
编程时间在初始化中,不放在这里更好,你说呢?
还有无论@tiny放在哪里编译都通不过的。

在Keil中用idata只能在局限范围内使用,你试试看吧。

使用特权

评论回复
70
刘前辈| | 2011-5-15 13:41 | 只看该作者
本帖最后由 刘前辈 于 2011-5-15 13:42 编辑

已经知道代码开销由指针引起,避免很容易:(EEROM段设置很简单)

void EE_Wrait(unsigned int pSrc, unsigned int pDst,unsigned char ucLength)      //写数据内置存储器
{        //从源地址写入到----目的地址----长度
    unsigned char ucCount;
      
   // 对数据EEPROM进行解锁
   do
   {
     FLASH_DUKR = 0xae;                    // 写入第二个密钥
     FLASH_DUKR = 0x56;                    // 写入第一个密钥
   }
   while((FLASH_IAPSR & 0x08) == 0);      // 若解锁未成功,则重新再来
   
   for(ucCount=0;ucCount<ucLength;ucCount++)
   {
     *(unsigned char *)pDst = *(unsigned char *)pSrc;
     while((FLASH_IAPSR & 0x04) == 0);        // 等待写操作成功
      pSrc ++;
     pDst ++;
    }
}

code 缩减1/3 ?——需要修改细节?从这里并看不出pDst是EEROM 区还是flash, Xdata RAM区。无所谓的,只要是指向004000H的指针就行。这是仿冯诺依曼结构,大家共64K/128K地址,读EEROM区和读 SRAM(Xdata)区一样指令?知道写入不一样就行了。


、、

然后讨论如何调用这个函数。


、、

使用特权

评论回复
71
aihe| | 2011-5-15 14:26 | 只看该作者
#error cpstm8 main.c:21 bad character ?
#error cpstm8 main.c:21 bad character ?
#error cpstm8 main.c:21(5) missing expression
#error cpstm8 main.c:21 bad character ?
#error cpstm8 main.c:21 bad character ?
#error cpstm8 main.c:21 bad character ?
#error cpstm8 main.c:21 bad character ?
#error cpstm8 main.c:21 bad character ?
#error cpstm8 main.c:21 bad character ?
main.c:
The command: "cxstm8 +mods0 +debug -pxp -no -pp -l -i"c:\program files\cosmic\cxstm8_32k\hstm8" -i"C:\Program Files\COSMIC\CXSTM8_32K\Hstm8"  -clDebug\ -coDebug\ main.c" has failed, the returned value is: 1
exit code=1.

test.elf - 11 error(s), 0 warning(s)
编译还是不过
COSMIC编译要求很严的

使用特权

评论回复
72
刘前辈| | 2011-5-15 17:49 | 只看该作者
本帖最后由 刘前辈 于 2011-5-15 18:21 编辑

很简单的调试:删去无关项,看看到底哪错:
void EE_Wrait(unsigned int pSrc, unsigned int pDst,unsigned char ucLength)   
      //写数据内置存储器
{        //从源地址写入到----目的地址----长度
    unsigned char ucCount;
        
   for(ucCount=0;ucCount<ucLength;ucCount++)
   {
     *(unsigned char *)pDst = *(unsigned char *)pSrc;
          pSrc ++;
         pDst ++;
}
}

上面是内存拷贝程序,与EEROM无关。若错就是上面写错了,很好查。

程序基本上是厂商提供的,如若错,肯定是我们自己哪不对。查吧,懒得查就放弃自己做。采用标准的。


bad character ,是出现了编译器不接受的字符,——是文字输入错误。很好检查。——不是程序语法错。

    编译器标出哪行错?告知。还可以学习、修改一下:

如若你原来写的函数可以用,不如见好就收,你好不容易花时间调试出来的;我随便改也不好。改的反而不能用了,不讨好的事。
        商家都提供了,看不出有什么问题。我不可能比商家写的更简单。

在我这怎么优化都是很简单的事,各种简约方法都很好玩。 要不我装一个COSMIC C 试试?意义不大。

下面是商家提供的程序:

unsigned int eeprom_address;     /* 定义eeprom_address 变量 */

//定义EEPROM字节写 函数
void EEPROM_WRITE_BYTE ( unsigned int eeaddress, unsigned char  eedata)
{
eeprom_address = eeaddress;

/* 设置编程时间,FIX =1:编程时间固定为标准编程时间tprog。*/
FLASH_CR1 &= (unsigned char)(~0x01);
FLASH_CR1 |= 0x01;

/* MASS密钥,解除EEPROM的保护 */
FLASH_DUKR = 0xAE;
FLASH_DUKR = 0x56;

*((unsigned char*) eeprom_address) = eedata;
//EOP=1,EEPROM编程结束
while((FLASH_IAPSR & 0x04) != 0x00);
}


看上去明显避开使用指针,很简约。


、、


、、

使用特权

评论回复
73
aihe| | 2011-5-15 18:09 | 只看该作者
*(unsigned char *)pDst = *(unsigned char *)pSrc
这个报错,呵呵
将地址转换成指针变量时必须强制转换
我觉得从整体考虑,优化函数的输入参数才是正事
算了也不要你改了,下次哪个网友有更好的方法告诉我一下

使用特权

评论回复
74
刘前辈| | 2011-5-15 18:46 | 只看该作者
本帖最后由 刘前辈 于 2011-5-15 18:53 编辑

有道理。确实不严格。pSrc/pDst 是常量地址,编译器严格点就通不过了。

好办,稍微改改就通过了。——先设一个uint 变量pSrcAdrr ,不是指针;就行了。看厂商标准程序也是这么干的。


优化函数参数为指针,确实方便;不过code 就上去了。所以厂商也没那么干。——到底是要好的算法,还是要最小的Code 。可能STM8  Flash 空间小,所以追求后一项。


、、

使用特权

评论回复
75
aihe| | 2011-5-15 19:02 | 只看该作者
别费那个劲了
就此打住吧

使用特权

评论回复
76
刘前辈| | 2011-5-15 19:05 | 只看该作者
void EE_Wrait(unsigned int pSrc, unsigned int pDst,unsigned char ucLength)      //写数据内置存储器
{        //从源地址写入到----目的地址----长度
    unsigned char ucCount;
    unsigned int pDstAddr = pDst;
    unsigned int pSrcAddr = pSrc;
   
// 对数据EEPROM进行解锁
   do
   {
     FLASH_DUKR = 0xae;                    // 写入第二个密钥
     FLASH_DUKR = 0x56;                    // 写入第一个密钥
   }
   while((FLASH_IAPSR & 0x08) == 0);      // 若解锁未成功,则重新再来
   
   for(ucCount=0;ucCount<ucLength;ucCount++)
   {
     *(unsigned  char *)pDstAddr = *(unsigned  char *)pSrcAddr;
     while((FLASH_IAPSR & 0x04) == 0);        // 等待写操作成功
     pSrcAddr ++;
     pDstAddr ++;
    }
}

Keil C 通过。可能 pDstAddr ++; 会因硬件特殊报错。


//

使用特权

评论回复
77
aihe| | 2011-5-15 19:26 | 只看该作者
编译通过,截图为证,到此为止



使用特权

评论回复
评分
参与人数 1威望 +1 收起 理由
123jj + 1
78
dongshan| | 2011-5-16 00:01 | 只看该作者
本帖最后由 dongshan 于 2011-5-16 00:17 编辑

我说以下几点,仅供讨论:
1. struct在c++中与class一样,是用来定义类的,它们之间的区别是: struct默认的成员属性是public的,class默认的是private.

2. A a1 = a2调用的是copy constructor. 而a1 = a2 调用的是assignment operator 函数(以下简称二个函数)。当你定义自己的类时,如果自己定义了这些函数,那我们没有争议,当然是调用这些函数。

3. 当我们没定义这二个函数时,编译器并不一定为我们提供这二个函数。提不提供要看你的类的定义具不具有 bitwise copy的语义。如果具有bitwise copy的语义,则编译器并不提供这二个函数,而是直接进行member-wise r 复制,而这种复制各种编译器的实现并不相同。如果不具有bitwise copy的语义,则会我们提供默认的这两二个函数。
4. 类具有以下四种特征时,则不具备bitwise 语义:
a. 当这个类有一个类成员,并且这个类成员显示的定义了这二个函数之一。
b. 当这个类继承的基类显示的定义了这二个函数之一。
c. 当这个类有虚函数的时候
d. 当这个类的继承链中存一个虚基类的时候。

以上四种情况存在时,则一个类的bitwise语义被破坏,这时编译器会提供上面提到的二个函数。除些之外,都是按照member-wise 来复制的,这个复制的实现各个编译器并不一定相同。

C++博大精深,学了十年,仍感觉是初学者。从来敢说自己会C++.也可能是用的少的原因

使用特权

评论回复
评分
参与人数 1威望 +1 收起 理由
highgear + 1
79
dongshan| | 2011-5-16 00:14 | 只看该作者
“从来敢说”应是“从未敢说”,

使用特权

评论回复
80
123jj| | 2011-5-16 12:17 | 只看该作者
编译通过,截图为证,到此为止

aihe 发表于 2011-5-15 19:26



aihe老师说的对,就此打住,再费那个劲,也不会有结果的,因为编译原理明摆着,优化是不可能超越基本硬件特征的。就算更改库函数,强制用汇编优化,也省不了几个字节,并且程序可移植性变差~~~

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则