变量可分为整型变量、实型变量、字符型变量等,整型变量按照有无符号分为有符号整型(signed)和无符号整型(unsigned)两类,按照所表达的数值范围和占内存字节数可分为短整型(short int)、基本整型(int)和长整型(long),某些系统还支持长长整型(long long)。同时,各类型又分有符号和无符号两类。实型变量主要有两种形式单精度浮点型(float)和双精度浮点型(double)。
对于整型变量而言,通常的操作系统默认都是有符号类型,如果定义无符号类型,需在前面加unsigned。
1. 计算机操作系统位长
计算机的内存结果是按照字节为单位进行信息存储,每个字节都有一个唯一的数字标识,通常将这个标识称为地址或物理地址。地址有长有短,从 1 字节到 8 字节不等,计算机操作系统按照内存地址的长度可分为 8 位机、16 位机、32 位机以及 64 位机等。
8 位机指内存地址为 1 字节数,地址值通常使用十六进制数表示,如 0xFB,0x12 等。通常较简单的单片机采用 8 位地址方式,称为 8 位机。
目前最常用的是 32 位机,即地址长度为 32bit,共 4 字节,例如,0x0012ff65,0x001B56FF。32 位机最大取址范围为 0x00000000-0xFFFFFFFF,共 4 GB内存。通常C语言中会使用其中一部分,如无特别说明,本书程序全部基于 32 位机。
2. 32位机计算机操作系统
32 位机中,短整型(short)在内存中占 2 字节,基本整型(int)在内存中占 4 字节,长整型(long)在内存中占 4 字节等。64 位机中还有长长整型(long long),在内存中占 8 字节。单精度浮点型(float)在内存中占 8 字节,双精度浮点型(double)在内存中占 16 字节。字符型变量可分为有符号和无符号两种类型,有符号字符型(char)和无符号字符型(unsigned char),两者在内存均占 1 字节。如表 2-4 所示为不同类型变量在内存中所占字节数。
表 2-4 数据类型内存结构表
C语言中可通过运算符sizeof获取数据类型内存字节数。
范例 2.15
MemoryTypeByte.c
MemoryTypeByte.c使用运算符sizeof获取不同数据类型在内存中所占的字节数,然后通过输出打印,由此可以直观地显示各种数据类型的内存字节数。
(光盘\chat2\MemoryTypeByte.c)
程序中第 4、5 和第 6 行分别使用了sizeof运算符,用于获取对象在内存中所占字节数,对象可以是变量、常量、指针、结构体等,后续章节将对该函数的应用做详细介绍。执行上述代码,输出结果为:
C语言规定,变量必须先定义后使用,未经定义的变量在使用时会提示错误而导致程序编译无法通过,合理定义变量和利用变量,将对程序的执行效率产生重要影响。
1. 怎样定义变量
变量在使用前需要先定义,其定义的一般形式有两种:
类型说明符是指要定义的变量类型,如整型变量的类型说明符为int、short或long等。如定义整型变量i,j,k,可以这样定义:
C语言中,这种表达叫做语句,分别表示定义 3 个int型的变量I,j,k。语句是C语言中的基本程序结构,并且必须以“;”作为语句的结束符,分号不可省略。
也可以将定义放在同一个语句中:
变量名也称为用户标识符,变量名的定义应遵循一定的规则。
(1) 由字母、数字和下划线组成。
(2) 变量名不能和关键字相同。
(3) 第一个字符必须是字母或下画线。
关键字是由C语言规定的具有特定意义的字符串,通常也称为保留字。用户定义的变量等标识符不应与关键字相同。C语言的关键字主要分为类型说明关键字和流程控制关键字两类,如表 2-5 所示为关键字表。
对每个关键字的详细说明,请参看附表 2,本书将在后续章节中依次讲解每个关键字的作用和用法。同时,最新的C语言语法版本中,将bool也列为关键字之一。
用户定义的变量名(用户标识符)不得与关键字相同,同时必须满足上述另外两个条件,如下列变量名均是非法的变量名 3i,int,ysl@163.com,_myname!,bool等。
表 2-5 关键字表
2. 变量赋初值
在变量定义的同时,还可以进行赋值操作,称做变量赋初值,如下定义:
像这种在定义变量的同时并赋予其一定值的操作称为变量赋初值。等价于下面语句:
当在一条语句中定义多个变量时,可以使用逗号表达式作如下定义方法:
关于逗号表达式的概念将在第3章中予以介绍,需要注意的是,不能使用如下方法给不同变量赋相同的值:
但可以在定义之后进行赋值操作,如:
此外,对于单个变量的赋值,可以在定义时进行,也可以在定义之后进行。
前面已经讲过,整型变量按照在内存中所占字节长度分为短整型(short)、基本整型(int)、长整型(long)等,其中short、int和long称为类型说明符。C语言系统通过这些关键字判断不同类型的变量,从而为其分配相应的内存空间。
范例 2.16
IntegerVariableDefine.c
IntegerVariableDefine.c 分别定义short、int和long型的整型变量,打印输出各个变量的值,并输出各种整型变量在内存中所占字节数。
(光盘\chat2\ IntegerVariableDefine.c)
程序第 4、第 5 和第 6 行分别定义 3 种类型的变量I、j和k并赋初值。程序第 7 行输出3 个参数的值,第 8 行分别输出 3 个参数的内存长度。程序运行输出结果为:
虽然变量i、j和k的值相同,但它们在内存中的存储结构却不尽相同,如图 2-3 所示为不同变量类型在内存中的结构示意图。
需要注意的是,在内存中数值以二进制形式存放,并以十六进制形式显示,所以对于十进制数 10,在内存中会以十六进制a显示。
变量在使用前应先定义,若未定义就使用某变量,程序编译时将出现错误,若去掉程序第 4 行,在编译时将出现如下错误:
错误原因是使用了未定义的标识符i。因此,在使用变量前一定记住先定义该变量,这对于程序初学者是经常犯的错误。
变量在使用前除了先定义,还要赋初值。赋值方式可以先定义后赋值,也可以在定义的同时赋初值。若使用了未经赋值的变量,则将出现不可预期的结果。
范例 2.17
InitialVariable.c
InitialVariable.c 定义整型变量i、j和k,未给变量i赋值便使用该变量,输出i、j和k的值,检查使用未赋值变量的输出结果。
(光盘\chat2\ InitialVariable.c)
程序第 7 行最后一个输出格式控制用于输出表达式i+j+k,用以验证未赋值参数参与运算后的输出结果。程序输出结果为:
程序输出了无法预料的结果-13108 和-13088,这是由于变量i未经赋值便进行了引用,通常称这种不确定值为垃圾值。为避免这类垃圾值影响程序正确结果,一般在变量定义时赋初值 0,如无特别说明,本书后续章节都将遵循这一约定。
实型变量分为单精度型(float)和双精度型(double)两类。单精度型变量占 4 个字节,精确位数为 7 位有效数字;双精度型变量占 8 个字节,精确位数为 16 位有效数字。实型变量的定义和整型变量类似,如:
分别表示定义单精度型变量p1, p2, p3 和双精度型变量x1, x2, x3。
范例 2.18
RealTypeVariable.c
RealTypeVariable.c 分别定义float和double型的变量,为两变量赋相同的值,并打印输出,检查两者输出值的差别。
(光盘\chat2\ RealTypeVariable.c)
程序第 4 行和第 5 行分别定义了float型和double型变量i和j,程序第 6 行和第 7 行分别对这两个变量赋初值以验证不同类型的精度。程序运行输出:
程序第 8 行输出语句中%.10f格式为输出小数点后 10 位数字。导致出现不同结果原因是float型和double型的精确度不同。
实型变量在内存中的数据存储格式按照符号位、指数位和尾数 3 部分。符号位表示数字的正负,指数位表示数字的指数大小,尾数部分表示小数点后能够精确的位数。如图 2-4 所示为float型和double型变量在内存中的存储形式。
图 2-4 实型变量存储结构
图 2-4(a)所示表示float型变量在内存中的存储结构,最高位为符号位,前 23 位(第
0 到第 22 位)为尾数位,中间 8 位(第 22 到第 30 位)为指数位。图 2-4(b)所示表示double型变量在内存中的存储结构,最高位为符号位,前 52 位(第 0 到第 51 位)为尾数位,中间11 位(第 52 到第 62 位)为指数位。
需要说明的是,在计算机中数据以二进制存储,因此上述各位值同样以二进制形式存储在计算机中,并且以规范化的指数形式存放。float型中,23 位二进制可表示十进制小数部分7 位,double型中,52 位二进制小数可表示十进制小数部分 16 位。
因此,float型只能精确到小数点后 7 位,其中第 7 位采用四舍五入,而double型则能精确到小数点后 16 位,能够准确输出所表达数据。
字符变量在内存中占 1 个字节,类型说明符为char。与整型变量和实型变量的定义类似,字符型变量的定义格式为:
表示定义了一个字符变量a,变量a可以被赋予任何字符常量和整型值。由于字符变量只占用 1 个字节,因此只能存放 1 个字符数据。在内存空间中,字符是以ASCII码值存放的,例如字符'a'在内存中存放的是其ASCII码值 97。正因为字符在内存中的这种存储模式,通常也把字符变量当做取值在 0~127 之间的整型量看待,并且字符变量也可以参与算数运算。
范例 2.19
CharacterVariableCalc.c
CharacterVariableCalc.c 分别定义整型和字符型变量,以整型和字符型打印输出,检查输出结果。
(光盘\chat2\ CharacterVariableCalc.c)
程序第 4 行和第 5 行分别定义了char型和int型变量c和i,程序第 6 行和第 7 行分别按不同的输出格式输出这两个变量,以验证两种数据类型的通用性。程序运行输出:
因为在ASCII码表中,大写字母A是 65,小写字母a是 97,两者相差 32,程序正是利用这一规律实现了使用算术运算进行大小写字母转换。这一方法在工程应用中被广泛采用。
枚举是基本数据类型之一,有的版本也把它作为符合数据类型,它是一组被命名的整型常数,枚举类型在工程应用中非常广泛。
在实际应用中,某些变量需要被限定在一定的范围内,如世界有 7 大洲,地球有 24 小时,一周有 7 天,一年有 12 个月等。这些范围很难以整型、实型或字符型加以说明,因此,为满足能够表达某组数据列表的需求,C语言提供了枚举类型。
枚举类型的定义使用关键字enmu,其一般形式为:
用户标识符即指枚举名,在枚举参数表列中应罗列出所有需要的值,这些值也称为枚举元素。例如:
该枚举类型名为world,共有 7 个枚举值。凡被声明为world类型的枚举变量,取值只能为枚举参数表列之一。
枚举变量可以先定义后声名,也可以在定义枚举类型的同时声明枚举变量。仍然以大洲名称为例,如下形式定义枚举类型同时声明枚举变量x,y,z。
如下形式为先定义枚举类型,后声明枚举变量。
需要说明的是,枚举参数表列中的值为常量,不是变量,因此其值不能被修改。因此,对枚举world中的元素的如下操作是非法的。
若程序中在枚举类型定义时没有对元素进行值对应,系统为枚举类型的每个元素顺序编号,并从 0 开始递增赋值,如上述定义中Asia的值为 0,Europe的值为 1,Antarctica的值为 6。
枚举可在定义时对元素进行值对应,通常也称为将某个值赋给元素。
范例 2.20
EnumValueTest.c
EnumValueTest.c 假定各大洲每年的工业产值单位为万亿¥,定义枚举类型数据,用于存储各大洲的工业产值状况,例如,亚洲为 500 万亿¥。
(光盘\chat2\ EnumValueTest.c)
程序第 6 行首先将整数 500 赋给枚举元素Asia,因此元素Europe将在前一元素基础上增1,变为 501。元素Africa被赋值为-100 之后,元SouthAmerica变为-99,同时NorthAmerica重新赋值为 800。同样,元素Oceania被赋值为 66 之后,Antarctica变为 67,而经过第 22 行算术运算之后,en_calc的值变为 901。程序运行输出结果:
枚举类型元素不能赋值为实型常量,如程序中第 6 行改为Asia = 500.5,程序将出现编译错误。也不能试图在枚举定义之后修改元素的值,若在程序 21 行后添加Asia = 10,程序同样出现非法错误,原因是Asia是一个常量,其值不允许再次改变。
(1) 常量和变量有什么区别呢?
解答:常量是不能改变的量,例如某个城市的名字,一般是不会改变的,但该城市的天气情况却经常变化,因此,城市的名称就是常量,而该城市的天气就是变量。
(2) 除了二进制、八进制、十进制和十六进制,还有其他进制么?
解答:有的,比如等式 110+20=200 也有成立的时候,但在什么情况下成立呢,显然十进制是不成立的。这里我们假设它在x进制下成立,那么将这个等式展开成十进制模式:
转化为:
解得:
显然,x=0 不符合要求,因此取x=3。
因此,在三进制情况下上述等式成立。所以,存在除一般进制外的其他进制,只不过日常生活中很少应用。
(3) 常量 123、123.和 123.0 有在内存中存储时有什么区别?
解答:常量 123 表示整型常量,在内存中占 4 个字节的存储空间,123.和 123.0 表示实型常量,在内存中占 8 字节的存储空间(实型常量在内存中默认都以双精度实型存储)。
(4) 定义变量为short和int类型,都属于整型,两者有什么区别呢?
解答:实际应用中,有时需要用到较大的数值,有时仅使用较小的数值。例如表示时间的变量,最大值为 24 或者 60 即可,而有些又需要较大的数值才能表示,例如地球进化的时间,地球到月球的距离等。short型只能表示-32768~32767 之间的数,内存中占 2 字节,int型能表示-2147483648~2147483647 之间的数,内存中占 4 字节。有时候不需要表达很大的数值时,就使用short类型,这样就节省了内存的很多资源。
(5) 为什么变量不能重名?
解答:变量不像人一样,可以根据体貌特征分析两个姓名相同的人,计算机识别不同的变量的唯一方法就是按照不同的变量名区分。而假如有一变量定义为int型,然后又将这一变量定义为short型,系统会因为定义时占不同的内存单元而出现矛盾,为了避免计算机这种矛盾出现,C语言系统禁止使用相同的变量名。
(6) 为什么计算机中需要区别有符号和无符号
解答:实际应用中,有些量必须由自已决定是否需要正负。如果这个量不会有负值,那么我们可以定它为不带正负的类型。例如年龄,总是正的,而某人的净收入,可能为正,也可能为负。计算机提供无符号的目的是尽可能对没有负号出现的变量赋予更大范围的数值。以使这个变量使用时可以更大范围的存取数据。
(7) 字符串"a"和字符'a'有什么区别?
解答:字符和字符串主要区别在于两者在内存中所占的资源不同。字符串中每个字符在内存中占 1 个字节,但字符串末尾系统自动添加字符串结束符'\0',因此,字符串"a"所占字节数为 2,而字符'a'所占字节数为 1。
(8) 标识符_input_,This-year,goto,3ku是否可定义为变量?
解答:变量定义的规则:
(1) 由字母、数字和下画线组成
(2) 变量名不能和关键字相同
(3) 第一个字符必须是字母或下画线
第一个标识符_input_可以作为变量;第二个标识符This-year由于含有字符"-",因此不能作为变量;第三个标识符goto为C语言关键字,因此也不能作为变量;第四个标识符 3ku由于首字母为数字 3,因此也不能作为变量。
(9) 为什么字符变量或常量可以进行算术运算?
在计算机系统中,字符都是以ASCII码存在系统中的,也就是每个字符都相当于一个数字,因此,程序使用字符作算术运算就相当于使用数字作算术运算,两者没有本质的区别。例如,定义变量c:
C语言中,字母以ASCII码存放,且大小写字母按序ASCII码递增,在字母'a'的基础上加 3,为字母'd',因此,变量c为字母'd'。
(10) 什么情况下会使用枚举呢?
解答:枚举类似于定义一些符号,这些符号都代表一定的数值,并且这些符号可以定义为人们容易看懂的名字。有时候在程序中要对一些变量赋值,同时又希望这些值表示一定的含义,这时使用枚举就可以顺利地达到要求。例如,定义枚举类型:
这样定义了一个一天中日常作息时间表,例如,有变量作为时间,当要验证该时间是否需要吃午饭时,只需要该变量和HaveLunch作比较,若相等,则该吃午饭了,若不等,则不吃午饭。这样设置将很容易使程序阅读者理解程序设置的功能。
(1) 使用%d格式分别打印十进制数 100 和十六进制数 0x100,分析打印数值的差别。
(2) 浮点数 100.123 可以使用%d格式打印输出,也可以使用%f格式输出,试编写完整C语言代码实现使用这两种不同格式输出的效果。
(3)字母"A"和"a"的ASCII码分别为 65 和 97,写一段完整的程序,将"A"和"a"的ASCII码输出到屏幕上。(提示:使用十进制输出格式%d可以输出字符的ASCII码值)。
(4) 试编写一段完整的程序,利用格式输出%s,输出字符串:
提示:若使用一条输出语句,调用printf函数,字符串中可以使用换行符'\n'。
(5) 变量定义时通常使用变量定义语句,如定义字符型变量c,可使用语句:
试编写程序,定义整型变量i,并输出i的值。(提示:定义变量需要赋初值,否则将输出垃圾值)
(6) 在程序题 5 的基础上,修改程序,定义char型变量a,并赋初值'a',使用a进行算术运算后输出最后一个小写字母'z'。(提示:字符可使用ASCII码参与算术运算,例如,变量'b'的ASCII码值等于'a'+1)
(7) 参考范例 2.6,试编写程序输出圆周率小数点后 10 位。(提示:圆周率小数点后20 位为 3.14159265358979323846)
(8) C语言中,可使用格式输出%o输出八进制数,试编写程序,以八进制输出整数10000 和-10000。
(9) C语言中,可使用格式输出%x输出十六进制数,试编写程序,以十六进制输出整数 1234567。
(10) 试编写程序,定义表示星期的枚举类型:
并在程序中定义枚举类型变量enum week week1;
使week1 = Sat并输出week1 的值。
附表 1
附表 2