(C语言中的“物种”)乐创DIY C语言讲义——3.2节
3.3 初识变量
C语言中的变量,其实都只不过是用户访问存储器的一种手段而已。C语言中变量的定义包含了两个层面的信息,其一是根据定义变量时的数据类型,分配给这个变量多少容量的内存单元(以一个字节为最小单位),如一个char类型的变量,就分配给这个变量1个字节的存储单元;其二是根据这个变量的数据类型,按照不同方式去存放这个变量,如一个无符号的类型,就按照其原码放入存储单元。
变量定义的方法很简单,其格式如下:
数据类型变量名;
之前的代码里面没有提及过,C语言所有的语句一般必须以英文状态下的分号“;”结尾。这里先暂时这么定义,等到后面讲了程序语句的时候,再具体来说明语句的规则。
变量定义的方法非常浅显易懂,这里的数据类型,暂时就是只有我们第3.2节讲述的那些基本类型,而变量名的命名方式比较随意,但是有一个原则是必须以字母或者下划线开头,而跳过开头的其他字母可以用字母,下划线或者数字。一般变量名定义的要求需要简洁明了,并且尽可能地包含出这个变量所表示的信息。这里有个非常有意思的故事,一般Windows程序员都喜欢用“匈牙利命名法”来定义变量。匈牙利命名法”是一种编程时的命名规范,基本原则是:变量名=属性+类型+对象描述。而Linux喜欢的风格是易读性,其基本规则是:单词_单词_……。举个例子来说,比如定义一个float类型的变量,来描述小明的身高,那么如果是个Windows程序员来定义这个变量,先确定这个变量的属性,比如这个变量是个全局变量,那么它的属性就是“g”,类型部分,由于这个变量是个浮点数类型的,那么它的类型就用“f”表示,对象描述首字母要大写,就是“Height”,那么组合起来就是“float gfHeight;”,而Linux底下很简单,直接定义成“float xm_height;”,当然我对匈牙利命名法也不熟悉,具体使用的时候还有很多其他约束,大家对变量命名的时候也不需要特地去按照某种方式做,只要做到两点即可,一,保持变量的易读性;二,不要用拼音。
定义好变量之后,接下来就可以去使用这个变量了,比如小明的身高是1.88米,那我们直接用“=”进行赋值语句的操作即可,即“xm_height = 1.88;”。然而,为了简便起见,可以直接在定义这个变量的时候,为其赋值,即“float xm_height = 1.88;”这种操作方式被称为变量的初始化赋值。
接下来假设又来了一个小伙伴叫做小强,我们也需要为他定义一个身高变量,那我们以此类推就可以写成:
float xm_height;
float xq_height;
xm_height = 1.88;
xm_height = 1.78;
其实不需要这么麻烦,C语言规定,如果有两个变量是同一个类型的,那么可以将其放到一起去定义,中间用英文状态下的逗号“,”分隔开即可以写成:
float xm_height, xq_height;
xm_height = 1.88;
xm_height = 1.78;
甚至可以写成:
float xm_height = 1.88, xq_height = 1.78;
前面说到定义一个变量具有两个层面的意思,第一个意思很好理解,根据数据类型,分配存储区大小,但是第二个意思就值得思考了,编译器怎么按照不同方式去存放这个变量呢?请大家先回想一下我们第一章讲述的原码和补码概念。假设定义了一个无符号的字符型变量“unsigned char length = 10;“,那么编译器先分配一个字节内存的存储单元给变量length,当这个变量被赋值为10的时候,由于它为无符号类型的变量,因此可以直接将其原码放入到这一块内存中去。如图3-3-1所示。而由于定义的unsigned char类型的变量,它的数据类型规定是1个字节(8bit)的数值范围,因此一旦当赋值的数值大于最大值255的时候,只会把低八位存入,超出的部分自动舍弃,这就叫做“内存溢出”。因此在定义变量的时候,一定要根据变量的最大赋值的最大数值选择合理的数据类型。
图3-3-1 unsignedchar赋值
有些读者可能会对图3-3-1中出现的地址感到疑问,这个地址是什么?其实这个地址是内存空间的地址,当我们直接用数据类型定义变量时,这个变量究竟存放到哪里,是由编译器自动决定的,如果在有开启了MMU(内存管理单元)的设备上,这个地址甚至还不是最终的物理地址,在没有MMU的单片机上,这个地址编译器会自动产生和回收,因此用定义变量方式所获得的内存,我们不需要去回收。实际上,这些内存地址,都是一些相对地址而已,它们会基于程序的数据段基值进行偏移。
再来看一个int类型的定义,比如,小明现在身无分文,而且欠了银行100元,因此可以定义int类型的变量“int money = -100;”。当编译器检测到int类型的变量money的时候,首先分配给它4个字节的存储空间,而由于这个变量是个有符号类型的整数,因此当赋值的时候,需要把数值转换成补码存入,负数的补码是保持符号位不变,其余位取反加一,ok,搞定。如图3-3-2所示。尽管思路是对的,但是我们发现在将这些补码数据转换完成之后,如何将这32位的二进制数存入4个字节当中,这就成为了一个很纠结的问题。我们可以把数据从低字节到高字节的顺序依次放到从低地址到高地址上面,也可以把数据从高字节到低字节的顺序依次放到从低地址到高地址上面
图3-3-2 int类型数据存放问题
事实上,这个问题看似简单,实则困扰了很多计算机芯片设计者。因为正如1加1为什么等于2一样,往往越简单的问题,越是哲学层面的问题,同样,吃鸡蛋该从它的“大端”敲碎还是从“小端”敲碎也是个哲学问题,而高字节的数据到底放到低地址单元还是高字节的数据放到高地址单元,这就是计算机中的“大小端”之争。
大端模式,是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中。小端模式,是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中。
大端小端模式与计算机本身的架构有关系,一般我们所用的X86架构都是小端模式,而arm可以设置大小端。对于大小端的判别,我们后续学到联合体会介绍如何判断一块芯片的大小端。
好了,了解到这一层面之后,这个问题就简单了,将低字节保存在低地址单元中,高字节保存在高地址单元中。最终的内存放置如图3-3-3所示。
图3-3-3小端模式下int类型数据存放
浮点数类型变量的内存分配也一样,事实上,这种方式定义的变量存放的内存区域都是随机的,也就是说在相邻地方定义的“int a,b;“,其最终的内存地址也不连续。
综上所述,我们在定义一个变量时,应该按照以下原则:
根据需要定义的对象,确定定义变量的类型,字符型,整形还是浮点型;
根据需要定义的对象,确定使用有符号还是无符号;
根据需要定义对象的数值范围,选择整形的长度。
+++++好物推荐+++++
之前有小伙伴问我录视频用什么耳机比较好,我自己的就是这个JBL的 TUNE500BT。
说说优点吧:
1、这个耳机煲了之后,音质比我的Bose OE2要好多了,特别低音的呈现。
2、可以连两个蓝牙主机,比如电脑和手机,当你用电脑看视频的时候,突然来个电话,就直接可以接听了。
3、最重要的一个就是便宜,咱们乐观地说500元以内还是数一数二。
4、续航根本别担心,足够8小时。
缺点:
1、塑料感强;
2、没有有线插孔;