搜索
bottom↓
回复: 24

iccavr 使用无符号长整型使用问题

[复制链接]

出0入0汤圆

发表于 2010-10-15 11:44:46 | 显示全部楼层 |阅读模式
请教马老师关于iccavr 编译器的中使用 无符号长整型问题
我在程序中定义一个无符号长整型,使用大小为0-99999999
将其拆分为 x1000000、x1000000、x100000、x10000、x1000、x100、x10、x1八个数字存数在eeprom
可是在使用过程中发现对系统进行重新上电之后(x10000、x1000、x100、x10、x1)>65536之后就会出现问题,现象为这几位自动变成了无符号整型计算,但更高几位(x1000000、x1000000、x100000)不受影响,我想请问这是什么原因,是iccavr不支持无符号长整型吗?

阿莫论坛20周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。

出0入0汤圆

 楼主| 发表于 2010-10-15 12:51:54 | 显示全部楼层
解决了  
刚请教了人  
是编译器将(x10000、x1000、x100、x10、x1)将这些数据优化成了整型的了
将1000 这些数据改成无符号整形的就可以了

出0入0汤圆

发表于 2010-10-15 13:20:57 | 显示全部楼层
先“教训”几句:要做好这个行当,掌握一点真正的本事,那么不管是学习、工作,都要踏踏实实,认认真真的,一步一步从基础做好,没有什么“捷径”的。

看看你最基本的东西和概念都不清楚,还怀疑这,怀疑那的,真的应该从自己本身找原因。

以上不是针对LZ一人的,凡是看到此贴的朋友,对照下自己的学习和态度,尤其是我的学生!

1。首先必须、必须、必须!!!(三次,重要!)学会和养成看HELP!!!
以下是从ICCAVR的HELP中拿到的,恐怕不会看不懂吧

Data Type Sizes

TYPE             SIZE (bytes)             RANGE
unsigned char        1            0..255
signed char        1            -128..127
char (*)                 1        0..255
unsigned short        2        0..65535
(signed) short        2        -32768..32767
unsigned int        2        0..65535
(signed) int        2        -32768..32767
pointer        2        N/A
unsigned long        4        0..4294967295
(signed) long        4        -2147483648..2147483647
float                 4        +/-1.175e-38..3.40e+38
double                 4        +/-1.175e-38..3.40e+38
(*) "char" is equivalent to "unsigned char"
floats and doubles are in IEEE standard 32-bit format with 8 bit exponent, 23-bit mantissa and 1 sign bit.

2。这里可以看到,ICCAVR支持的数据类型。里面有无符号长整型(unsigned long),占用4字节,表示的范围绝对超过了你需要的(0-99999999)。

3。不明白你为何要拆成8个字节。

4。你拆的过程,以及再组装的过程都对吗?恐怕这个你也不能确定!

============================================================

下面交给你两招,掌握后请回顾你的C语言到底学的如何。


如果你的想法仅仅是要在EEPROM中保存这个无符号长整型变量的值,作为备份,下次启动再使用的话。

1。请选择使用CVAVR平台。在这个平台中,可以直接定义一个eeprom的无符号长整型变量,省下的简单多了。你也不用考虑是几位的。

    unsigned long a1;
    EEPROM unsigned long e_a1;
    ......

    e_a1 = a1;           //eeprom 变量的值就是a1,保存在EEPROM中了
    a1 = e_a1;           //回读EEPROM的值

2。定义一个联合体

   union data{
     unsigned long a1;
     unsigned char b1[4];
   } u_a;

   u_a.a1 = 12345;      //a1赋值,为4个字节,由于与B1[]占用相同的单元,所以B1数组个元素就是A1的四个字节分别的值,注意这里你可以不管是高位在前,还是地位在前的问题。

   保存的EEPROM中主要简单的按顺序写到四个EEPROM字节中。

   e_a0 = b1[0];e_a1 = b1[1];e_a2 = b1[2];e_a3 = b1[3];

   恢复回读数据只要:b1[0] = e_a0;b1[1] = e_a1;b1[2] = e_a2;b1[3] = e_a3;就可以了。根本不需要做任何的转换计算。

   第2个方法在任何C平台都能用,只是读/写EEPROM的方法不同。

   CVAVR有许多特点是其它C平台没有的,操作EEPROM方便就是其中之一。如果你是新手、或者你的C语言能力一般般(比如根本不知道,也看不懂第2个办法是如何处理的,有什么优点),建议学习使用CVAVR。

出0入0汤圆

发表于 2010-10-15 13:27:16 | 显示全部楼层
回复【1楼】z421868436
解决了   
刚请教了高人   
是编译器将(x10000、x1000、x100、x10、x1)将这些数据优化成了整型的了  
将1000 这些数据改成无符号整形的就可以了
-----------------------------------------------------------------------

这个是“高人”?无效的工作。

一个4字节的数据,要拆成8个字节(不止8个,是N个)保存,还要拆,组装。

出0入0汤圆

 楼主| 发表于 2010-10-15 14:12:08 | 显示全部楼层
回复【3楼】machao
-----------------------------------------------------------------------
多谢马老师指点!
不过有些东西你认为无效的 别人就不一定了
我这样拆 在组装肯定是我有特殊的用处的,而且也不仅仅是为了存放在eeprom里面的问题

对于马老师在二楼的回答我很感谢,说实话本人也是刚开始用avr半年,玩单片机和C语言也不到一年的时间,确实是很多地方做的不够好,有些习惯也确实没养好,这个正在慢慢的改变。
关于你在2喽中几点问题:
1、help确实我没有看
3。不明白你为何要拆成8个字节。
   这里我拿着这些数据有特殊的作用
4。你拆的过程,以及再组装的过程都对吗?恐怕这个你也不能确定!
   在我的程序中我的思路是正确的,这个过程也是正确的,这点可以肯定,之前我用过的unsigned int型的时候用过,只是现在因为对数字的要求变大了,所以换成了无符号长整型才出现现在的错误
但是像你在3楼所说的未免也太过于片面了。
另外上面我上午碰到的问题也只是因为我自己处理不短时间之后才提问的,我是经验不足,
cdata =1000000*ReadData[0]+1000000*ReadData[1]+1000000*ReadData[2]+100000*ReadData[3]+10000*ReadData[4]+100*ReadData[5]+100*ReadData[6]+ReadData[7];这样的一个表达式(cdata定义为unsigned long型),得出的结果由于编译器的优化原因不能得到正确结果,你能保证你能一眼看出问题的所在?

出0入0汤圆

发表于 2010-10-15 15:18:14 | 显示全部楼层
回复【4楼】z421868436
回复【3楼】machao  
-----------------------------------------------------------------------
你认为无效的 别人就不一定了
我这样拆 在组装肯定是我有特殊的用处的,而且也不仅仅是为了存放在eeprom里面的问题
对于马老师在二楼的回答我很感谢,说实话本人也是刚开始用avr半年,玩单片机和c语言也不到一年的时间,确实是很多地方做的不够好,有些习惯也确实没养好,这个正在慢慢的改变。
但是像你在3楼所说的未免也太过于片面了。
另外上面我上午碰到的问题也只是因为我自己处理不短时间之后才提问的,我是经验不足,
cdata =1000000*readdata[0]+1000000*readdata[1]+1000000*readdata[2]+100000*readdata[3]+10000*readdata[......
-----------------------------------------------------------------------

这个不用多讲,明白人自然自己心里有数的。等你有了一定经验和实践后,回过来会体会到更多的东西。我的建议不是表面的。

对于你LZ位提的问题,我已经猜到问题所在,2楼的第4点就指出了:“你拆的过程,以及再组装的过程都对吗?”。因为没有代码,当然不能具体指出的。后面的2点不是指计算问题本身,而是给你点更高层、更全面的建议。


既然你贴出了表达式:
cdata =1000000*ReadData[0]+1000000*ReadData[1]+1000000*ReadData[2]+100000*ReadData[3]+10000*ReadData[4]+100*ReadData[5]+100*ReadData[6]+ReadData[7];

你的结论是(或者是高人的提醒):“结果由于编译器的优化原因不能得到unsigned long型”(问题肯定有,但我并不赞同这个说法)。

那么我要问你真的明白了?明白了什么?既然cdata定义为unsigned long了,那么编译器恐怕不能随便优化吧,你能具体说明在那里优化了,如何优化的?

你问我能保证是否能一眼看出问题的所在?,回答:肯定能。因为我的学生犯这样错误的太多了。我见的多了。我知道问题到底是怎样产生的,但不是什么编译器优化产生的问题。同时我会告诉学生,应该如下这样的写代码,既简洁、高效,还不容易出错。

cdata = 0;
for (i=0;i<=7;i++){cdata = cdata*10 + ReadData;}

出0入0汤圆

发表于 2010-10-15 16:10:37 | 显示全部楼层
回复【5楼】machao
-----------------------------------------------------------------------

呵呵,马老师,
这段代码不对吧?
cdata = 0;
for (i=0;i<=7;i++){cdata = cdata*10 + ReadData;}

出0入0汤圆

发表于 2010-10-15 16:15:47 | 显示全部楼层
回复【6楼】mingyuexin1981
回复【5楼】machao  
-----------------------------------------------------------------------
呵呵,马老师,
这段代码不对吧?
cdata = 0;  
for (i=0;i&lt;=7;i++){cdata = cdata*10 + readdata;}
-----------------------------------------------------------------------

请指出

出0入0汤圆

 楼主| 发表于 2010-10-15 16:23:42 | 显示全部楼层
回复【5楼】machao
回复【4楼】z421868436  
回复【3楼】machao   
-----------------------------------------------------------------------  
你认为无效的 别人就不一定了  
我这样拆 在组装肯定是我有特殊的用处的,而且也不仅仅是为了存放在eeprom里面的问题  
对于马老师在二楼的回答我很感谢,说实话本人也是刚开始用avr半年,玩单片机和c语言也不到一年的时间,确实是很多地方做的不够好,有些习惯也确实没养好,这个正在慢慢的改变。  
但是像你在3楼所说的未免也太过于片面了。  
另外上面我上午碰到的问题也只是因为我自己处理不短时间之后才提问的,我是经验不足,  
cdata =1000000*readdata[0]+1000000*readdata[1]+1000000*readdata[2]+100000*r......
-----------------------------------------------------------------------

这点我在实际使用中的验证的时候在超过65536的时候得到的数据就会出现问题
之后将后面的1000000 ---10 全部该位变量  并且定义为unsigned long 型之后使用  得到的结果即是正确的,程序在另外部分没有做任何更改,请问您对此的解释,
另外一个
cdata = 0;   
for (i=0;i<=7;i++){cdata = cdata*10 + readdata;}  
这点学习了!谢谢

出0入0汤圆

发表于 2010-10-15 16:23:44 | 显示全部楼层
回复【7楼】machao
-----------------------------------------------------------------------

不好意思,马老师,刚刚看错了,这样是没有问题的

出0入0汤圆

发表于 2010-10-15 16:28:25 | 显示全部楼层
1000000
这样写不行的,
加上UL就可以了
1000000UL

超过整型范围的常数都要加

出0入0汤圆

 楼主| 发表于 2010-10-15 16:32:36 | 显示全部楼层
回复【10楼】snoopyzz
1000000
这样写不行的,
加上ul就可以了
1000000ul
超过整型范围的常数都要加
-----------------------------------------------------------------------

这点我还真不知道,能够详细说说?

出0入0汤圆

发表于 2010-10-15 17:36:58 | 显示全部楼层
回复【8楼】z421868436
-----------------------------------------------------------------------   
这点我在实际使用中的验证的时候在超过65536的时候得到的数据就会出现问题
之后将后面的1000000 ---10 全部该位变量  并且定义为unsigned long 型之后使用  得到的结果即是正确的,程序在另外部分没有做任何更改,请问您对此的解释,
另外一个  
cdata = 0;   
for (i=0;i<=7;i++){cdata = cdata*10 + readdata;}   
这点学习了!谢谢
-----------------------------------------------------------------------

cdata = 0;   
for (i=0;i<=7;i++){cdata = cdata*10 + readdata;}   
不是我发明的,是教课书中的标准写法。

1。产生代码短(只需要调用一个*10 的函数和一个加法),而你的那个写法,要产生7个*1000000、*100000....乘法。浪费了大量时间和代码。效率极低。一看就是菜鸟写的代码。

2。不容易出错。你的ReadData[]一般定义是char型的。
在 cdata = cdata*10 + readdata这个表达式中,我们看看类型是如何转换的:   
     cdata              =              cdata * 10         +    readdata
     unsigned long      =      unsigned long * signed int +    char
     unsigned long      =            unsigned long        +    char
     unsigned long      =                         unsigned long


在你的式子里,使用了常数 100000,10000,1000。关键是你没有清楚常数是什么类型的。
1。通常,常数被定义成有符号的整数型
2。在大型计算机系统中,由于资源丰富,常数通常被定义成统一的比较长的长度,如4个字节,那么就是 signed long
   而对于小型或嵌入式,由于资源有限,常数通常自动定义成2种:signed long 和signed int。如果你写的数在-32728至32768之间就是signed int,超出这个范围,认定为signed long。

明白了这个,下面再看你的式子:

cdata =1000000*ReadData[0]+1000000*ReadData[1]+1000000*ReadData[2]+100000*ReadData[3]+10000*ReadData[4]+100*ReadData[5]+100*ReadData[6]+ReadData[7];   

    首先很多人认为cdata是unsigned long的,所以后面所有的变量会“先变成”unsigned long,然后再计算。这是错误的!编译系统是一级、一级转换的。

    你的式子里有3级转换,第一级是7个乘法各自单独转换:

   结果: signed long + signed long + signed long + signed long  +  signed int + signed int + signed int + char
   第2级,全部转换成 signed long 再相加(实际上还不是如此简单的)
   第3级,将signed long的结果转换成unsigned long赋给变量。

   由于你的ReadData[],都是1位数,最大不超过9,所以问题出在了10000*ReadData[4];10000是signed int ,所以该项结果大于32767就变成负数,影响整个结果。

   所以我对你说大于65536才出错抱怀疑态度,根据我的分析65535也不对的?不知道分析的是否对,你用40000转换一下,看是否正确。


    还好你的ReadData[],都是1位数,最大不超过9,否则你就更加摸不到头脑了。

    你在每一项前都加上强制转换,当然就没有问题了。这种隐含的转换,每个编译系统可能还有不同的地方。为了避免这种隐含的错误,我给了你最简单和不容易出错的写法:

cdata = 0;   
for (i=0;i<=7;i++){cdata = cdata*10 + readdata;}

会这种写法不一定就是“高手”,所以我不是高手。但对于说是编译器把1000优化成整型我不认同,因为它本身就是整型。否则对于常数-1000,编译器如何优化?整形,还是带符号整形?

出0入0汤圆

发表于 2010-10-15 17:49:33 | 显示全部楼层
回复【10楼】snoopyzz
1000000
这样写不行的,
加上ul就可以了
1000000ul
超过整型范围的常数都要加
-----------------------------------------------------------------------

这个不是关键。对于0-99999999,1000000也可以

signed long  4字节    范围-2147483648..2147483647
=======================================================
标准常数定义:

xxxxU        无符号
xxxxL        long
xxxxUL       无符号 LONG

XXXX    麻烦了,但一定是有符号的

根据编译器,一般小系统

xxxx  在-32768..32767内     signed int
xxxx  超出上范围            signed long

出0入0汤圆

发表于 2010-10-15 17:55:03 | 显示全部楼层
编译器有类型提升规则,而常量默认都是整形,8位机整型一般默认是16位的,

所以加了UL后缀,就不会默认编译出整形乘法,而是长整型乘法了。

出0入0汤圆

发表于 2010-10-15 18:04:39 | 显示全部楼层
回复【14楼】snoopyzz
编译器有类型提升规则,而常量默认都是整形,8位机整型一般默认是16位的,
所以加了ul后缀,就不会默认编译出整形乘法,而是长整型乘法了。
-----------------------------------------------------------------------

错误!
1。常量默认都是整形  ==》带符号整型
2。8位机整型一般默认是16位的。如果我写a * (-100000),那么这个-100000默认为16位还是32位?

出0入0汤圆

发表于 2010-10-15 18:14:55 | 显示全部楼层
各位“高手”,请翻翻C语言的教课书。

我手上的谭浩强老先生的《C语言程序设计》1994年6月第9次印刷本的

P14 倒数第7行看起,关于常量类型问题
P21 倒数第6行的例子,关于类型自动转换问题。

都是第二章最基础的DD。怎么学的,还硬犟。

出0入0汤圆

发表于 2010-10-18 10:54:54 | 显示全部楼层
谢谢马老师指教,
LZ的错误关键在于数据溢出,*10000这个步骤上,unsigned char * signed int ,结果类型仍然为 signed int
其值范围是-32768~32767,所以杯具了,
如果每一位都只是0~9的话,LZ只需要把*10000改成*10000UL就可以了,
我是习惯性都加上UL,可以避免很多不小心溢出的问题,

所以,我在10L的发言的确不对,并不是*1000000这一步的问题,当常数超出整型范围时,编译器会当作是长整形的。
但都加上UL肯定不会错:)

出0入0汤圆

发表于 2010-10-18 15:06:39 | 显示全部楼层
问题在于写出这样代码:
cdata =1000000*ReadData[0]+1000000*ReadData[1]+1000000*ReadData[2]+100000*ReadData[3]+10000*ReadData[4]+100*ReadData[5]+100*ReadData[6]+ReadData[7];

本身就是杯具。加上基本的概念还糊度,杯具+杯具*10。听“高人”指点,认为是编译器优化,把责任推到编译器身上,更是杯具+杯具*10+杯具*100  :)   

要这样写 cdata = 0; for (i=0;i<=7;i++){cdata = cdata*10 + readdata;} 也就避免了转换出错的情况,效率还高。

出0入0汤圆

发表于 2010-11-5 00:06:41 | 显示全部楼层
mark mark,我的这个问题困扰我很久了,我就奇怪明明是long int,为啥就是不能大于32676呢,百度也搜不到,问群里的人,没人搭理我,都说让我自己补C。。。刚来准备发帖问马老师就看到这个了,说实话有个老师带就是不一样啊,我犯了个LZ同样的错误还死活不解,马老师教训批评的对,我接受您的批评和教育!准备重新重头多编的细看谭浩强老先生的《C语言程序设计》,手里有这么本好书不仔细看到处找答案。。。我简直太杯具了。。。

看下我的代码犯的错误:
signed long high_now=0;
unsigned char high[8]={"-1234.5"};       

switch(high_num){
    case 0:
        high_now = 0;
        break;
    case 3:
        high_now = (high[0]-0x30)*10 + (high[2]-0x30);  //x.x
        break;
    case 4:
        high_now = (high[0]-0x30)*100 + (high[1]-0x30)*10 + (high[3]-0x30);  //xx.x
        break;
    case 5:
         high_now = (high[0]-0x30)*1000 + (high[1]-0x30)*100 + (high[2]-0x30)*10 + (high[4]-0x30);  //xxx.x
        break;
    case 6:
        high_now = (high[0]-0x30)*10000 + (high[1]-0x30)*1000 + (high[2]-0x30)*100 + (high[3]-0x30)*10 + (high[5]-0x30);  //xxxx.x
        break;


顺便请教下马老师,cdata = 0; for (i=0;i<=7;i++){cdata = cdata*10 + readdata;} 这样的写法对于high[8]={"-1234.5"}有什么好办法吗?

出0入0汤圆

发表于 2010-11-5 13:49:14 | 显示全部楼层
回复【17楼】snoopyzz
谢谢马老师指教,
lz的错误关键在于数据溢出,*10000这个步骤上,unsigned char * signed int ,结果类型仍然为 signed int
其值范围是-32768~32767,所以杯具了,
如果每一位都只是0~9的话,lz只需要把*10000改成*10000ul就可以了,
我是习惯性都加上ul,可以避免很多不小心溢出的问题,
所以,我在10l的发言的确不对,并不是*1000000这一步的问题,当常数超出整型范围时,编译器会当作是长整形的。
但都加上ul肯定不会错:)

-----------------------------------------------------------------------

为什么这样可以:
speed_now = (speed[0]-0x30)*10000L + (speed[1]-0x30)*1000 + (speed[2]-0x30)*100 + (speed[4]-0x30)*10 + (speed[5]-0x30)
                                 ↑
speed_now = (long)speed[0]-0x30)*10000 + (speed[1]-0x30)*1000 + (speed[2]-0x30)*100 + (speed[4]-0x30)*10 + (speed[5]-0x30)
              ↑
speed_now = (speed[0]-0x30)*(long)10000 + (speed[1]-0x30)*1000 + (speed[2]-0x30)*100 + (speed[4]-0x30)*10 + (speed[5]-0x30)
                              ↑

这样就不行了:
speed_now = (speed[0]-0x30)*10000) + (speed[1]-0x30)*1000 + (speed[2]-0x30)*100 + (speed[4]-0x30)*10L + (speed[5]-0x30)
                                                                                                    ↑
speed_now = (long)((speed[0]-0x30)*10000) + (speed[1]-0x30)*1000 + (speed[2]-0x30)*100 + (speed[4]-0x30)*10 + (speed[5]-0x30))
              ↑
speed_now = (long((speed[0]-0x30)*10000)) + (speed[1]-0x30)*1000 + (speed[2]-0x30)*100 + (speed[4]-0x30)*10 + (speed[5]-0x30)
              ↑


突然想通了,哈哈,因为在(speed[0]-0x30)*10000)大于32767时就已经溢出了,都溢出在转型,肯定不行了!~

出0入0汤圆

发表于 2011-12-19 21:38:03 | 显示全部楼层
回复【2楼】machao  
-----------------------------------------------------------------------

马老师,请教一下如何处理64位数除法运算。   phase=2^64 / rx  ,请问在ICCAVR平台中有没有那个头文件可以处理这个运算,还是必须自己定义数组来实现。

出0入0汤圆

发表于 2012-11-20 00:00:33 | 显示全部楼层
snoopyzz 发表于 2010-10-18 10:54
谢谢马老师指教,
LZ的错误关键在于数据溢出,*10000这个步骤上,unsigned char * signed int ,结果类型仍 ...

   我也是二样解决!O(∩_∩)O

出0入0汤圆

发表于 2012-11-20 00:33:51 来自手机 | 显示全部楼层
学习了!谢马老师14楼的精彩谅解

出0入0汤圆

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

本版积分规则

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

GMT+8, 2024-3-29 17:29

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

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