keil大常量计算问题

发布者:DreamyEclipse最新更新时间:2015-10-19 来源: eefocus关键字:keil  大常量计算 手机看文章 扫描二维码
随时随地手机看文章
Keil C51是与ANSI C兼容的编译器,ANSI C规范规定十进制整数常量的默认数据类型是int、long int和unsigned long int的其中一种,对给定的常量是其中的哪一种要看这个常量的实际大小,如果常数在-32768~32767之间则按int类型处理,如果按int类型处理会溢出就考虑long int或更大的数据类型unsigned long int。总之,编译器总是按尽可能的原则指定常量的类型。

但这一原则并不总能奏效,当两个常量做运算时就可能导致溢出。如:
#define SYSCLK                        22118400    // SYSCLK in Hz (22.1184 MHz external crystal oscillator)
#define SLIDER_REST_TIME    100              // in ms,slider rest time
#define REST_DELAY               SYSCLK * SLIDER_REST_TIME / (65536 * 1000)
unsigned char i;
i = REST_DELAY;
在keil c51中运行i为0xE1,即225,并不是期望的结果22118400 * 100 / (65536 * 1000) = 33.75,取整为33。原因分析如下:
宏替换后为:i = 22118400 * 100 / (65536 * 1000);,编译器首先为22118400定义类型,因为22118400不在int的表示范围内,而在long int的范围-2147483648~2147483647内,所以22118400按long int类型处理,在做乘积运算时100被自动按long int处理,22118400 * 100将按两带符号长整型常量进行运算,运算结果仍为带符号长整型,结果写成十六进制是0x83D60000,其十进制是-2083127296,显然出现了溢出错误。keil编译器并没有给出任何错误或警告提示信息(VC++6.0还给出警告warning C4307: '*' : integral constant overflow),继续进行下一个运算65536 * 1000,结果为带符号长整型,十六进制为0x3E80000,十进制为65536000,最后按两长整型除法计算-2083127296 / 65536000,结果为0xFFFFFFE1,由于i为字符类型,取0xFFFFFFE1的最低有效字节为0xE1赋值给i,i的最终值为0xE1。
解决这种溢出错误的方法用C语言的一个术语就是“提升”(promotion),拿上例来说就是将22118400指定为无符号长整型,即:
#define SYSCLK                        22118400UL
注:虽然只要将22118400、100、65536和1000四个常数中的一个指定为无符号长整型即可得到正确的结果,但考虑到可读性及规范性,应选择大整数指定其类型。
小结:按C51编译器的默认类型整数常量运算可能出现溢出错误,对大整数应指定其数据类型以避免出现可能的运算错误。

转自:幽幽灵猫

/////////////
 近日做射频系统,一个简单的MCU系统,程序全部用C语言设计,开发环境为盗版的Keil,用到定时器定义一个60秒的时间时,如此定义:
uint16  time;   //16位变量

time 60*1000/TMRCYC     //TMRCYC=5,定时器中断间隔为5ms
                          //time总时间60s
实际运行时,60s定时总是感觉不到,也就是说60s根本就没定义成功。
理论上讲,time =12000,比16位最大值65535小,应该没问题,但实际就是不行,以前没怎么用过Keil,所以也没注意这个问题,是否其他编译器也有这个问题不得而知,最后,定义改为:
time 60*1000L/TMRCYC   //常量1000后加L
问题解决。
但仍未完全明白其中道理,难道是编译器的问题,在此抛砖引玉,鄙人虽说也有几年程序经验,但哎,偏偏这么一个看似基础性的东西却不懂, 希望有大虾们解释下,可能对你们来说问题很简单,但不懂或没有遇见的小菜们却是有千千万万,你们就权当做好事了。 
///
上一篇     下一篇共25篇  

编程中无穷大常量的设定技巧2012年11月22日 13:08:05

转自 http://aikilis.tk/

如果问题中各数据的范围明确,那么无穷大的设定不是问题,在不明确的情况下,很多程序员都取0x7fffffff作为无穷大,因为这是32-bit int的最大值。如果这个无穷大只用于一般的比较(比如求最小值时min变量的初值),那么0x7fffffff确实是一个完美的选择,但是在更多的情况下,0x7fffffff并不是一个好的选择。

  1. 很多时候我们并不只是单纯拿无穷大来作比较,而是会运算后再做比较,例如在大部分最短路径算法中都会使用的松弛操作:
    if (d[u]+w[u][v] 我们知道如果u,v之间没有边,那么w[u][v]=INF,如果我们的INF取0x7fffffff,那么d[u]+w[u][v]会溢出而变成负数,我们的松弛操作便出错了,更一般的说,0x7fffffff不能满足“无穷大加一个有穷的数依然是无穷大”,它变成了一个很小的负数。
  2. 除了要满足加上一个常数依然是无穷大之外,我们的常量还应该满足“无穷大加无穷大依然是无穷大”,至少两个无穷大相加不应该出现灾难性的错误,这一点上0x7fffffff依然不能满足我们。

所以我们需要一个更好的家伙来顶替0x7fffffff,最严谨的办法当然是对无穷大进行特别处理而不是找一个很大很大的常量来代替它(或者说模拟它),但是这样会让我们的编程过程变得很麻烦。在我读过的代码中,最精巧的无穷大常量取值是0x3f3f3f3f,我不知道是谁最先开始使用这个精妙的常量来做无穷大,不过我的确是从一位不认识的ACMer(ID:Staginner)的博客上学到的,他/她的很多代码中都使用了这个常量,于是我自己也尝试了一下,发现非常好用,而当我对这个常量做更深入的分析时,就发现它真的是非常精巧了。

  1. 0x3f3f3f3f的十进制是1061109567,也就是10^9级别的(和0x7fffffff一个数量级),而一般场合下的数据都是小于10^9的,所以它可以作为无穷大使用而不致出现数据大于无穷大的情形。
  2. 另一方面,由于一般的数据都不会大于10^9,所以当我们把无穷大加上一个数据时,它并不会溢出(这就满足了“无穷大加一个有穷的数依然是无穷大”),事实上0x3f3f3f3f+0x3f3f3f3f=2122219134,这非常大但却没有超过32-bit int的表示范围,所以0x3f3f3f3f还满足了我们“无穷大加无穷大还是无穷大”的需求。
  3. 最后,0x3f3f3f3f还能给我们带来一个意想不到的额外好处:如果我们想要将某个数组清零,我们通常会使用memset(a,0,sizeof(a))这样的代码来实现(方便而高效),但是当我们想将某个数组全部赋值为无穷大时(例如解决图论问题时邻接矩阵的初始化),就不能使用memset函数而得自己写循环了(写这些不重要的代码真的很痛苦),我们知道这是因为memset是按字节操作的,它能够对数组清零是因为0的每个字节都是0,现在好了,如果我们将无穷大设为0x3f3f3f3f,那么奇迹就发生了,0x3f3f3f3f的每个字节都是0x3f!所以要把一段内存全部置为无穷大,我们只需要memset(a,0x3f,sizeof(a))。

所以在通常的场合下,0x3f3f3f3f真的是一个非常棒的选择。

 


关键字:keil  大常量计算 引用地址:keil大常量计算问题

上一篇:c52串口通讯RS232总结
下一篇:51系列单片机12M晶振与9600波特率漫谈

推荐阅读最新更新时间:2024-03-16 14:36

关于keil中的c语言和c++的混合编程
c语言编程简单,但是对于编写面向对象来说,实在是不方便。 一个工程是很多驱动文件的集合,底层驱动文件一般使用c语言来编写,但是一般数据封装和功能实现使用c++开发会更加方便。 所以在工程中使用c语言和c++混合编程是非常又必要的。 第一步先设置一下keil的c++编译环境,只需设计下面一步即可。 a.h文件: #ifndef _A_ #define _A_ void funA(); #endif a.c文件: #include a.h void funA() { } b.h文件: #ifndef _B_ #define _B_ class B { public:
[单片机]
Keil MDK STM32系列(四) 基于抽象外设库HAL的STM32F401开发
概述 Windows下使用Keil MDK5进行 STM32F401 的开发和编译, 配合ST-LINK工具进行烧录, 使用硬件抽象库HAL. STM32F401硬件环境和连接 略, 与SPL环境相同 STM32F4 硬件抽象库 STM32F4xx_HAL_Driver 直接下载 STM32CubeF4 MCU 固件开发包 前往 https://github.com/STMicroelectronics/STM32CubeF4 点击Code - Download ZIP 文件比较大, 有接近300M, 解压备用 当前版本是v1.26.2. ST硬件抽象库HAL结构说明 STM32CubeF4Drivers 目录结构
[单片机]
keil mini2440 分散加载文件scatter中(InRoot$$Sections)的理解
查阅一些资料终于认识了 (InRoot Sections)实现对映像的加载,而这一段代码就是∗(InRoot Sections)实现对映像的加载,而这一段代码就是∗(InRoot Sections)它是__main()的一部分。 从启动代码说起 启动代码 1。异常/中断跳转的地址表。 2。堆栈初始化 3。分散加载镜像文件 IMAGE(映像文件) 1个RO, 1个RW, 1个ZI组成。 并且RO的load region和execution region相同,这个里面放置 *(InRoot$$Sections) 主要作用COPY RW区到RAM,然后再RW区后面创建ZI区。 库函数__main函数中有这个段。 注释掉会报出
[单片机]
KEIL51调试时一些错误总结
(1)提示无M51文件 编译时候提示: F:\...\XX.M51 File has been changed outside the editor, reload ? ------ 解决方法: 重新生成项目,产生STARTUP.A51即可。 (2)L15重复调用 ***WARNING L15: MULTIPLE CALL TO SEGMENT SEGMENT: ?PR?SPI_RECEIVE_WORD?D_SPI CALLER1: ?PR?VSYNC_INTERRUPT?MAIN CALLER2: ?C_C51STARTUP 该警告表示连接器发现有一个函数可能会被主函数和一个中断服务程序(或者调
[单片机]
Keil MDK下如何设置非零初始化变量(复位后变量值不丢失)
一些工控产品,当系统复位后(非上电复位),可能要求保持住复位前RAM中的数据,用来快速恢复现场,或者不至于因瞬间复位而重启现场设备。而keil mdk在默认情况下,任何形式的复位都会将RAM区的非初始化变量数据清零。如何设置非初始化数据变量不被零初始化,这是本篇文章所要探讨的。 在给出方法之前,先来了解一下代码和数据的存放规则、属性,以及复位后为何默认非初始化变量所在RAM都被初始化为零了呢。 什么是初始化数据变量,什么又是非初始化数据变量?(因为我的文字描述不一定准确,所以喜欢举一些例子来辅助理解文字。) 定义一个变量:int nTimerCount=20;变量nTimerCount就是初始化变量,也就是已经有初值;
[单片机]
Proteus在单片机系统设计中的应用
1 引言 单片机是电子类等相关专业的一门重要课程,在实际工程中有着广泛应用。全国各大中专院校开设了不同类型的单片机课程及相关的实验和综合设计。在这些专业的课程设计、毕业设计及大学生电子竞赛中,单片机也是一种非常重要的开发工具。 随着计算机技术的迅速发展,使用EDA软件进行电路仿真与设计已经成为一种趋势。众多院校在电路实践教学改革中引入了EDA技术,并建立了EDA实验室。配备了EWB、Pspice、Multisim、Protel、MaxPlus II、SystemView等相关软件及配套硬件。但这些设备对仿真单片机却无能为力。本文介绍的一款EDA软件——Proteus,则有很大的不同,它的最大特点就是能仿真单片机。 目前Pr
[单片机]
Proteus在单片机系统设计中的应用
KEIL C51代码优化详细分析:局部参数放寄存器变量,数据覆盖技术
阅读了《单片机与嵌入式系统应用》2005年第10期杂志《经验交流》栏目的一篇文章《Keil C51对同一端口的连续读取方法》(原文)后,笔者认为该文并未就此问题进行深入准确的分析 文章中提到的两种解决方法并不直接和简单。笔者认为这并非是Keil C51中不能处理对一个端口进行连续读写的问题,而是对Kei1 C51的使用不够熟悉和设计不够细致的问题,因此特撰写本文。 本文中对原文提到的问题,提出了三种不同于原文的解决方法。每种方法都比原文中提到的方法更直接和简单,设计也更规范。(无意批评,请原文作者见谅) 1 问题回顾和分析 原文中提到:在实际工作中遇到对同一端口反复连续读取,Keil C51编译并未达到预期的结果。原文作者
[单片机]
关于keil4ARM 通过Jlink在线调试的问题
keilC中的设置如下: 注意第4张图和第5张图的区别 当我点选上updata target before debugging的时候,就能够直接进行在线调试,如果不点选的话,flash里面的程序还是上次的程序,这时候进行在线调试,还是对上次的程序进行调试。 点选上之后,每次的程序都是新的,可以直接进行在线调试。这种动作类似于先将程序在flash download中下载,然后直接点击调试按钮仿真 是一样的。
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved