|
从坑里刚爬出来, 发个帖子.
先说起因.
因为要用到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类型亦可.
(全文完)
后记: 折腾了大半天, 累死我了. 昨天睡觉扭了脖子, 疼的很, 先睡了.
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?注册
x
阿莫论坛20周年了!感谢大家的支持与爱护!!
曾经有一段真挚的爱情摆在我的面前,我没有珍惜,现在想起来,还好我没有珍惜……
|