从坑里刚爬出来, 大家来围观一下, 涨涨姿势.
从坑里刚爬出来, 发个帖子.先说起因.
因为要用到unix时间戳, 可恶的是AVR-GCC自带的库里面居然不包括time.h, 还好我要尔不高, 我只用到mktime()和localtime()两个函数而已, 而自己造轮子的话还是略有难度, 于是下载了glibc的源代码, 打算把这两个函数扒出来, 结果一看代码傻眼了, 比我想像的复杂, 那好吧, 先Google再说.所幸搜索之一通后找到一个从linux内核里扒出来的mktime()版本和一个网友自己写的localtime().
下面这个就是从linux内核里扒出来的mktime():
static time_t _mktime(int year,
int mon,
int day,
int hour,
int min,
int sec)
{
if (0 >= (int)(mon -= 2))
{ /* 1..12 -> 11,12,1..10 */
mon += 12; /* Puts Feb last since it has leap day */
year -= 1;
}
return ((((time_t)(year/4 - year/100 + year/400 + 367*mon/12 + day) + year*365L - 719499L
)*24 + hour /* now have hours */
)*60 + min /* now have minutes */
)*60 + sec; /* finally seconds */
}
然后在电脑使用GCC编译, 测试, 没问题之后用AVR-GCC编译, 下载到板子上一试, 居然有问题.
先说一下时间相关的代码逻辑:
先从RTC(PCF8563)中获取日期信息, 填入struct tm结构体, 然后用mktime()转换成unix时间戳. RTC获取时间的接口函数返回的即是这个时间戳.
在外部使用细分时间的时候再将获取到的时间戳使用localtime()转换回来, 得到日期等信息.
现在可以描述一下问题现像了:
当初始化的时间(绝对不会错)读回来后用localtime()转出来一看, 驴头不对马嘴.
更惨的是手头没调试器, 只能从串口打印调试信息.
于是打印读到的unix时间戳一看, 错的. 很自然的怀疑是调用mktime()的时候结构体信息填错了, 打印了一堆信息后证明没问题.
然后怀疑缓冲溢出, 野指针等, 结果证明均无问题. 在源文件里差写了1/5的调试代码后, 终于怀疑到mktime()头上来了.
无论如何, 我还是有点不相信, 一来这是在linux内核里扒出来的, 二来我已经在电脑上用GCC编译测试过了, 确实没问题.再说这段代码这么短, 按过程手算一遍结果都是对的啊.
最先怀疑的是那个return表达式太长, 编译器在处理表达式时出了问题. 虽然连我自己都不相信, 但我依然还是将它拆分成几部分. 结果证明, 与表达式无关, 但真相就快浮出水面了.
先看一段代码.
int main(void)
{
uint32_t i;
int j;
j = 1999;
i = j * 365 - 719499;
return 0;
}
你能看出来这段代码那里有问题吗? 很明显没什么问题, 在电脑上计算结果也是对的, 无任何警告, 当然在AVR-GCC里也没任何警告, 如图, 使用-Wall选项警告全开哦.
诡异的是算出来的结果是错的. 或许我说过这句话后你已经知道是怎么回事了, 但是mktime()这个函数里看起来可不是那么明显.
然后打印了好几次调试信息后, 我把代码改成这样:
int main(void)
{
uint32_t i;
int j;
j = 1999;
i = 1999 * 365 - 719499;
return 0;
}
这次TM终于报了个警告: warning: integer overflow in expression !!
草, 瞬间明白了, 栽到整形提升上了.
如果还有没明白的童鞋, 我来解释一下:
参数j是int型的, 365 和 719499 都足够用int表示, 所以右值的类型是int型. 很不幸的是当乘法计算完毕后结果就溢出了! 为什么会溢出呢? 因为在AVR-GCC中, int类型占用的空间为两个字节! 即sizeof(int) = 2. 在PC上不会溢出的原因是因为在PC平台上int类型占用4个字节!
知道原因后解决就很简单了, 因为是在计算乘法时溢出的, 所以将365改为365L即可, 或者将j强制转换为uint32类型亦可.
(全文完)
后记: 折腾了大半天, 累死我了. 昨天睡觉扭了脖子, 疼的很, 先睡了.
恩恩 恩,涨了点知识,以后会注意 我一般这样处理:
如果是简单的计算或循环计数,用int
如果涉及到较大的数值就用uint32_t 或 uint16_t,具体用哪个需要估算中间值的范围
记得刚学C语言的时候书上说int是16位的,实际试验的时候发现是32位的,所以对这个映象很深刻 还是那句话: 现象越奇怪,错误越低级 好!楼主辛苦了! 涨知识了,记住了 这是基本常识,折腾只能怪自己基础不扎实 radar_12345 发表于 2014-10-27 07:38
还是那句话: 现象越奇怪,错误越低级
这个才是经验总结啊 wshtyr 发表于 2014-10-27 00:24
我一般这样处理:
如果是简单的计算或循环计数,用int
不同平台之间程序的移植尤其要注意,很多PC平台上的代码用的是标准类型,而糟糕的是不同平台对标准类型的长度定义不一样,溢出了都不清楚在哪里,更别说警告了。 楼主用的什么编辑器,配色看起来挺舒服的 是蛮舒服的 做嵌入式多了,逐步就抛弃了使用int
我的程序,只使用U8 U16 U32 U64和S8 S16 S32 S64这种二次定义的类型
一是移植的时候方便,二是数据sizeof是确定的
当然,从执行速度和产生代码大小上看,int因为和CPU的字长一致,往往是最好的 amigenius 发表于 2014-10-27 08:07
这是基本常识,折腾只能怪自己基础不扎实
你肯定没仔细看贴。这个问题估计大部分人不会一眼看出来。 涨知识,Mark! DevLabs 发表于 2014-10-27 08:27
你肯定没仔细看贴。这个问题估计大部分人不会一眼看出来。
我肯定没仔细看,因为这种问题,瞧一眼就知道是什么问题了。对于第二句话,您要么是把大部分人看扁了,要么是您指只有中国人才算是人。 int类型在不同的平台上长度是不一样的
所以我一般不用int类型。
16位我用short,32位用long 一起来长长姿势~ 风轻云淡 发表于 2014-10-27 08:24
楼主用的什么编辑器,配色看起来挺舒服的
gVim,配色是solarized。 radar_12345 发表于 2014-10-27 07:38
还是那句话: 现象越奇怪,错误越低级
非常有道理 仿真器你没用上? Vmao 发表于 2014-10-27 09:15
仿真器你没用上?
手边没有仿真器.
后来装了AVR studio软件仿真, 但AVR Studio软件仿真跑的巨慢, 放弃. radar_12345 发表于 2014-10-27 07:38
还是那句话: 现象越奇怪,错误越低级
总结的很正确。 涨知识 DevLabs 发表于 2014-10-27 10:32
手边没有仿真器.
后来装了AVR studio软件仿真, 但AVR Studio软件仿真跑的巨慢, 放弃. ...
gVim握爪{:lol:}
刚学的时候想砸键盘,现在越来越好用了 现象越奇怪,错误越低级 {:handshake:} 现象越奇怪,错误越低级。超赞同! 学习一下 wshtyr 发表于 2014-10-27 20:43
gVim握爪
刚学的时候想砸键盘,现在越来越好用了
现在使用别的编辑器也不自觉的去按ESC{:lol:}
有意思,以前也碰到过 yu_studio 发表于 2014-10-27 08:51
int类型在不同的平台上长度是不一样的
所以我一般不用int类型。
16位我用short,32位用long ...
万一某个编译器上long是64位你怎么办,还是免不了掉坑里
C语言跨平台上,其实他们自己也做过很多努力的,只是使用者以各种原因忽略。C语言可移植数据类型:#include <stdint.h> 涨知识了,记住了 takashiki 发表于 2014-10-28 10:04
万一某个编译器上long是64位你怎么办,还是免不了掉坑里
C语言跨平台上,其实他们自己也做过很多努力的, ...
现在我一般使用 <stdint.h> 和 <stdbool.h>.
然后字符串数组为了使用库函数时编译器不产生警告, 还是使用 char. radar_12345 发表于 2014-10-27 07:38
还是那句话: 现象越奇怪,错误越低级
支持这观点,真神! 习惯用uint32、uint16了,一般for里的i可能会用下int 有时候一些越低级的错误越难发现 涨知识了,学习学习 不错,涨姿势了。。。 wshtyr 发表于 2014-10-27 00:24
我一般这样处理:
如果是简单的计算或循环计数,用int
int不固定是16位或32位,选16位芯片开发,在开发环境中认为16位,32位芯片,开发环境就认为是32位。好像short型是固定16位的。 {:smile:}{:smile:}{:smile:} C99都有stdint.h,用这个类型定义变量吧 为了避免这类问题,我一般使用u8,u16,u32 现象越奇怪,错误越低级 对于这类问题我只能说呵呵,长见识了{:smile:}
页:
[1]