零基础学C语言程序设计
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

3.2 数值处理

在C语言中,数值处理包括整数、小数的各种运算处理,如算术运算、扩展赋值、增量/减量运算、正/负运算等。本节将详细讲解这些运算处理方式,以及对运算时所出现的各种问题的处理。

3.2.1 算术运算

算术运算又称四则运算,包括加法、减法、乘法和除法。在数学中,我们会通过加号、减号、乘号和除号实现这几种运算。在C语言中,提供了对应的运算符,如加法运算符(+)、减法运算符(-)、乘法运算符(*)、除法运算符(/),以及其他运算符。下面依次讲解这几种运算符的使用。

1. 加法运算符

加法运算符(+)是二目运算符,拥有两个操作数。该运算符可以让两个数值或变量进行相加运算。

加法运算符的语法如下:

2. 减法运算符

减法运算符(-)是二目运算符,拥有两个操作数。该运算符可以让两个数值或变量进行相减运算。

减法运算符的语法如下:

3. 乘法运算符

乘法运算符(*)是二目运算符,拥有两个操作数。该运算符可以让两个数值或变量进行相乘运算。

乘法运算符的语法如下:

【示例3-9】将两个变量进行加法、减法及乘法运算后的结果赋给变量。

程序如下:

运行程序,输出以下内容:

在进行运算时,如果运算结果超出了对应变量可存储数值的最大范围后,就会出现溢出错误。一旦出现溢出错误后,输出的运算结果将变成一个随机数。

【示例3-10】将两个整型变量进行乘法运算,而运算结果超出整型变量的范围。

程序如下:

运行程序,输出以下内容:

4. 除法运算符

除法运算符(/)属于双目运算符,拥有两个操作数。该运算符可以让两个数字或变量进行相除运算。除法运算符的语法如图3.4所示。

图3.4 除法运算符的语法

【示例3-11】将两个变量相除的运算结果赋给变量。

程序如下:

运行程序,输出以下内容:

注意:由于除法运算不一定是整除运算,所以存放除法运算的结果变量,一般会声明为小数类型。

另外,在除法运算中,要注意操作数2,也就是除数不能为0;特别要注意当除数为一个变量时,该变量的取值一定不能为0;如果操作数2为0,程序运行时会出现错误。

【示例3-12】验证在除法运算中,当操作数2为0时,运行程序时会出现错误信息。

程序如下:

运行程序,输出以下错误信息:

5. 求余运算符

求余运算符(%)属于二目运算符,拥有两个操作数,且两个操作数只能为整数。该运算符可以让两个数字或变量进行求余运算。求余运算符的语法如图3.5所示。

图3.5 求余运算符的语法

求余运算在生活中十分常用。

【示例3-13】770个员工去旅游,每辆大客车上能坐48人,为了节约资源,利用求余运算计算出除了坐满人的大客车外,还要雇用一个几人座的小汽车以使剩余的人坐下。

程序如下:

运行程序,输出以下内容:

(1)在求余运算时,如果操作数1为正整数,操作数2为负整数,则运算结果为正整数。

【示例3-14】将正整数与负整数进行求余运算。

程序如下:

运行程序,输出以下内容:

(2)在求余运算时,如果操作数1为负整数,操作数2为正整数,则运算结果为负数。

【示例3-15】将负整数与正整数进行求余运算。

程序如下:

运行程序,输出以下内容:

(3)在求余运算时,如果两个操作数都为负整数,则运算结果为正整数。

【示例3-16】将负整数与负整数进行求余运算。

程序如下:

运行程序,输出以下内容:

注意:编译器不同,会导致求余运算结果的符号位不同。

3.2.2 扩展赋值运算

可以发现,在进行算数运算时,有些表达式中操作数1与表达式结果相同,如图3.6所示。

图3.6 操作数1与表达式结果相同

显然,a=a*b这种写法有些重复。在C语言中,为了提高运算效率和简化书写,提供了扩展赋值运算符,又称复合赋值运算符。扩展赋值运算符如表3.2所示。

表3.2 扩展赋值运算符

【示例3-17】使用加法赋值运算符进行求和运算。

程序如下:

运行程序,输出以下内容:

3.2.3 增量/减量运算

老师在点名时一般是按照学号的顺序进行的。如果想要在程序中按照学号读取学生时,那么指向学号的变量就会不断加一。如果使用加法运算符,程序会十分麻烦。为了对应这种问题,C语言提供了专门的运算符号,将其称为增量运算符与减量运算符。

1. 增量运算符

增量运算符(++)属于一目运算符,拥有一个操作数,且操作数必须是整数和小数类型的变量。该运算符可以让变量进行自加运算。根据运算符使用的位置,该运算符有以下两种语法形式。

(1)前缀增量运算符会让操作数自增1后再参与其他运算,其语法如下:

【示例3-18】使用前缀增量运算符进行运算。

程序如下:

运行程序,输出以下内容:

对于printf("运算后i的值为:%d",++i);这行代码,首先i变量会进行自增,然后才会参与输出,所以输出结果为101,如图3.7所示。

图3.7 printf("运算后i的值为:%d",++i);这行代码的分步运行

(2)后缀增量运算符会让操作数参与运算后,操作数的值再自增1,其语法如下:

【示例3-19】使用后缀增量运算符进行运算。

程序如下:

运行程序,输出以下内容:

对于printf("运算后i的值为:%d",i++);这行代码,首先输出变量i,然后进行变量i的自加运算,所以第一次输出结果为100,第二次输出结果为101,如图3.8所示。

图3.8 printf("运算后i的值为:%d",i++);这行代码的分步运行

2. 减量运算符

减量运算符(-)属于一目运算符,拥有一个操作数,且操作数必须是整数和小数类型的变量。该运算符可以让变量进行自减运算。根据运算符使用的位置,该运算符有两种语法形式。

(1)前缀减量运算符会让操作数自减1后再参与其他运算,其语法如下:

【示例3-20】使用前缀减量运算符进行运算。

程序如下:

运行程序,输出以下内容:

对于printf("运算后i的值为:%d",++i);这行代码,首先i变量会进行自减,然后才会参与输出,所以输出结果为99,如图3.9所示。

图3.9 printf("运算后i的值为:%d",++i);这行代码的分步运行

(2)后缀减量运算符会让操作数参与其他运算后,操作数的值再自减1,其语法如下:

【示例3-21】使用后缀增量运算符进行运算。

程序如下:

运行程序,输出以下内容:

对于printf("运算后i的值为:%d",i-);这行代码,首先输出变量i,然后进行变量i的自减运算,所以第一次输出结果为100,第二次输出的结果为99,如图3.10所示。

图3.10 printf("运算后i的值为:%d",i-);这行代码的分步运行

3. 增量/减量运算符的使用建议

由于增量/减量运算符会涉及多个加号或减号的使用,所以在一个表达式中要尽量避免多次出现同一类运算。

【示例3-22】使用多个增量运算符的运算。

程序如下:

运行程序,输出以下内容:

对于i=i+++i++;这行代码,就多次使用了自增运算符,而且还与加法运算符混用。这种书写方法不但可读性极低,而且经不同编译器的运算,其结果会不同。所以,一定要尽量避免这种书写方法。

3.2.4 正/负运算

生活中所接触的数据会有盈亏之分。例如,对于公司来说,花费的金额就是负数,而盈利的金额就属于正数。在C语言中,使用正/负运算符用于数字的正/负运算。正/负运算符包含两个符号“+”与“-”。

1. 正运算符

正运算符(+)属于一目运算符,拥有一个操作数。该运算符一般用于格式上的对齐,并不能让负数变为正数。

正运算符的语法如下:

【示例3-23】验证使用正运算符无法改变数的值。

程序如下:

运行程序,输出以下内容:

从程序运行结果可以看出,正运算符并没有改变变量a的值。所以,正运算符一般用于格式上的对齐,如下所示:

2. 负运算符

负运算符(-)属于一目运算符,拥有一个操作数。负运算符可以让数字进行负运算。

负运算符的语法如下:

【示例3-24】使用负运算符改变值的正/负。

程序如下:

运行程序,输出以下内容:

从程序运行结果可以看出,负运算符将变量a、b的值进行了正/负改变。

3.2.5 数据类型不一致的处理

在捐款时,有人会捐100元(整数),而有人会捐1.5元(小数)。如果让计算机计算这些捐款,就会涉及整数与小数的相加问题。也就是说,两个数据类型的数字进行相加,涉及了数据类型不一致问题。对此,在C语言中,规定了以下3种针对数据类型不一致的处理方式。

1. 自动转换

在生活中,我们会先把大米放入袋子中,再将多袋大米放入汽车中,最后将几车的大米放到火车车厢中。这都是根据大米的量放置到合适的容器中,如图3.11所示。

图3.11 大米的容器

C语言的自动转换过程也是一样的。在C语言中,规定在表达式中如果出现数据类型不同的情况,都必须转换为同一类型数据才能进行运算,而自动转换的方向是由所占存储单元少的数据类型向所占存储单元多的数据类型转换。自动转换规则如图3.12所示。

图3.12 自动转换规则

【示例3-25】输出表达式的值占几个字节。

程序如下:

运行程序,输出以下内容:

从程序运行结果可以看出,表达式a+b的值占4个字节,这说明数值在计算时被自动转换为int类型。

注意:当赋值运算符(=)左、右两侧操作数的数据类型不同时,一定是将右侧操作数的数据类型转换为左侧操作数的数据类型。如果左侧操作数所占存储单元少于右侧操作数所占存储单元,则会出现数据丢失的情况。

【示例3-26】演示当赋值运算符左侧操作数所占存储单元少于右侧操作数所占存储单元时发生数据丢失的情况。

程序如下:

运行程序,输出以下内容:

从程序运行结果可以看出,程序运行输出的值明显是错误的,这是因为在存储数据时发生了溢出的情况,即发生了数据丢失的情况。

2. 小数运算

在C语言中,双精度类型是小数的默认存储方式。在表达式中如果出现了单精度和双精度两种类型的小数时,计算机会默认将其全部转换为双精度类型来进行运算和保存。

【示例3-27】输出表达式的值占几个字节。

程序如下:

运行程序,输出以下内容:

从程序运行结果可以看出,表达式a+b的值占8个字节,这说明数值在计算时自动转换为double类型。

3. 强制转换

强制转换又称手动转换。有时为了节约存储空间或其他目的,程序员要将数值手动转换为指定的数据类型。例如,表达式3.2+3.3的值会被自动存储为双精度类型,而为了节约空间,程序员可以将其强制转换为单精度类型,这样将节省很多存储空间。

强制转换的语法如下:

在该语法中,类型说明符的小括号是必须存在的,而表达式的小括号是可以不加的,但一定不要因为不写小括号而产生歧义,如图3.13所示。

图3.13 产生歧义

【示例3-28】输出表达式的值占几个字节。

程序如下:

运行程序,输出以下内容:

从程序运行结果可以看出,表达式a+b的值占4个字节,这说明数值在计算时被强制转换为int类型。

注意:如果将所占存储单元多的数据类型转换为所占存储单元少的数据类型,可能会导致溢出问题。

3.2.6 运算优先等级

在小学数学中,我们都知道算数的运算优先规则。在C语言中也一样,当遇到复杂的带有多个运算符的表达式时,就要注意遵守运算符的运算规则,该规则包含优先级与结合性两部分。

1. 优先级

在算术运算中,当遇到不同优先级的运算符时要遵循运算符优先级的先后顺序来进行运算。++与-的优先级是最高的;+与-的优先级是最低的;使用()可以改变运算的顺序。运算符优先级如图3.14所示。

图3.14 运算符优先级

【示例3-29】输出表达式的值。

程序如下:

运行程序,输出以下内容:

在上面程序中,表达式的运算顺序如图3.15所示。

图3.15 表达式的运算顺序

2. 结合性

在表达式中,如果所有的运算符优先级相同时,就要遵循运算符结合性来进行运算。运算符结合性分为左结合与右结合。左结合的执行顺序是从左向右,如四则运算符都是左结合的。右结合的执行顺序是从右向左,如增量运算符是右结合的。

【示例3-30】输出表达式的值。

程序如下:

运行程序,输出以下内容:

在上面程序中,表达式的运算顺序如图3.16所示。

图3.16 表达式的运算顺序

注意:在书写表达式时一定要注意运算符优先级问题,这样才能让计算机正确表示表达式的运算顺序。

3.2.7 数值比较

在上体育课时,同学们会按照个子高低进行排队。在这里,就涉及了身高的比较问题。在C语言中,提供数值比较的专用运算符-数值比较运算符。

数值比较运算符属于二目运算符,拥有两个操作数,可以用于两个操作数的比较。将使用数值比较运算符构建的表达式称为关系表达式。

关系表达式的语法如下:

在C语言中,提供了6个数值比较的运算符,如表3.3所示。

表3.3 数值比较运算符

【示例3-31】比较两个数的大小。

程序如下:

运行程序,输出以下内容:

在上面程序中,返回值为1,表示8确实小于10。

注意:由于小数类型的数值有保留小数点后精确位位数的问题,所以对有些值的判断会出现误差。

【示例3-32】比较两个小数的大小。

程序如下:

运行程序,输出以下内容:

在上面程序中,变量a的值3.000 000 008明显是比变量b的值3.000 000 009小,返回值应该为1,但返回值却为0,表示变量a是大于或等于变量b的。这里就是因为单精度类型小数只精确保留小数点后6~7位,从而出现判断错误。