一.储存信息:变量和常量
C语言有两种储存数值的方式:[变量和常量]
变量是一个数据储存位置,其值在程序执行期间会发生变化,而常量的值固定不变。
字母x=1字节
数字500=2字节
数字241.105=4字节
短语sams teach youself C=21字节
一张打印页面=大约3000字节
变量:是计算机内存中一个已命名的数据存储位置。在程序中使用变量名,实际上是引用储存在该位置的数据。
变量名:要在C程序中使用变量,首先知道如何创建变量名。
在C语言中,变量名必须遵循以下规则。
变量名可以包含字母(从a~z, 从A~Z)、数字(0~9) 和下划线( _ )。
变量名的第1个字符必须是字母。
下划线作为第1个字符是合法的,但不推荐这样做。
变量名的第1个字符不能是数字(0~9) 。
注意字母的大小写。C语言区分大小写,因此,变量名count和Count是不同的变量名。
其次C语言的关键字不能用作变量名。因为关键字是C语言的一部分。
通常写C语言时变量名只用小写字母,常量名用大写字母。且变量名字符不超过31个。
如下表举例说明:
变量名 |
合法性 |
percent |
合法 |
y2x5__fg7h |
合法 |
annual_profit |
合法 |
_1990_tax |
合法,但不推荐 |
savings#account |
不合法,包含非法字符# |
double |
不合法,这是C语言的关键字 |
4sale |
不合法,第一个字符是数字 |
C语言的数值变量包括以下两大类:
整型变量储存没有小数部分的值(即,只储存整数)。
整型变量分为两类:
1.有符号整型变量可以储存正值或负值
2.而无符号整型变量只能储存正值和0。
浮点型变量储存带有小数部分的值(即,实数)。
如下表所示:
既然int和long 类型完全一样,为何还要把它们归为两个不同的类型?
在64位英特尔系统下,int和long类型的大小完全相同。但是,在其他系统中它们可能不同。
并且C是灵活且可移植的语言,因此为这两种类型提供了不同的关键字。在英特尔系统下,int和long可以互换。
变量类型 |
关键字 |
所需内存(字节) |
取值范围 |
字符 |
char |
1 |
-128~127 |
短整型 |
shaort |
2 |
-32768~32767 |
整型 |
int |
4 |
-2147483648~2147438647 |
长整型 |
long |
4 |
-2147483648~2147438647 |
长长整型 |
long long |
8 |
-923372036854775808~923372036854775807 |
无符号字符 |
unsigned char |
1 |
0~255 |
无符号短整型 |
unsigned short |
2 |
0~65535 |
无符号整型 |
unsigned int |
4 |
0~4294967295 |
无符号长整型 |
unsigned long |
4 |
0~4294967295 |
无符号长长整型 |
unsigned long long |
8 |
0~18446744073709551615 |
单精度浮点型 |
float |
4 |
1.2E-38~3.4E38 |
双精度浮点型 |
double |
8 |
2.2E-308~1.8E308 |
二.语句:
语句:是一条完整的指令,命令计算机执行某些任务。
在C语言中,虽然语句可以跨越多行,但通常将语句写成一行。
C语言的语句大多数以分号结尾。
如:
x = 2 + 3
是一条赋值表达式语句。它命令计算机将2与3相加后的结果赋给x变量。
2.1在语句中留白:
空白指的是源代码中的空格、水平制表符、垂直制表符和空行。
C编译器会忽略所有的空白。当编译器阅读源代码中的语句时,它查找语句中的字符和末尾的分号,但是忽略空白。
所以:
x=2+3
与下面等价:
x = 2 + 3
也与下面等价:
x =
2
+
3 ;
虽然以上都可行,但是尽量放在一行。
C编译器忽略空白这条规则有一个例外:
就是不忽略字面字符串常量中的制表符和空白(它们被视为字符串的一部分)。
字符串就是一系列字符。放在双引号中的字符串就是字面字符串常量,编译器会逐字符地解译它。
例1:这是一个字面字符串常量
"How now brown cow"
下面这个字符串则与上面不同:
"How now brown cow"
两者的区别在于,各单词之间的空格数量不同。C编译器会记录字面字符串常量中的空白。
例2:这个例子虽然合法但不建议
printf(
"Hello,world!"
);
但是如果是按照下面写则就不对
printf("Hello,
world!");
要将字面字符串常量分成多行,必须在分隔处使用斜杠字符(\)。因此,下面这样写才是合法的:
printf("Hello,\
world!");
2.2创建空语句:
如果让分号单独占一行,就创建了空语句。空语句不执行任何行为。
2.3复合语句:
复合语句也称为块,是放在花括号{}中的一组(一条或多条) C语句。如下所示便是一个块:
{
printf("Hello,");
printf("world!");
}
在C语言中,只要是可以使用单条语句的地方都可以使用块。注意,花括号对可以放在不同的位置。
下面的示例与上面的示例等价:
{ printf("Hello, ");
printf("world!"); }
总结:在语句中使用空白的方式要始终一致让花括号各占一行,提高代码的可读性。对齐花括号, 更方便查找块的(/ )连接另一行。开始和结束。如无必要,不要让一行语句跨越多行。尽可能保持一条语句占一行。如果将字符串写成多行,不要忘记在行尾用斜杠(\)连接另一行。
三.理解表达式:
在C语言中,一切可求值的内容都是表达式。C语言有各种不同复杂程度的表达式。
3.1简单表达式:
表达式 |
描述 |
PI |
符号常量(已在程序中定义) |
20 |
字面常量 |
rate |
变量 |
-1.25 |
字面常量 |
对字面常量求值得到它本身的值。
对符号常量求值得到用#define指令创建该常量时为其指定的值。
对变量求值得到程序赋给它的当前值。
3.2复杂表达式:
复杂表达式由更简单的表达式和连接这些表达式的运算符组成。
例如:
2 + 8
是一个由3个子表达式2、8和+ (加法运算符) 组成的表达式。对表达式2 + 8求值为10。
C表达式可以更复杂如下:
1.25 / 8 + 5 * rate + rate * rate / cost
当表达式中包含多个运算符时,对整个表达式求值的结果取决于运算符优先级。
下面的则是赋值表达式语句:
x = a + 10;
该语句对表达式a + 10 求值,并将计算结果赋给x。
另外,整条语句x=a+10也是一个表达式,求值的结果就是赋值运算符左侧变量的值。
因此,可以像下面这样写语句,将表达式a + 10的值赋给两个变量x和y :
y = x = a+10
还可以这样写:
x = 6 + (y = 4 + 5)
该语句的结果是y的值为9,x的值为15。注意,为了顺利通过编译,必须在该语句中添加圆括号。
注意:不应该将赋值表达式语句嵌套在其他表达式中。
四.数学运算符:
运算符是命令C编译器对一个或多个运算对象执行某些操作或行为的符号。
运算对象是运算符执行的项。在C语言中,所有的运算对象都是表达式。
C语言的运算符分为4大类:
赋值运算符;
数学运算符;
关系运算符;
逻辑运算符。
4.1赋值运算符:
赋值运算符是一个等号(=)。在程序设计中,它的用法与数学中的用法不同。
如果写出:
x = y;
在C程序中,该语句的意思是“将y的值赋给x”,而不是“x等于y”。
在赋值表达式语句中,赋值运算符的右侧可以是任意表达式,而左侧必须是一个变量名。
因此,赋值的格式是:
变量=表达式;
执行该语句时,将对表达式求值,并将结果赋值给变量。
4.2数学运算符:
C语言通过数学运算符执行数学运算(如加法、减法)。
C语言有两个一元数学运算符和五个二元数学运算符。
一元数学运算符
之所以称为一元数学运算符,是因为这些运算符只需要一个运算对象。C语言有两个一元数学运算符。
运算符 |
符号 |
操作 |
示例 |
递增 |
++ |
为运算对象递增1 |
++x, x++ |
递减 |
- - |
为运算对象递减1 |
--x,x-- |
递增运算符和递减运算符只能用于变量,不可用于常量。
一元数学运算符为运算对象执行加1或减1的操作。换言之,语句:
++x;
--y;
分别和下面等价:
x = x+1
y = y - 1
如上面表所示,一元数学运算符可置于运算对象的前面(前缀模式)或后面(后缀模式)。
这两种模式并不等价。它们在执行递增或递减操作时有区别。
使用前缀模式时,先递增或递减运算对象,再对表达式求值。
使用后缀模式时,先对表达式求值,再递增或递减运算对象。
请看以下两条语句:
x = 10;
y = x++;
执行完上述语句后,x的值是11,y的值是10。先将x的值赋给y,再递增x。与此相反,执行完下面两条语句后,x和y的值都是11。先递增x,然后再将x的值赋给y :
记住,=是赋值运算符,该语句是赋值表达式语句,不是数学中的等式。作为类比,可以将=视为“拷贝”运算符。语句y= x;的意思是将x的值拷贝给y。完成拷贝后,改变x的值不会影响y。
以下程序中演示了前缀和后缀模式的区别。
输入:
/* 该程序用于解释一元运算符前缀模式和后缀模式的不同 */
#include <stdio.h>
int a, b;
int main(void)
{
//将a和b设置为0
a = b = 0;
// 首先是递增运算符,然后是递减运算符。
// a使用一元运算符的后缀模式,b使用一元运算符的前缀模式。
// 打印的a值是a递增或递减1之前的值,打印的值是递增或递减1后的值。
printf("Count up!\n");
printf("Post Pre\n");
printf("%d %d\n", a++, ++b);
printf("%d %d\n", a++, ++b);
printf("%d %d\n", a++, ++b);
printf("%d %d\n", a++, ++b);
printf("%d %d\n", a++, ++b);
printf("nCurrent values of a and b:\n");
printf("%d %d\n\n", a, b);
printf("Count down!\n");
printf("Post Pre");
printf("\n%d %d", a--, --b);
printf("\n%d %d", a--, --b);
printf("\n%d %d", a--, --b);
printf("\n%d %d", a--, --b);
printf("\n%d %d\n", a--, --b);
return 0;
}
输出结果:
解析:
该程序在第5行声明了两个变量a和b
第10行将两个变量都设置为0。
第1部分的printf()语句(第18-22行)使用一元递增运算符计数至5。
对于a,每一行在打印a的值之后再递增a ;
而对于b则是在打印之前先递增b的值。
完成所有递增操作后,第25行的printf语句显示两个变量的值均为5。
第2部分的printf()语句(第29-33行),a与b均递减1。
同样,
对于a,在打印a的值之后再递减a ;
对于b,则是在打印之前先递减b的值。
二元数学运算符:
二元运算符有两个运算对象。下列表列出了C语言中一些常用的二元数学运算符。
运算符 |
符号 |
操作 |
实例 |
加法 |
+ |
将两个运算对象相加 |
x + y |
减法 |
- |
将第一个运算对象减去第二个运算对象 |
x - y |
乘法 |
* |
将两个运算对象相乘 |
x * y |
除法 |
/ |
将第一个运算对象除以第二个运算对象 |
x / y |
求模 |
% |
得到第一个运算对象除以第二个运算对象后的余数 |
x % y |
前4个运算符应该很熟悉,平时经常会用到它们。但是第5个运算符可能是第1次见。
%号是求模运算符,返回第1个运算对象除以第2个运算对象的余数。
例如:11求模4得3 (因为11除以4两次后余3)。
下面还有其他的例子:
100 求模 9 得 1
10 求模 5 得 0
40 求模 6 得 4
下面程序演示了如何使用求模运算符将总秒数转换为小时、分钟、秒。
输入:
/* 举例说明求模运算符 */
/* 输入一个总秒数 */
/* 并将其转换为小时、分、秒 */
#include<stdio.h>
/* 定义常量 */
#define SECS_PER_MIN 60
#define SECS_PER_HOUR 3600
unsigned seconds, minutes, hours, secs_left, mins_left;
int main(void)
{
/* 输入总秒数 */
printf("Enter number of seconds (< 65000): ");
scanf("%d", &seconds);
hours = seconds / SECS_PER_HOUR;
minutes = seconds / SECS_PER_MIN;
mins_left = minutes % SECS_PER_MIN;
secs_left = seconds % SECS_PER_MIN;
printf("%u seconds is equal to ", seconds);
printf("%u h, %u m, and %u s\n", hours, mins_left, secs_left);
return 0;
}
输出1:
输出2:
解释:
本次程序所用的格式与前面程序的格式一致。
第1-3行是一些注释,说明该程序的用途。
第4行是空行,提高程序的可读性。编译器不仅忽略语句和表达式中的空格,还忽略空行。
第5行包含该程序需要使用的头文件。为提高程序的可读性。
第9行和第10行定义了两个符号常量SECS_PER_MIN和SECS_PER_HORE。
第12行声明该程序中用到的所有变量。有些程序员喜欢每一行声明一个变量,而不是将它们都放在一行。这只是风格的问题,两种写法都正确。
第14行是main()函数,该函数是程序的主体。程序必须先获得一个值,才能把秒转换为小时和分。
因此,第18行通过printf()函数在屏幕上提示用户输入数据,
接着第19行使用scanf()函数获取用户输入的数据。
然后,scanf() 语句把用户输入的总秒数储存在seconds中。
第21行的表达式通过总秒数(seconds )除以符号常量,SECS_PER_HOUR得到总小时数。
因为hours是整型变量,SECS_PER_HOUR是整型符号常量,所以计算结果仍为整数(两数相除的余数被忽略)。
第22行采用相同的计算逻辑,通过用户输入的总秒数(seconds )除以符号常量SECS_PER_MIN得到总分钟数。
因为第22行计算出的总分钟数中包含小时数,
所以第23行使用求模运算符去掉小时数,并储存余下的分钟数。
第24行也采用相同的计算逻辑得出剩余的秒数。
第26行和第27行获得表达式计算后的值,并将它们显示在屏幕上。
第29行在程序退出前,向操作系统返回0,结束该程。
4.3运算符优先级和圆括号:
在一个包含多个运算符的表达式中,如何确定操作的执行顺序?
我们用下面的赋值表达式语句来说明这个问题的重要性:
x = 4 + 5 * 3;
如果先执行加法将得到以下结果,x被赋值为27
x = 9 * 3;
如果先执行乘法将得到以下结果,x被赋值为19
x = 4 + 15;
很显然,必须指定某些规则来执行操作的顺序才行。这种顺序称为运算符优先级
C语言对此有严格的说明。每个运算符都有特定的优先级。编译器对表达式求值时,会首先执行优先级最高的运算符。
下列表列出了C语言的数学运算符优先级。相对优先级一栏中的数字1代表最高优先级,因此会首先执行该操作。
运算符 |
相对优先级 |
++ -- |
1 |
* / % |
2 |
+ - |
3 |
如表所示,在C表达式中按照以下顺序执行操作:
一元递增和递减
乘法、除法和求模
加法和减法。
如果表达式中包含多个相同优先级的运算符,通常根据运算符在表达式中的出现顺序,从左至右执行。
例如,在下面的表达式中,%号和*的优先级相同,但%是最左边的运算符,因此先执行求模:
12 % 5 * 2
该表达式计算得4 (12%5得2,2 乘以2得4)。
返回之前介绍的例子:
语句x= 4 + 5*3;将19赋值给x因为先计算乘法后计算加法。
如果表达式无法按照你预想的优先级执行,怎么办?
我们仍使用之前的例子来说明:
如果想先计算4 + 5,后计算4 + 5的和与3的乘积,应该怎么做?
在C语言中,可以使用圆括号来改变计算顺序。无论运算符本身优先级怎样,都优先计算圆括号中的子表达式。
因此,可以这样写:
x = (4 + 5) * 3;
因为首先计算圆括号中的表达式4 + 5,所以赋给x的值为27
可以在表达式中使用多个圆括号,而且可以嵌套。出现套嵌圆括号时,先计算最里面圆括号中的表达式,再依次计算至最外面。
请看下面稍微复杂的表达式:
x = 25 - (2 * (10 + (8 / 2)));
该表达式的计算顺序如下。
首先计算最里面的表达式8/2,得4:
x = 25 - (2 * (10 + 4))
移至外层,下一个表达式10 + 4,计算得14 :
x = 25 - (2 * 14)
计算最外面的表达式2* 14,得28 :
x = 25 - 28
最后计算表达式25 - 28,然后将计算结果(-3 )赋值给x变量:
x = -3
并非只有在改变表达式的计算顺序时才使用圆括号,为了让某些表达式更加清晰,也可以在其中添加圆括号(即使它们并未改变运算顺序)。圆括号必须成对使用,否则编译器会生成错误消息。
4.4子表达式的计算顺序:
前面内容提到过,如果C表达式中包含多个优先级相同的运算符,将从左至右依次计算它们。
例如,表达式:
w * x / y * z
首先计算w乘以x,然后将乘积除以y,再将除法的结果乘以z
然而,如果表达式中还有其他优先级的运算符,就无法保证一定按从左至右的顺序执行操作。
请看以下表达式:
w * x / y + z / y
根据运算符优先级,先执行乘法和除法,再执行加法。
但是,C语言并未规定是先计算子表达式w*x / y,还是先计算z /y。
可能不清楚为什么要考虑这些。请看另一个例子:
w * x / ++y + z / y
如果先计算左边的子表达式(w*x / ++y),那么在计算右边子表达式之前y将递增1。
如果先计算右边的子表达式(z / y),y则不会提前递增。
因此,要避免在程序中写出这类不确定的表达式。
4.5关系运算符:
C语言的关系运算符用于比较表达式,提出诸如“x是否大于100?”或“y是否等于0 ?”的问题。
含有关系运算符的表达式,计算结果为真(1)或为假(0)。
下列表a中列出了C语言的6种关系运算符。
表b列出了如何使用关系运算符的示例。虽然这些示例都使用字面常量,但其原理也适用于变
表a:
运算符 |
符号 |
提出的问题 |
示例 |
等于 |
== |
运算对象1是否等于运算对象2? |
x == y |
大于 |
> |
运算对象1是否大于运算对象2? |
x > y |
小于 |
< |
运算对象1是否小于运算对象2? |
x < y |
大于或等于 |
>= |
运算对象1是否大于或等于运算对象2? |
x >= y |
小于或等于 |
<= |
运算对象1是否小于或等于运算对象2? |
x <= y |
不等于 |
!= |
运算对象1是否不等于运算对象2? |
x != y |
表b:
表达式 |
含义 |
计算结果 |
5 == 1 |
5是否等于1? |
0 (假) |
5 > 1 |
5是否大于1? |
1 (真) |
5 != 1 |
5是否不等于1? |
1 (真) |
(5 + 10) == (3 * 5) |
(5 + 10) 是否等于(3 * 5)? |
1 (真) |
五.if语句:
关系运算符主要用在if语句和while语句中构建关系表达式。
下面介绍if语句的基本知识,学习如何使用关系运算符构建程序控制语句。
什么是程序控制语句?
通常,C程序会按照语句在源代码文件中出现的顺序从上至下来执行。
程序控制语句用于改变语句的执行顺序,它可以让程序的其他语句执行多次,或完全不执行(根据不同情况而异)。
if语句是C语言的程序控制语句之 一,除此之外还有while和do...while语句。
if语句的基本格式是,对表达式求值并根据求值结果命令程序执行特定内容。
if语句的格式如下:
if (表达式)
{
语句
}
如果对表达式计算为真,就执行语句。
如果对表达式计算为假,则不执行语句。
无论哪种情况,都将执行if语句后的代码。可以认为,是否执行语句取决于表达式的结果。
注意,if (表达式)和语句不是独立的语句,它们一起组成了完整的if语句。
通过使用复合语句或块,if语句可以控制多条语句的执行。
前面介绍过块是一组(一条或多条)用花括号括起来的语句。只要可以使用单条语句的地方,就可以使用块。
因此,可以这样写if语句:
if (表达式)
{
语句1
语句2
/* 在这里添加代码 */
语句n
}
注意:不要在if语句的表达式末尾加分号。
在下面的示例中,由于if行末尾有分号,无论x是否等于2,都会执行语句1执行。
分号导致每行都被当作单独的语句,而非一起被视为一条语句:
if (x == 2); /* 不要使用分号 */
语句1
对于这样的错误,编译器一般也不会显示出来错误在哪里!
在编程时,if语句常与关系表达式一起使用。
换言之,“仅当条件为真时,才执行后面的语句”。如下所示:
if (x > y)
y = x;
仅当x大于y时,才将x赋值给y。如果x不大于y,则不会执行赋值操作。
下面,通过以下程序来演示if语句的用法。
输入:
// 该程序用于说明if语句和一些c语言关系运算符的用法
#define CURRENTYEAR 2013
#include<stdio.h>
int birth_year, age;
int main(void)
{
printf("Enter the year you were born: ");
scanf("%d", &birth_year);
// 两个if语句,判断用户是否在闰年出生
if (birth_year % 4 == 0)
printf("You were born in a leap year!\n");
if (birth_year % 4 != 0)
printf("You were not born in a.leap year!\n");
age = CURRENTYEAR - birth_year;
// 判断用户是否到达投票年龄和法定饮酒年龄
if (age >= 18)
printf("You can vote this year!\n");
if (age <= 21)
printf("It is illegal for you to drink alcohol!\n");;
return (0);
}
输出1:
输出2:
输出3:
解析:
程序包含4个if语句(第15~17行)。
第3行通过#define指令创建了一个符号常量CURRENTYEAR。
第4行是stdio.h头文件,程序中使用了printf函数和scanf()函数,必须包含该头文件。
第6行定义了if语句要用到的两个整形变量。
前两个if语句用于判断用户是否出生在闰年。
求模运算符(号)常用于处理类似的情况。
闰年一定能被4整除,因此将用户输入的年份求模4,如果余数为0
(记住,在测试两者是否相等时要使用==而不是= )则意味着该年是闰年。
第2个if语句使用不等于运算符(!=)包含了其他不是闰年的年份。
这样设置比单独判断求模的余数等于1、2或3要更效率。
如果需要包含除一种情况以外的所有情况,使用不等于运算符是不错的处理方案。
该程序的输出示例显示,输入的大部分出生年份都能满足这些要求
(闰年出生、达到选举年龄和法定饮酒年龄)之一,只有小部分能满足全部要求。
注意:上面的程序中,if语句中的语句采用了缩进格式。这是一种常用的做法,可以提高代码的可读性。
5.1 else子句:
if语句可以选择包含else子句。方法如下:
if (表达式)
语句1;
else
语句2;
如果表达式计算结果为真,则执行语句1。
如果表达式计算结果为假,控制将转到else语句,执行语句2。
语句1和语句2都可以是复合语句或块。
例如下列程序中用带else子句的if语句重写了上面的程序。
// 该程序用于演示带else子句的if语句和一些c语言关系运算符的用法
#define CURRENTYEAR 2013
#include<stdio.h>
int birth_year, age;
int main(void)
{
printf("Enter the year you were born: ");
scanf("%d", &birth_year);
// 判断用户是否在闰年出生
if (birth_year % 4 == 0)
printf("You were born in a leap year!\n");
else
printf("You were not born in a.leap year!\n");
age = CURRENTYEAR - birth_year;
// 判断用户是否到达投票年龄和法定饮酒年龄
if (age >= 18)
printf("You can vote this year!\n");
if (age <= 21)
printf("It is illegal for you to drink alcohol!\n");;
return (0);
}
输出1:
输出2:
输出3:
解析:
第15~18行与上一个程序清单稍有不同。
第15行仍然是检查birth_year是否能被4整除,如果能被整除则是闰年。
与上一个程序中使用另一个if语句包含不能被4整除的年份(即,不是闰年的年份)
该程序在第17行使用else子句包含了其他所有情况。
第2组if语句不是二选一的情况,因此不适合使用else子句,
除非你想在程序中添加“你尚未达到选举年龄”和“你已经达到法定饮酒年龄”的句子。
语法:
if语句的格式:
格式1:
if (表达式)
{
语句
}
下一条语句
这是最简单的if语句格式。如果表达式为真,便执行语句;如果表达为假,就忽略语句。
格式2:
if (表达式)
{
语句1
}
else
{
语句2
}
下一条语句
这是普通的if语句格式。如果表达式为真,便执行语句1,否则执行语句2。
格式3:
if (表达式1)
语句1
else if (表达式2)
语句2
else
语句3
下一条语句
这是嵌套的if语句。
如果表达式1为真,程序在继续运行下一条语句之前,会先执行语句1 ;
如果表达式1为假,则会判断表达式2。
如果表达式1为假且表达式2式为真,则执行语句2。
如果表达式1和表达式2都为假,则执行语句3。这3条语句中只有一条语句被执行。
示例1:
if ( salary > 450000 )
{
tax = .30;
}
else
{
tax = .25;
}
示例2:
if ( age < 18 )
printf("Minor");
else if ( age < 65 )
printf("Adult");
else
printf("Senior Citizen");
六.对关系表达式求值:
对关系表达式求值的结果,要么为真(1),要么为假(0)。
虽然关系表达式常用于if语句和其他条件结构中,但是它们也可作为一般数值使用。
如下列程序所示:
输入:
/* 该程序用于说明关系表达式的计算 */
#include <stdio.h>
int a;
int main()
{
a = (5 == 5); /* 对关系表达式的求值为1 */
printf("\na = (5 == 5)\na = %d", a);
a = (5 == 5); /*对关系表达式的求值为0 */
printf("\na = (5 != 5)\na = %d", a);
a = (12 == 12) + (5 != 1); /* 赋值运算符右边为1 + 1 */
printf("\na = (12 == 12) + (5 != 1)\na = %d\n", a);
return 0;
}
输出:
解析:
该程序的输出看上去让人有些困惑。
注意,使用关系运算符时最常犯的错误是,用单等号(赋值运算符)代替双等号。
下面表达式的结果是5 (当然, 也将5赋值给x ) :
x = 5
而下面表达式的结果不是0,就是1 (这取决于x 是否等于5 .),并不会改变x中的值:
x == 5
如果不小心将关系表达式写成:
if ( x= 5)
printf("x is equal to 5");
就一定会打印这条消息,因为无论x的值是多少,对if语句的关系表达式求值的结果都恒为真。
查看程序,理解为何a的值会不同。
第9行,5确实等于5,因此将关系表达式结果(1 )赋给a。
第12行,“5 !=5”(5不等于5 )为假,因此将0赋给a。
再次重申,关系运算符用于创建关系表达式,询问表达式之间的关系。
关系表达式返回的结果是一个数值,要么是1 (表示结 果为真),要么是0 (表示结果 为假)。
6.1关系运算符的优先级:
与前面讨论的数学运算符类似,关系运算符也有优先级,在含有多个关系运算符的表达式中,通过优先级判断它们的执行顺序。
另外,在使用关系运算符的表达式中,同样也可以使用圆括号改变操作的执行顺序。
首先,所有的关系运算符都比数学运算符的优先级低。
因此,如果要表达将x加2的和与y作比较,可以这样写:
if (x + 2 > y)
也可以这样写(圆括号提高了代码的可读性) :
if ((x + 2) > y)
这种情况下,虽然C编译器不要求加圆括号
但是(x + 2)周围的圆括号能非常清晰地表达出:x加2的和与y作比较。
其次关系运算符还有两个优先级。
运算符 |
相对优先级 |
> >= < <= |
1 |
!= == |
2 |
因此如果这样写:
x == y > z
就相当于:
x == (y > z)
C编译器会先计算表达式y > z (其结果要么是0 ,要么是1)
然后判断x是否与上一步的计算结果(1或0)相等。
注意:不要在if语句的关系表达式中使用赋值表达式语句。这会让他人不易读懂你的代码。
他们也许认为你写错了,并将赋值表达式语句改成逻辑相等语句。
不要在包含else的if语句中使用不等于运算符(!= )。
使用等于运算符(== )会更好。
例如,下面的代码:
if (x != 5)
语句1
else
语句2
可以写成:
if (x == 5)
语句1
else
语句2
七.逻辑运算符:
有时,你可能需要一次询问多个关系问题。
例如,“ 如果是工作日的早上7点,且不是假期,就响铃”。
C语句的逻辑运算符可以把两个或多个关系表达式组合成一个单独的表达式,该表达式的计算结果不是真就是假。
下表列出了C语言的3种逻辑运算符。
运算符 |
符号 |
示例 |
与 |
&& |
exp1 && exp2 |
或 |
|| |
exp1 || exp2 |
非 |
! |
!exp1 |
下面的表解释了这些逻辑运算符的用法。
表达式 |
计算结果 |
exp1 && exp2 |
仅当 exp1和exp2都为真时表达式为真(1) ;否则,表达式为假(0) |
exp1 || exp2 |
如果exp1或exp2为真,表达式为真(1); 如果两者均为假,则表达式为假(0) |
!exp1 |
如果exp1为真,表达式为假(0) ; 如果exp1为假,则表达式为真(1) |
如果表达式中使用了逻辑运算符
那么该表达式的计算结果(为真或假)取决于其运算对象(即,关系表达式)的计算结果(为真或为假)。
下列表中列出了一些代码示例:
表达式 |
计算结果 |
(5 ==5 ) && (6 != 2) |
真(1),因为两边的运算对象都为真 |
(5 > 1) || (6 < 1) |
真(1),因为一个运算对象为真 |
(2 == 1) && (5 == 5) |
假(0),因为一个运算对象为假 |
! (5 == 4) |
假(0),因为运算对象为假 |
可以创建包含多个逻辑运算符的表达式。
例如,要询问“x是等于2、3还是4 ?”,可以这样写:
(x == 2) || (x == 3) || (x == 4)
灵活使用逻辑运算符能将同一个问题以不同的方式表达出来。
如果x是一个整型变量,上面的问题还可以这样写:
(x > 1) && (x < 5)
或者:
(x >= 2) && (x <= 4)
八.详议真/假值:
前面已经学过,关系表达式的结果为0表示假,为1表示真。
更重要的是,要意识到任何数值都能解译为真或假。
在C语言的表达式或语句中使用它们时,注意下面的规则:
●0表示假;
●非0表示真。
下面举例说明,该示例打印x的值:
x = 125;
if (x)
printf ("%d", x);
因为x是非0值,if语句解译表达式(x )为真。
可以更普遍地应用这一规则
对于任意C表达式,(expression)都相当于(expression != 0)。
如果expression 的结果为非0,则这两个表达式的结果都为真;
如果expression 的结果为0,则这两个表达式的结果都为假。
使用!运算符,也可以这样写:
(!expression)
与下面代码也等价:
(expression == 0)
8.1运算符的优先级:
C语言的逻辑运算符也有优先级顺序,无论是逻辑运算符之间,还是相对于其他运算符。
!运算符与一元数学运算符++和--的优先级相同。
因此,!运算符比所有的关系运算符和二元数学运算符的优先级高。
相比之下,虽然&||&的优先级比高,但是 && 和 || 运算符的优先级都较低(比所有数学运算符和关系运算符的优先级低)。
和C语言的其他运算符一样,也可以使用圆括号来改变逻辑运算符的计算顺序。
例如:考虑下面的示例:
你要写一个逻辑表达式完成3个独立的比较。
1. a是否小于b ?
2. a是否小于c ?
3. c是否小于d ?
你希望如果条件3为真,且条件1或条件2其中之一为真,则整个逻辑表达式为真。
则可以这样写:
a < b || a < c && c < d
然而,编译器不会按照你预想的顺序执行。因为&&运算符的优先级高于||运算符, 因此,上面的表达式相当于:
a < b || (a < c && c < d)
而且,如果(a<b)为真,那么不管(a<c)和(c<d)的关系是否为真,整个表达式的结果都为真。
因此,应该这样写:
(a < b || a < c) && c < d
强制编译器先计算||再计算&&。
为了更好地理解下列程序用上述两种方式计算表达式。
输入:
#include <stdio.h>
/*初始化变量。注意,c不小于d */
/* 做这样的设置,仅为测试需要 */
/* 因此,整个表达式结果应该为假 */
int a = 5, b = 6, c = 5, d = 1;
int x;
int main(void)
{
/* 不用圆括号,计算表达式的结果 */
x = a < b || a < c && c < d;
printf("\nWithout parenthese the expression evaluates as %d", x);
/* 加上圆括号,计算表达式的结果 */
x = (a < b || a < c) && c < d;
printf("\nWhith parentheses the expression evaluates as %d\n", x);
return 0;
}
输出:
解析:
注意程序打印的两个表达式的结果不同。
该程序初始化了4个用于比较的变量(第7行)。
第8行声明x,用于储存结果。
第14行和第19行使用了逻辑运算符。
其中,第14行不使用圆括号,根据运算符优先级,编译器会计算&&。
在这种情况下,计算的结果与预期不符;
第19行使用圆括号改变了表达式的计算顺序。
8.2复合赋值运算符:
C语言的复合赋值运算符将二元数学操作和赋值操作结合起来。
例如,假设你希望让x的值增加5,换言之,将x与5相加,并把结果赋值给x。可以这样写:
x = x + 5
使用复合赋值运算符,可以这样写:
x += 5
复合赋值运算符的通用语法如下:
exp1
op= exp2
;
这与下面的写法等效(op指的是二元运算符) :
exp1
= exp1
op exp2
;
可以使用前面讨论过的5个二元数学运算符创建其他复合赋值运算符。
下列表列出一些例子:
这样写 |
相当于 |
x *= y |
x = x * y |
y -= z + 1 |
y = y - z + 1 |
a /= b |
a = a / b |
x += y / 8 |
x = x + y / 8 |
y % 3 |
y = y % 3 |
复合运算符提供方便的速写方式,当赋值运算符左侧的变量名特别长时,很能体现这种书写方式的优势。
与其他赋值表达式语句一样,复合赋值表达式语句的值也是赋给左侧变量的值。
因此,执行下面的语句,x和z的值都为14 :
x = 12;
z = x += 12;
8.3条件运算符:
条件运算符是C语言唯一的三元运算符,这意味着需要3个运算对象。条件运算符的语法是:
exp1
? exp2
: exp3
;
如果exp1为真(即,值为非0 ),整个表达式的结果为exp2的值。
如果exp1为假(即,值为0 ),整个表达式的结果为exp3的值。
例如:下面语句中
如果y为真,则把1赋值给x ;
如果y为假,则把100赋值给x :
x = y ? 1 : 100;
同样地,要把x和y中的较大者赋值给z,可以这样写:
z = (x > y) ? x : y;
也许你没有注意到,条件运算符的原理类似于if语句。
上面.这条语句也可写成:
if (x > y)
z = x;
else
z = y;
但是,条件运算符不能替换所有使用if...else的情况。
在可以替换的情况下,用条件运算符更为简洁。
而且,条件运算符还能用于无法使用if语句的地方。
例如,插入一个函数调用中(如,单独的printf()语句) :
printf( "The larger value is %d", ((x > y) ? x : y) );
8.4逗号运算符:
在C语言中,逗号常作为简单的标点符号,用于分隔变量声明、函数参数等。
在某些情况下,逗号还可以作为运算符,将两个子表达式组成一个表达式。
规则如下:
●两个表达式都会被计算,先计算逗号左边的表达式;
●整个表达式的结果是右边表达式的值。
例如,下面的语句会将b的值赋给x,然后递增a,接着再递增b:
x = (a++, b++);
因为上面语句中的++运算符都是后缀模式,所以在递增b之前,已经将b的值赋给x。
而且,必须使用圆括号,因为逗号运算符的优先级比赋值运算符的优先级还低。
后面会介绍逗号运算符常用于for语句中。
九.运算符优先级归纳:
下列表按优先级降序列出了C语言的运算符,方便查看优先级,以备查阅。
注意,同行运算符的优先级相同。
优先级 |
运算符 |
1 |
( ) [] -> |
2 |
! ~ ++ * (间接运算符) & (取址运算符) sizeof + (一元) - (一元) |
3 |
* (乘法) / % |
4 |
+ - |
5 |
>> << |
6 |
< <= > >= |
7 |
== != |
8 |
& (换位与) |
9 |
^ |
10 |
| |
11 |
&& |
12 |
|| |
13 |
?: |
14 |
= += -= *= /= &= ^= |= <<= >>= |
15 |
, |
( )是函数运算符
[ ] 是数组运算符
十.以上全部总结:
本次介绍了语句、表达式和运算符相关的内容。
C编译器会忽略代码中的空白(除字符串常量中的空白外)。
大部分语句以分号结尾。
复合语句(或块)是由花括号括起来的多条语句,可用于任何单条语句使用的地方。
许多语句都由表达式和运算符组成。
表达式:
一切可求值(结果为数值)的内容都是表达式。
复杂表达式可包含多个简单的表达式(子表达式)。
运算符是C语言中的符号,命令计算机对一个或多个表达式执行操作。
运算符:
在C语言中,许多运算符都是一元运算符,需要一个运算对象;
另外大部分是二元运算符,需要两个运算对象;
只有一个三元运算符"一一"条件运算符。
C语言定义了运算符的优先级别,规定了在包含多个运算符的表达式中执行操作的顺序。
本次介绍的C运算符分为3大类。
①数学运算符:对运算对象执行算术运算(如,加法)。
②关系运算符:对运算对象进行比较(如,大于)。
③逻辑运算符:对真/假表达式进行求值。记住,C语言使用1和0分别表示真和假,任何非0值都解译为真。
其次还介绍了C语言中的if语句,该语句根据关系表达式的求值结果执行相应的操作。
问答题:
1.在储存更大的数时,为何要使用int和float类型的变量而不是更大类型的变量(如,long int和double ) ?
long int 类型的变量所占用的RAM比int多。在小型程序中,这不是问题。但是,随着程序越来越大,程序员要考虑内存的使用效率。如果确定用户输入的数会超过int 或long的取值范围,就要做必要地调整。记住,即使能确定用户输入的数在变量大小的取值范围内,也无法保证用这些数进行数学运算(加法或乘法)后所得的结果一定在变量的取值范围内。
2.如果把一个小数赋值给整型变量会出现什么情况?
可以把小数赋值给int变量。如果该变量是一个变量,编译器可能会发出警告,待赋值数的小数部分会被截断。例如,如果将3.14赋值给一个整型变量pi ,那么pi的值是3。其小数部分.14将会被截断并丢弃。
3.如果将超出某类型取值范围的数放入该类型变量中,会出现什么情况?
许多编译器都允许这样做,不会发出任何警告或错误消息。编译器将该数字回绕((wrap)处理为合适的值(因此是错误的值)储存在变量中。例如,如果将32768赋值给2字节的有符号short类型变量(取值范围是-32768~32767 ),该变量实际上储存的值是-32768;如果将65535赋值给该变量,它实际储存的值是-1 。
4.如果将负值赋给无符号类型变量,会出现什么情况?
从上一个问题的回答可知,如果这样做,编译器可能不会发出任何警告或错误消息。就像给变量赋过大的值一样,编译器同样会回绕处理负值。例如,如果将-1赋给2字节长的unsigned int类型的变量,编译器会把unsigned int 类型最大的正值(65535 )储存在变量中。
5.用#define指令创建的符号常量和用const关键字创建
这两种方式创建的符号常量的区别涉及指针和变量作用域。指针和变量作用域是C程序设计中的两个重要部分
6.空格和空行对程序运行有何影响?
空白(空行、空格和制表符)可提高代码的可读性。 编译器在编译时程序时会忽略空白,因此不会影响可执行程序。
正是由于这个原因,你应该使用空白让程序易于阅读。
7.一元运算符和二元运算符的区别是什么?
顾名思义,一元运算符需要一个运算对象,二元运算符需要两个运算对象。
8. - 是一元运算符还是二元运算符?
既是一元运算符也是二元运算符。编译器可以根据表达式中变量的个数,判断你使用的是哪种形式的-运算符。
下列的语句中 - 是一元运算符:
x = -y;
而下列的语句中 - 则是二元运算符:
x = a - b;
9.负数被视为真还是假?
记住,0为假,其他非0 (包括负数) 都为真。
10.整型变量和浮点型变量有何区别?
整型变量可储存整数(没有小数部分的数字)﹔浮点型变量储存实数(有小数部分的数字)。
11.列出使用双精度浮点型( double类型)变量而不用单精度浮点型(float类型)变量的两个原因。
double 类型变量的值域比float类型变量大(可储存更大和更小的值)。另外,double类型变量的精度比float变量高。
12.对于变量大小,有哪5条规则一定是正确的?
char类型变量的大小是1字节。
short类型变量的大小不超过int类型变量。
int类型变量的大小不超过long类型变量。
unsigned类型变量的大小与int类型变量相同。
float 类型变量的大小不超过double类型变量。
13.与字面常量相比,使用符号常量的两个优点是什么?
符号常量名提高了代码的可读性。使用符号常量很方便修改其值。
14.定义符号常量MAXIMUM的值为100,有哪两种方法?
#define MAXIMUM 100
const int MAXIMUM = 100;
15.C语言允许变量名包含哪些字符?
字母、数字和下划线。
16.创建变量名和符号常量名时,必须遵循哪些规则?
变量名和符号常量名应描述待储存的数据。变量名应该为小写,符号常量名为大写。
17.符号常量和字面常量之间有何区别?
符号常量是表示字面常量的符号。
18.int 类型的变量能储存的最小值是多少?
如果unsigned int的大小是2字节,它能储存的最小值是0;如果是有符号整型,可储存的最小值是-32768 。
19.在C语言中下面是什么语句?含义是什么?
x = 5 + 8;
这是一条赋值表达式语句,他的命令是计算机将5和8相加,并将结果赋值给x变量。
20.什么是表达式?
对其结果的求值为数值,就是表达式
21.如果表达式中包含多个运算符,如何判断运算的执行顺序?
根据运算符的相对优先级
22.如果x变量的值是10,分别执行下面两个语句后,x和a的值是多少?
a = x++;
a = ++x;
执行第一个语句后,a的值是10,x的值是11,
执行第二个语句后,a和x第值都是11。(两条语句必须单独执行)
23.对表达式10%3求值是多少?
答:1
24.对表达式5 +3*8/2+2求值是多少?
答:19
25.请重写第24题,为表达式加上圆括号,使其值得16。
(5 + 3) * 8 / (2 + 2)
26.如果表达式求值为假,则其值是多少?
答:0
27.如下所列各项,哪一个优先级高?
a. == 或 < 右边大
b. *或 + 右边大
c. != 或 == 相同
d. >= 或 > 相同
28.什么是复合赋值运算符,用于什么情况?
复合赋值运算符把二元数学运算和赋值运算结合起来,提供了一种简洁的表示法。
本次介绍的复合赋值运算符有
+=、-=、/=、*=和%=。
操作题
1.下面代码格式不妥,请输入并编译,看是否能运行:
输入:
#include <stdio.h>
int x,y;int main() { printf(
"\nEnter two numbers");scanf(
"%d %d",&x,&y);printf(
"\n\n%d is bigger", (x>y)?x:y);return 0; }
输出:
解析:
虽然该程序的格式很糟糕,但是可以正常运行。
该程序的目的是演示空白不会影响程序的运行。
使用空白是为了提高程序的可读性。
用更好的方法排列一下:
#include <stdio.h>
int x,y;
int main(void)
{
printf("\nEnter two numbers");
scanf("%d %d",&x,&y);
printf("\n\n%d is bigger", (x>y)?x:y);
return 0;
}
2.排错下列程序使其正常运行:
#include <stdio.h>
int x = 1:
int main(void)
{
if (x = 1);
printf(" x equals 1");
otherwise
printf(" x does not equal 1");
return 0;
}
输出:
正确修改为:
#include <stdio.h>
int x = 1;
int main(void)
{
if (x == 1)
printf(" x equals 1");
else
printf(" x does not equal 1");
return 0;
}