关于经典面试一年多少秒的思考!启发#define与UL!
2016年01月11日 13:52:03 Agou_66 阅读数:1935 标签: C语言#defineUL宏定义一年多少秒 更多
个人分类: C语言
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Agou_66/article/details/50496550
预处理-宏定义一讲中,一道经典面试题宏定义一年有多少秒?由此引发知识点的模糊,所以写博客作为记录,也共大家学习!
此题不考虑闰年问题,如下:
#define SEC_PER_YEAR 356*24*60*60
那么问题来了,到底这样定义好不好,我们知道宏定义是在预处理的时候做的,在预处理时他会将字符串SEC_PER_YEAR替换成356*24*60*60。
程序如图:
经过gcc -E 预编译后如图:
结果是:
为了数据的完整加()能让此宏定义更加安全,C语言默认宏定义的整形数字是 int,为了让该宏定义完美运行在8位、16位、32位、64位机器中将宏改进为(这句是很关键的,这就是机器不管是多少位,只要将这个数加上UL就会按照无符号数进行输出,就不会有溢出的的现象了,到底UL和unsigned long int 有何区别?看到下面的解释还是有区别的。UL值只能修饰"整形常量"的,不能修饰非整形常量,到底何为整形常量:3*4*6这个式子是不是整形常量呢?,看下面紫色的文本,“”UL值只能修饰"整形常量"“”的说法不正确,好像与整形常量没有多大关系,只是UL先给谁结合的问题,查百度:
C语言整形常量定义
2014年09月01日 19:16:41 atleks 阅读数:3308
在书中看到这样一道题目:用预处理指令#define声明一个常数,用以表明一年中有多少秒。书中给的答案是:
#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL
写一个小程序测试此宏定义,如下:
-
#include <stdio.h>
-
#define SECONDS_PER_YEAR (60*60*24*365)UL
-
int main()
-
{
-
printf("%lu\r\n", SECONDS_PER_YEAR);
-
return 0;
-
}
使用GCC编译时报如下错误: error: ‘SECONDS_PER_YEAR’ undeclared (first use in this function)
查阅一下C99标准(当然,也可以查阅最新的C标准C11,不过我使用的编译器大都是只支持到C99的),在6.4.4节讲到了关于常量的定义,查看其
关于整形常量的定义,如下:
对于十进制整数常量,其定义的形式为:decimal-constant integer-suffixopt, 在decimal-constant和integer-suffix之间并没有括号,这里的integer-suffix是整形常量的后缀名,譬如:45UL,45*34*23UL这两种情况都可以,但是如果写成(45*34*23)UL就错了,因为在整形常量和后缀名之间加上括号,这样就执行括号内的计算,然后再与UL结合,前面的计算就溢出(前提是int 的位数是16位,不能表示这个数了,如果写成45UL,45*34*23UL,系统首先是23和UL结合,这就认为这个数是unsigned long int 类型,进行了类型提升,符合了C语言算术计算中的类型提升原则,也就是说这种类型的表示的范围被扩大了,前面的数字再乘进来,就会按照unsigned long int类型的范围来表了,)了,再加上UL还有什么意义呢?。所以,文章
开头提到的书中给出的例题的答案是错误的。下面详细讲解一下有关C语言中整形常量的定义。
在C语言中的整形常量,关于其默认类型,参考C99标准中6.4.4.1节:
The type of an integer constant is the first of the corresponding list in which its value can be represented.
即,C语言中一个十进制整数会被默认为int类型,如果长度超出int表示范围,而又在long int的范围之内,则会被认为是long int类型,如果long int
也表示不了,则会被认为是long long int类型。对于很多在常见的32位系统上编程的人来说可能结尾处加UL的作用并不明显,因为int和long都是32位,
但是对于嵌入式环境编程的人来说,这个就很有必要了,因为程序跑的环境有可能是16位的,且int是16位而long是32位的,这个时候加UL后缀就很有必要了。
对于宏定义:
#define SECONDS_PER_YEAR 60 * 60 * 24 * 365
则60*60*24*365的结果会被认为是一个int,如果是16位的环境则会溢出。如果在结尾加上UL,如下:
#define SECONDS_PER_YEAR 60 * 60 * 24 * 365UL
根据上述C99标准中有关整形常量定义可以知道其实UL首先是与365结合,即365会被认为是一个unsigned long类型的数,而根据C语言算术计算中的类型提升
原则可以知道整个算式的计算结果也会被认为是UL类型的。
对于宏定义:
#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL
之所以会报错是因为,UL找不到结合的数字,在16位的环境中,先计算60×60×24×365,则会溢出,然后再将这个溢出的值与UL结合,已经没有意义,故编译
会报错。
参考资料:
[1] C99标准
[2] http://bbs.21ic.com/icview-196689-1-1.html
[3] http://blog.csdn.net/ropenyuan/article/details/6157589
):
#define SEC_PER_YEAR (356*24*60*60)UL
在去编译,你会发现出错了!错误是 error: expected ‘,’ or ‘;’ before ‘UL’明明对着呢,为啥会错呢!就因为UL,UL告诉编译器(是编译器非预编译器)此时是unsigned long int类型,U和L是 “整数常量” 的后缀修饰,因此UL只能修饰整数常量本身;所以将宏写为:
#define SEC_PER_YEAR (356*24*60*60UL)
此时在编译发现完美运行,这回美了,定义出个完美的宏,错!在C51编译器中int是16
位的,int最大值是32767,然而356*24*60 是int类型已经溢出,所以依然得不得完美的宏。所以次宏改为:
#define SEC_PER_YEAR (1UL*356*24*60*60)
这次终于完美了;1UL保证以后的数据不会在任何情况下溢出!(为什么要加1UL呢?,我认为上面文本中紫色的文本内容讲的有道理。)
由于我没有51编译器,下面给个师哥的关于宏定义数据溢出的测试结果笔记链接:
通过笔记也可以加深对数据溢出的理解!