C语言剖解(04)
好了,我们继续开讲。本篇我们说:变量、整型常量和整型变量。
我们上一篇,着重调侃了常量,那什么是变量呢?很简单,相对于常量来说,能够改变的量就是变量。变量名本身没有什么特殊含义,只是一个表示形式,就像人名一样。确切的说,变量有三部分组成,分别是:
变量名、存储空间和变量的值。
变量名是我重点强调的对象,我将其表述为三规定两规范。
三个规定如下:
规定一:以字母、数字和下划线构成,以字母和下划线为首位。
规定二:不能和C语言的关键字重复。
规定三:大小写严格区分。
说道这里,我不得不说,我非常佩服一些 “闲人”,这些人对C语言语法的研究,简直是登峰造极,以至于能够轻松的找到C语言的一些搞笑的东东,这些东东,我也很少见到。然而悲催的是,有些内容,研究者无非是想博大家一乐,或者警惕大家一些注意事项,可却成了一些公司的笔试题,这些题有时会让初学者对程序开发语言望而怯步。我庆幸我在刚踏上编程之路的时候,没碰到这些问题,否则,我也是欲哭无泪。若换做现在,如果哪个公司让我去面试,让我做题,我会直接扭头就走,或者抛下两句话,调侃一下面试官,然后昂首走出。为什么?因为这浪费我的时间,浪费我的青春。更不用说碰到这样悲催的事情了。
记得我有一次去一个小公司面试架构师,坐在那里等了好久,后来公司部门领导出来说:“不好意思,久等了,刚我们讨论了一下,我们很想录用你,也熟知你的能力,只是你看,你能不能把工资降一下,降到和我一个级别,你看行不?”
我有点晕,他们怎么熟知我的能力?事后才知道,原来他们本来打算对我进行技术面试的那个家伙,竟然是我带过的徒弟,经常因为能力问题被我骂的体无完肤。没想到来这个公司当开发经理了,他看到是我之后,直接就去和部门经理谈了,根本就没走出办公室。于是才有刚才那一出。也是从那次开始,我开始重新定位我的求职条件:研发人员在60人以上,有公司自己的技术储备和技术特色,否则免谈。想换工作的目的当然是要往高处走,以期尽可能多的掌握一些技能。
这些我有时也向学生表述一番,是为了提高大家的学习热情,更为活跃一下上课的气氛。同时向他们证明,他们的老师不是一个笨蛋,是个有故事的男人哦。
好,言归正传,我提高声音问学生:
“三规定记住了没?”
“记住了!”
听完学生回答,我不紧不慢的在main函数里写了如下一行代码:
int $1 = 0;
然后我问学生:“这个正确不?”
“不正确!”
说实话,我真不忍心拿这个打击学生。但无奈有些公司就这样出题,我得让学生知道这个是可以的。 于是,我书写如下代码
#include<stdio.h>
void main(void)
{
int $1 = 123456789;
printf("%d\n",$1);
}
我编译,是通过的,连个告警都没有,我运行,结果正确打印了123456789;
学生懵了。
“大多数情况下,规定是不会错的,但有极个别的内容,并不遵守规定,刘哥我本不想给你们说这个,但无奈有些公司那帮咸蛋们,非要这样考,偏偏这个就可以,至于为什么,大哥我也说不清。但换是我,可以淬一口吐沫到卷子上,然后一走了之,你们就不行,先找到工作才是硬道理。!”
学生们乐呵了。
“乐呵完了吧,下面咱们说两个规范,两个规范是什么呢?”
学生开始翻书,然后一头雾水,说上并没有给出总结。
“别翻了,两个规范如下:”
规范一:C语言并不规定变量名的长度,但是我们尽可能的让长度不超过16个字符。
规范二:我们尽可能的用简洁易懂的英文单词或者缩写来作为变量名,不要用汉语拼音。
“老师,错了,书上写的是8个字符”
“我呸, 尽信书不如无书,信刘哥。”
“我们信春哥!”
“咋不信曾哥呀,咋不信芙蓉姐姐呀!”
我边说边写了如下一个变量名;
intabcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789;(注意了,代码中是一行)
然后,我编译,程序不报错,只给出了如下告警
warningC4786:'abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcde
fghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789': identifier was truncated to '255' characters in the debug information
即编译器在编译的时候,把字符截取为255个字符了。
“看到了吧,规范还是很重要的,中国人老闲外国人的名字长, 那是用中国规范来说的,超出了规范,我们就记起来别扭了,明白了吧,如果一个班里,每个人的名字都很长,估计班主任点完名字,这堂课就结束了。”
“呵呵”
“所以,规范虽然不是规定,我们也要遵守。”
好,接下来,我们讲整型常量和整形变量。
整型常量有三种表示法:
十进制表示法:如:123,-456,八进制表示法:如:0123,0753;
十六进制表示法:0x123;
当然,这样讲如同读书,没什么意思,学生也难以记忆深刻。所以,通常我会举如下几个例子,并问学生;
0125
这是几进制?
“八进制”
029
这是几进制?
“八进制”
“毛,这是八进制么?八进制最大数是7,这里都到9了,还是八进制么?”
“哦!”
“哦,恍然大迷瞪吧!那这个是几进制?”我说完又写一个
“0xFG”
“什么都不是!”
“哎,对,聪明,看看编译器怎么说的”,我边说,边写出如下代码
int iVal = 029;
int iVal1 = 0xFg;
结果,编译器报出如下错误。
E:\lesson\cl_04\cl04.c(4) : error C2041: illegal digit '9' for base '8'
E:\lesson\cl_04\cl04.c(5) : error C2059: syntax error : 'bad suffix onnumber'
E:\lesson\cl_04\cl04.c(5) : error C2146: syntax error : missing ';'before identifier 'g'
E:\lesson\cl_04\cl04.c(5) : error C2065: 'g' : undeclared identifier
Error executing cl.exe.
cl08.exe - 4 error(s), 0 warning(s)
意思我就不多说了,大家看吧。
随后,我会给大家讲解一下码制转换。码制转换讲完,基本耗去我将近40分钟的时间了。
整型变量
讲完了码制转换,我开始讲整型变量。前面说过,变量有三部分构成:变量名、存储空间和变量值。整型变量当然也不例外。
我在main函数下写如下这么一个例子:
int iVal = 123456789;
书上有这么一说:“数据在内存中是以二进制的形式存放的。”于是,我打开内存”memory”,将iVal的地址输入到Memeory的编辑框中,看到的内容如下:
0012FF7C 15 CD 5B 07(这里注意了,如果你用的是win7或更高版本,首地址为0012FF44)
“看到内存中的二进制了么?”
“看到了!”
“吹牛吧,在那里?”
肯定看不到,显示的是16进制的形式,前面的0012FF7C表示变量的地址;变量的值为15 CD 5B 07,他们都是16进制的,原因很简单,二进制表示一个数会写的非常长。为了显示的方便,使用16进制来表示了,每4个二进制数的最大值自然是F,因此用两个16进制来表示1个字节(8个字符) 很浅显易懂。
“那15 CD 5B 07是不是123456789呢?”
“是!”
“真的么?”我反问道,并打开计算器,调制科学型,然后输入15 CD 5B 07,出来的结果转换为10进制是:365779719,而并非是123456789。
“为什么”,学生迷茫了。
“因为在我们VC中,采用的是小端表示法!”我边说,边把上面的值反过来输入到计算其中,即07 5B CD 15,然后再次转换为10进制,结果正确的显示了:123456789;那什么是小端表示法呢?我们再次把内存中的地址拉出来。如下:
0012FF7C 15 CD 5B 07.
0012FF80 C0 FF 12 00
我们看到第一行结束之后,第二行开始的地址为0012FF80,依据16进制表示法,一个地址占8个字节,每4个字节二进制数代表一个16进制数。因此2个16进制字符代表一个地址。0012FF7C是代表一个整数的首地址。即15所在的地址。那CD即为0012FF7D,如此 5B为0012FF7E;07为0012FF7F,所以,下一行,自然为0012FF80了,说明地址值是越来越大的。而123456789的16进制的自然表示法为:07 5B CD 15; 把最高位的07存放在最大的地址中,把最低位的15存放在最小地址中,这就是小端表示法。
“通过上面的分析,整型占几个字节?”
“4个”;
“对了,因此int iVal = 1 在内存中的存放应该为01 00 00 00”
我用VC再次在内存中查阅了并验证了一下。
“这下大家该明白了吧。”
“明白了”
“真的么,那么这个在内存中怎么存的呢?”我边问边改写代码如下:
int iVal = -5;
一个负号把学生弄的不知所措了。但也有聪明的。
“转换为2进制,最高位是符号位”
“聪明,那上面这个数的2进制数怎么写?”
“1000 0000 0000 0000 0000 0000 0000 0101”
“那改为16进制应该怎么写?”
“10 00 00 05”
“那在内存中应该怎么存?”
“05 00 00 10”
“好,我们看一下内存中是否是这样。”我再次打开内存查看,结果却是如下内容:
0012FF7C FB FF FF FF
“哦?”
“哦~~差异了吧,为什么不是呢?”
“负数是以补码的形式存放的。”还是有学生知道的。
“嗯!聪明一半,无论正负,都是以补码的形式存放的,只不过,整数的补码是其本身,负数的补码是其绝对值取反加1.”
于是我从新演示-5的二进制码如下。
绝对值:0000 0000 0000 0000 00000000 0000 0101
取反后:1111 1111 1111 1111 11111111 1111 1010
补码值:1111 1111 1111 1111 11111111 1111 1011
16进制码:FF FF FF FB;
内存存放:FB FF FF FF;
经过演示,学生大多笑了,笑了就对了,说明听懂了。
“为什么要用补码表示呢?”我这一问学生又不支声了。
“正5和负5之和是多少?”
“零”
“那好,我们看一下如果用原码,是多少?。”
0000 0000 00000000 0000 0000 0000 0101
+ 1000 0000 0000 0000 0000 0000 0000 0101
10000000 0000 0000 0000 0000 0000 1010
“很明显,答案不是0”,所以,科学家为了解决这类问题,想出了补码。我们再来看一下用补码的结果。
0000 00000000 0000 0000 0000 0000 0101
+ 1111 1111 1111 1111 11111111 1111 1011
———————————————————————————— 0000 0000 0000 0000 0000 0000 0000 0000
到这里,学生基本明白了。
看着学生微笑的脸,我想我应该是讲懂了。于是后面的整型变量的分类:
基本整型(有符号、无符号):短整型(有符号、无符号):长整形(有符号、无符号)我基本是一笔带过,唯一强调的是:
“时代变了,谭浩强的书中针对的对象也改变了,翻到书本的第43页,改一下:基本整型为4个字节,而不是2个,短整型为2个字节,长整型和基本整型在32位机上也是4个字节。至于64位机上,大家下去试一下,前提是,硬件和软件和操作系统都得是64位的才能试出来。”
“还有,C语言并没有明确规定每种整型所占的字节数,只是说:短整型不能大于整型,整型不能大于长整型”因此,上面的总结,是大多数操作系统和编译器认可的,并非所有!“
说完了这些,对整形变量的定义和数据的溢出,我则是照书宣讲。不在话下。