初学者牢记:只要用户输入的内容和格式控制字符串匹配,就能够正确提取。在后续讲解格式化输入所演示的奇怪问题,几乎都是因为输入的内容和格式控制字符串不匹配
程序是人机交互的媒介,有输出必然也有输入,在 C语言系统化精讲(三):C语言变量和数据类型-上篇 一文中讲解了如何将数据输出到显示器上,本小节我们开始讲解如何从键盘输入数据。在C语言中,有多个函数可以从键盘获得用户输入:
scanf() 是最灵活、最复杂、最常用的输入函数,但它不能完全取代其他函数,大家都要有所了解。本小节我们只讲解 scanf(),其它的输入函数将在下面几个小节进行讲解。
scanf 是 scan format 的缩写,意思是格式化扫描,也就是从键盘获得用户输入,和 printf 的功能正好相反。举例:
#define _CRT_SECURE_NO_WARNINGS
#include
int main()
{
int a = 0, b = 0, c = 0, d = 0;
scanf("%d", &a); //输入整数并赋值给变量a
scanf("%d", &b); //输入整数并赋值给变量b
printf("a+b=%d\n", a + b); //计算a+b的值并输出
scanf("%d %d", &c, &d); //输入两个整数并分别赋值给c、d
printf("c*d=%d\n", c * d); //计算c*d的值并输出
return 0;
}
运行结果(↙表示按下回车键):
12↙
60↙
a+b=72
10 23↙
c*d=230
从键盘输入12,按下回车键,scanf() 就会读取输入数据并赋值给变量 a;本次输入结束,接着执行下一个 scanf() 函数,再从键盘输入 60,按下回车键,就会将 60 赋值给变量 b,都是同样的道理。第 9 行代码中,scanf() 有两个以空格分隔的 %d,后面还跟着两个变量,这要求我们一次性输入两个整数,并分别赋值给 c 和 d。注意 %d %d
之间是有空格的,所以输入数据时也要有空格。对于 scanf(),输入数据的格式要和控制字符串的格式保持一致。其实 scanf 和 printf 非常相似,只是功能相反罢了:
scanf("%d %d", &a, &b); // 获取用户输入的两个整数,分别赋值给变量 a 和 b
printf("%d %d", a, b); // 将变量 a 和 b 的值在显示器上输出
它们都有格式控制字符串,都有变量列表。不同的是,scanf 的变量前要带一个 & 符号。 &称为取地址符,也就是获取变量在内存中的地址。数据是以二进制的形式保存在内存中的,字节(Byte) 是最小的可操作单位。为了便于管理,我们给每个字节分配了一个编号,使用该字节时,只要知道编号就可以,就像每个学生都有学号,老师会随机抽取学号来让学生回答问题。字节的编号是有顺序的,从 0 开始,接下来是 1、2、3……下图是 4G 内存中每个字节的编号(以十六进制表示):
这个编号,就叫做 地址(Address)。 int a;
会在内存中分配四个字节的空间,我们将第一个字节的地址称为变量 a 的地址,也就是 &a 的值。对于前面讲到的整数、浮点数、字符,都要使用 & 获取它们的地址,scanf 会根据地址把读取到的数据写入内存。我们不妨将变量的地址输出看一下:
#include
int main()
{
int a = 'F';
int b = 12;
int c = 452;
printf("&a=%#x, &b=%#x, &c=%#x\n", &a, &b, &c);
return 0;
}
运行结果如下图所示:
注意: 这里看到的地址都是假的,是虚拟地址,并不等于数据在物理内存中的地址。虚拟地址是现代计算机因内存管理的需要才提出的概念,我们将在后续的文章中详细讲解。
再来看一个 scanf 的例子:
#define _CRT_SECURE_NO_WARNINGS
#include
int main()
{
int a, b, c;
scanf("%d %d", &a, &b);
printf("a+b=%d\n", a + b);
scanf("%d %d", &a, &b);
printf("a+b=%d\n", a + b);
scanf("%d, %d, %d", &a, &b, &c);
printf("a+b+c=%d\n", a + b + c);
scanf("%d is bigger than %d", &a, &b);
printf("a-b=%d\n", a - b);
return 0;
}
运行结果:
10 20↙
a+b=30
100 200↙
a+b=300
56,45,78↙
a+b+c=179
25 is bigger than 11↙
a-b=14
第一个 scanf() 的格式控制字符串为 %d %d
, 中间有一个空格,而我们却输入了10 20,中间有多个空格。第二个 scanf() 的格式控制字符串为 "%d %d"
, 中间有多个空格,而我们却输入了100 200,中间只有一个空格。这说明 scanf() 对输入数据之间的空格的处理比较宽松,并不要求空格数严格对应,多几个少几个无所谓,只要有空格就行。第三个 scanf() 的控制字符串为 "%d, %d, %d"
,中间以逗号分隔,所以输入的整数也要以逗号分隔。第四个 scanf() 要求整数之间以 is bigger than 分隔。用户每次按下回车键,程序就会认为完成了一次输入操作,scanf() 开始读取用户输入的内容,并根据格式控制字符串从中提取有效数据,只要用户输入的内容和格式控制字符串匹配,就能够正确提取。本质上讲,用户输入的内容都是字符串,scanf() 完成的是从字符串中提取有效数据的过程。
连续输入: 在本小节最开始的示例代码中,我们一个一个地输入变量 a、b、c、d 的值,每输入一个值就按一次回车键。现在我们改变输入方式,将四个变量的值一次性输入,如下所示:
12 60 10 23↙
a+b=72
c*d=230
可以发现,两个 scanf() 都能正确读取。合情合理的猜测是,第一个 scanf() 读取完毕后没有抛弃多余的值,而是将它们保存在了某个地方,下次接着使用。如果我们多输入一个整数,会怎样呢?
12 60 10 23 99↙
a+b=72
c*d=230
这次我们多输入了一个 99,发现 scanf() 仍然能够正确读取,只是 99 没用罢了。如果我们少输入一个整数,又会怎样呢?
12 60 10↙
a+b=72
23↙
c*d=230
输入三个整数后,前两个 scanf() 把前两个整数给读取了,剩下一个整数 10,而第三个 scanf() 要求输入两个整数,一个单独的 10 并不能满足要求,所以我们还得继续输入,凑够两个整数以后,第三个 scanf() 才能读取完毕。从本质上讲,我们从键盘输入的数据并没有直接交给 scanf(),而是放入了缓冲区中,直到我们按下回车键,scanf() 才到缓冲区中读取数据。如果缓冲区中的数据符合 scanf() 的要求,那么就读取结束;如果不符合要求,那么就继续等待用户输入,或者干脆读取失败。注意,如果缓冲区中的数据不符合 scanf() 的要求,要么继续等待用户输入,要么就干脆读取失败,上面我们演示了 继续等待用户输入
的情形,下面我们对代码稍作修改,演示 读取失败
的情形:
#define _CRT_SECURE_NO_WARNINGS
#include
int main()
{
int a = 1, b = 2, c = 3, d = 4; //修改处:给变量赋予不同的初始值
scanf("%d", &a);
scanf("%d", &b);
printf("a=%d, b=%d\n", a, b);
scanf("%d %d", &c, &d);
printf("c=%d, d=%d\n", c, d);
return 0;
}
运行结果:
12 60 a10↙
a=12, b=60
c=3, d=4
前两个整数被正确读取后,剩下了 a10,而第三个 scanf() 要求输入两个十进制的整数,a10 无论如何也不符合要求,所以只能读取失败。输出结果也证明了这一点,c 和 d 的值并没有被改变。这说明 scanf() 不会跳过不符合要求的数据,遇到不符合要求的数据会读取失败,而不是再继续等待用户输入。总而言之,正是由于缓冲区的存在,才使得我们能够多输入一些数据,或者一次性输入所有数据,这可以认为是缓冲区的一点优势。然而,缓冲区也带来了一定的负面影响,甚至会导致很奇怪的行为,请看下面的代码:
#define _CRT_SECURE_NO_WARNINGS
#include
int main()
{
int a = 1, b = 2;
scanf("a=%d", &a);
scanf("b=%d", &b);
printf("a=%d, b=%d\n", a, b);
return 0;
}
输入示例:
a=99↙
a=99, b=2
输入 a=99,按下回车键,程序竟然运行结束了,只有第一个 scanf() 成功读取了数据,第二个 scanf() 仿佛没有执行一样,根本没有给用户任何机会去输入数据。如果我们换一种输入方式呢?
a=99b=200↙
a=99, b=200
这样 a 和 b 都能够正确读取了。注意,a=99b=200 中间是没有任何空格的。肯定有好奇的小伙伴又问了,如果 a=99b=200 两个数据之间有空格又会怎么样呢?我们不妨亲试一下:
a=99 b=200↙
a=99, b=2
你看,第二个 scanf() 又读取失败了!在前面的例子中,输入的两份数据之前都是有空格的呀,为什么这里不能带空格呢,真是匪夷所思。好吧,这个其实还是跟缓冲区有关系,等后面大家有一定基础之后再进行深入讲解。要想破解 scanf() 输入的问题,一定要学习缓冲区,它能使你对输入输出的认识上升到一个更高的层次,以后不管遇到什么疑难杂症,都能迎刃而解。可以说,输入输出的 命门
就在于缓冲区。
输入其它数据: 除了输入整数,scanf() 还可以输入单个字符、字符串、小数等,请看下面的演示:
#define _CRT_SECURE_NO_WARNINGS
#include
int main()
{
char letter;
int age;
char url[30];
float price;
scanf("%c", &letter);
scanf("%d", &age);
scanf("%s", url); //可以加&也可以不加&
scanf("%f", &price);
printf("26个英文字母的最后一个是 %c。\n", letter);
printf("我经营CSDN博客已经%d年了,网址是 %s,学习Python3入门与进阶专栏的价格是:%g。\n", age, url, price);
return 0;
}
运行示例:
z↙
4↙
https://blog.csdn.net/xw1680↙
69.9↙
26个英文字母的最后一个是 z。
我经营CSDN博客已经4年了,网址是 https://blog.csdn.net/xw1680,学习Python3入门与进阶专栏的价格是:69.9。
scanf() 和 printf() 虽然功能相反,但是格式控制符是一样的,单个字符、整数、小数、字符串对应的格式控制符分别是 %c、%d、%f、%s。scanf() 格式控制符汇总:
表格中,绿色的格式控制符是 C99 新引入的。
C语言有多个函数可以从键盘获得用户输入,它们分别是:
scanf() 是最灵活、最复杂、最常用的输入函数,上一小节我们已经进行了讲解,本节接着讲解剩下的函数,也就是字符输入函数和字符串输入函数。
输入单个字符当然可以使用 scanf() 这个通用的输入函数,对应的格式控制符为 %c,上一小节已经讲到了。本节我们重点讲解的是 getchar()、getche() 和 getch() 这三个专用的字符输入函数,它们具有某些 scanf() 没有的特性,是 scanf() 不能代替的。
① getchar()。 最容易理解的字符输入函数是 getchar(),它就是 scanf("%c", c)
的替代品,除了更加简洁,没有其它优势了;或者说,getchar() 就是 scanf() 的一个简化版本。下面的代码演示了 getchar() 的用法:
#include
int main()
{
char c;
c = getchar(); //合并: char c = getchar();
printf("c: %c\n", c);
return 0;
}
输入示例:
@↙
c: @
② getche()。 getche() 就比较有意思了,它没有缓冲区,输入一个字符后会立即读取,不用等待用户按下回车键,这是它和 scanf()、getchar() 的最大区别。请看下面的代码:
#include
#include
int main()
{
char c = _getche();
printf("c: %c\n", c);
return 0;
}
输入示例:
@c: @
输入 @ 后,getche() 立即读取完毕,接着继续执行 printf() 将字符输出,所以没有按下回车键程序就运行结束了。注意,getche() 位于 conio.h 头文件中,而这个头文件是 Windows 特有的,Linux 和 Mac OS 下没有包含该头文件。换句话说,getche() 并不是标准函数,默认只能在 Windows 下使用,不能在 Linux 和 Mac OS 下使用。注意: getche() 被 _getche() 取代。
③ getch()。 getch() 也没有缓冲区,输入一个字符后会立即读取,不用按下回车键,这一点和 getche() 相同。getch() 的特别之处是它没有回显,看不到输入的字符。所谓回显,就是在控制台上显示出用户输入的字符;没有回显,就不会显示用户输入的字符,就好像根本没有输入一样。回显在大部分情况下是有必要的,它能够与用户及时交互,让用户清楚地看到自己输入的内容。但在某些特殊情况下,我们却不希望有回显,例如输入密码,有回显是非常危险的,容易被偷窥。getch() 使用举例:
#include
#include
int main()
{
char c = _getch();
printf("c: %c\n", c);
return 0;
}
输入 @ 后,getch() 会立即读取完毕,接着继续执行 printf() 将字符输出。但是由于 getch() 没有回显,看不到输入的 @ 字符,所以控制台上最终显示的内容为 c: @。注意,和 getche() 一样,getch() 也位于 conio.h 头文件中,也不是标准函数,默认只能在 Windows 下使用,不能在 Linux 和 Mac OS 下使用。对三个函数的总结:
输入字符串当然可以使用 scanf() 这个通用的输入函数,对应的格式控制符为 %s,上小节已经讲到了;本节我们重点讲解的是 gets() 这个专用的字符串输入函数,它拥有一个 scanf() 不具备的特性。gets() 的使用也很简单,请看下面的代码:
#include
int main()
{
char author[30], lang[30], url[30];
gets(author);
printf("author: %s\n", author);
gets(lang);
printf("lang: %s\n", lang);
gets(url);
printf("url: %s\n", url);
return 0;
}
运行结果:
AmoXiang↙
author: AmoXiang
C-Language↙
lang: C-Language
http://www.baidu http://www.baidu↙
url: http://www.baidu http://www.baidu
gets() 是有缓冲区的,每次按下回车键,就代表当前输入结束了,gets() 开始从缓冲区中读取内容,这一点和 scanf() 是一样的。gets() 和 scanf() 的主要区别是:
也就是说,gets() 能读取含有空格的字符串,而 scanf() 不能。
总结: C语言中常用的从控制台读取数据的函数有五个,它们分别是 scanf()、getchar()、getche()、getch() 和 gets()。其中 scanf()、getchar()、gets() 是标准函数,适用于所有平台;getche() 和 getch() 不是标准函数,只能用于 Windows。
关于缓冲区,我们将在后续的文章中展开讲解。
说到减肥就要运动,看似简单的运动也是能消耗热量的,那么本文的示例就来实现用行走的步数来计算消耗的热量值,具体代码如下所示:
从代码中可以看到,代码中的 int,printf,scanf
等函数,大家已经了解了,可能唯一看不懂的就是橙色框中的代码――运算符表达式,简单来说,它就是用来计算的,在数学中有各种运算符,例如:加、减、乘、除、大于等等。在C语言中也会用到各种运算符来计算数据。本小节就来详细讲解C语言中用到的运算符。
在C语言中,程序需要大量的运算,就必须利用表达式和运算符操纵数据,用来表示各种不同运算的符号称为运算符,而运算符和操作数组成的式子称为表达式。
在数学中,总会用到加、减、乘、除这四则运算,用符号表示分别是 +,-,*,÷
同样,在C语言中也有加各种各样的运算符。例如,C语言中也有 加(+),减(-),乘(*),除(/)
, 当然除了这些运算符,还有其他运算符,如下表所示:
看到 表达式
就会不由自主地想到数学表达式,数学表达式是由数字、运算符和括号等组成,如下图所示:
数学表达式在数学当中是至关重要的,那么表达式在C语言中也同样重要,它是C语言的主体。在C语言中,表达式由操作符和操作数组成。根据表达式所含操作符的个数,可以把表达式分为简单表达式和复杂表达式两种,简单表达式是只含有一个操作符的表达式,而复杂表达式是包含两个或两个以上操作符的表达式,例如:
5+20;// 简单表达式
(iNumber+3)*Bate-2;// 复杂表达式
表达式本身什么事情也不做,只是返回结果值。在程序对返回的结果值不进行任何操作的情况下,返回的结果值不起任何作用。例如上面两行代码,这两个表达式只能得出结果,在表达式的左边没有常量和变量,那么,这个表达式得出的结果就不会被输出使用。表达式产生作用主要包括两种情况:
① 放在赋值语句的右侧
② 放在函数的参数中
表达式返回的结果值是有类型的。表结果值的数据类型取决于组成表达式的变量和常量的类型。
ps: 每个表达式的返回值都具有逻辑特性。如果返回值是非零的,那么该表达式返回真值,否则返回假值。通过这个特点,可以将表达式放在用于控制程序流程的语句中,这样就构建了条件表达式。
在开篇示例中,代码的第08行,使用了 =
这个运算符,
在数学中,它的含义是 等于
,而在C语言中,与数学中的含义不同,它有另外的含义――赋值。 本小节就来介绍 =
在C语言的意义。
在C语言中,=
就是赋值运算符,作用就是将一个数值赋给一个变量,如下图所示:
就像男孩给女孩礼物一样,女孩就相当于变量,男孩相当于赋值运算符,而礼物就相当于值。
赋值表达式就是为变量赋值。在声明变量时,可以为其赋一个值,就是将一个常数或者一个表达式的结果赋值给一个变量,变量中保存的内容就是这个常量或者赋值语句中表达式的值。这就是为变量赋值。为变量赋值为常数的一般形式如下:
数据类型 变量名 = 常数; //举例:
char cChar = 'Z';
int iFirst = 1314;
float fPlace= 1314.520f;
赋值语句把一个表达式的结果值赋给一个变量。一般形式如下:
类型 变量名 = 表达式; //举例:float fPrice=fBase+Day*3;
这句代码得到赋值的变量 fPrice 称为左值,因为它出现的位置在赋值语句的左侧。产生值的表达式称为右值,因为它出现的位置在表达式的右侧。
注意: 这是一个重要的区别,并不是所有的表达式都可以作为左值,如常数只可以作为右值。
生活中,常常会遇到各种各样的计算,例如下图所示,某超市老板每天需要计算本日的销售金额,他就会将每种产品的销售额相加,来计算本日的总销售额,而此处的 相加
即为数学运算符的 +
,+
在C语言中称为算术运算符。
除上图所示的 +
,在开篇示例中,我们可以看到使用了 *
,它也是运算符之一,那么,在C语言中除了这两种运算符,还有其他运算符吗?其实,C语言中有两个单目算术运算符、5个双目算术运算符。下面详细进行介绍。
算术运算符包括两个单目运算符(正和负),五个双目运算符,即乘法、除法、取模、加法和减法。具体符号和对应的功能如下表所示:
在上表中,取模运算符 %
用于计算两个整数相除得到的余数,并且取模运算符的两侧均为整数,如 7%4
的结果是3。其中的单目正运算符是冗余的,也就是为了与单目负运算符构成一对而存在的。单目正运算符不会改变任何数值,例如,不会将一个负值表达式改为正。
注意: 运算符
-
可作为减法运算符,此时为双目运算符。-
也可作负值运算符,此时为单目运算,如 -5 等;运算符+
也是如此,当+
作为加法运算符时,它为双目运算符,为正值运算符时,它为单目运算符。
如果在表达式中使用的是算术运算符,则将表达式称为算术表达式。下面是一些算术表达式的例子,其中使用的运算符就是之前所列出的算术运算符,代码如下:
Number=(3+5)/Rate;
Height=Top-Bottom+1;
Area=Height * Width;
需要说明的是,两个整数相除的结果为整数,如 7/4 的结果为1,舍去的是小数部分。但是,如果其中的一个数是负数时会出现什么情况呢?此时机器会采取 向零取整
的方法,即为 -1.75,取正后是1.75,取整之后是1或者2,采用向0靠拢,那么就要取1,最后结果则为-1。这种方法也称为 向零去尾
,把小数点后的尾去掉 。
注意:如果用
+,-,*,/
运算的两个数中有一个为实数,那么结果是 double 型,这是因为所有实数都按 double 型进行运算。实数的定义:
对取余运算的补充说明: 取余,也就是求余数,使用的运算符是 %。C语言中的取余运算只能针对整数,也就是说,% 的两边都必须是整数,不能出现小数,否则编译器会报错。另外,余数可以是正数也可以是负数,由 % 左边的整数决定:
示例:
#include
int main() {
printf(
"100%%12=%d \n100%%-12=%d \n-100%%12=%d \n-100%%-12=%d \n",
100 % 12, 100 % -12, -100 % 12, -100 % -12
);
return 0;
}
程序运行结果如下图所示:
在C语言中还有两个特殊的运算符,即自增运算符 ++
和自减运算符 --
, 就像公交车的乘客数量,每上来一位乘客,乘客的数量就会增加一个,此时的乘客数量就可以使用自增运算符,而自增运算符的作用就是使变量值增加1。同样,自减运算符的作用就是使变量值减少1,例如,客车的座位,每上来一位乘客,客车的座位就会减少一个,此时座位这个变量就可以使用自减运算符。
注意:在表达式内部,作为运算的一部分,两者的用法可能有所不同。如果运算符放在变量前面,那么变量在参加表达式运算之前完成自增或者自减运算;如果运算符放在变量后面,那么变量的自增或者自减运算在变量参加了表达式运算之后完成,如下图所示:
常见错误:自加自减是单目运算符,因此表达式和常量不可以进行自加自减,例如:5++ 和 (a+5)++ 都是不合法的。
自增和自减的示例1:
/*================================================================
* Copyright (C) 2023 AmoXiang All rights reserved.
*
* 文件名称:05-test.c
* 创 建 者:AmoXiang
* 创建日期:2023年10月11日 16:56:39
* 描 述:
*
================================================================*/
#include
#include
int main() {
int a = 10, b = 20;
printf("a=%d, b=%d\n", a, b); // a=10, b=20
++a; //11
--b; //19
printf("a=%d, b=%d\n", a, b);// a=11, b=19
a++;//12
b--;//18
printf("a=%d, b=%d\n", a, b);// a=12, b=18
return 0;
}
程序运行结果如下图所示:
自增和自减的示例2:
/*================================================================
* Copyright (C) 2023 AmoXiang All rights reserved.
*
* 文件名称:05-test.c
* 创 建 者:AmoXiang
* 创建日期:2023年10月11日 16:56:39
* 描 述:
*
================================================================*/
#include
int main() {
int a = 10, b = 20, c = 30, d = 40;
int a1 = ++a, b1 = b++, c1 = --c, d1 = d--;
// ++a先自增 后参与运算 故a=11,a1=11
printf("a=%d, a1=%d\n", a, a1);
//b++先参与运算 后自增 故b=21,b1=20
printf("b=%d, b1=%d\n", b, b1);
//下面同理
printf("c=%d, c1=%d\n", c, c1);//29 29
printf("d=%d, d1=%d\n", d, d1);//39 40
return 0;
}
程序运行结果如下图所示:
自增和自减的示例3:
/*================================================================
* Copyright (C) 2023 AmoXiang All rights reserved.
*
* 文件名称:05-test.c
* 创 建 者:AmoXiang
* 创建日期:2023年10月11日 16:56:39
* 描 述:
*
================================================================*/
#include
int main() {
int a = 12, b = 1;
// 12 - 1 ==> c = 11, b = 0
int c = a - (b--); // ①
// 13 - (-1) == > 14
int d = (++a) - (--b); // ②
printf("c=%d, d=%d\n", c, d);
return 0;
}
程序运行结果如下图所示:
在数学中,经常会比较两个数的大小。例如,如下图所示,爸爸的身高是1.80米,儿子的身高是1.20米,很明显,爸爸的身高比儿子的身高要高。这句话中,隐含的意思用到了比较,那么这个比较就是关系运算符的一种,在C语言中,关系运算符的作用就是判断两个操作数的大小关系。
关系运算符包括大于、大于等于、小于、小于等于、等于和不等于,如下表所示:
注意: 符号 >=(大于等于)
与 <=(小于等于)
的意思分别是大于或等于、小于或等于。
关系运算符用于对两个表达式的值进行比较,返回一个真值或者假值。返回真值还是假值取决于表达式中的值和所用的运算符。其中真值为1,假值为0,真值表示指定的关系成立,假值则表示指定的关系不正确。例如:
18>3 /*因为18大于3,所以该关系成立,表达式的结果为真值 即为1*/
18>=3/*因为18大于3,所以该关系成立,表达式的结果为真值 即为1*/
18<3 /*因为18大于3,所以该关系不成立,表达式的结果为假值 即为0*/
18<=3 /*因为18大于3,所以该关系不成立,表达式的结果为假值 即为0*/
18==3 /*因为18不等于3,所以该关系不成立,表达式的结果为假值 即为0*/
18!=3 /*因为18不等于3,所以该关系成立,表达式的结果为真值 即为1*/
关系运算符通常用来构造条件表达式,用在控制程序的流程语句中,如 if 语句是用于判断条件而执行语句块,在其中使用关系表达式作为判断条件,如果关系表达式返回的是真值则执行下面的语句块,如果为假值就不去执行,如下图所示:
常见错误:例如 i==3
中的 ==
是合法的关系运算符,而 i=3
中的 =
不是合法的关系运算符,它是赋值运算符。示例,将关系运算符的结果输出:
#include
int main() {
char c = 'k';
int i = 1, j = 2, k = 3;
float x = 3e+5, y = 0.85;
// 'a'->97 + 5 --> 102 'f' < 'k' 'a' + 5 < c==> 1
int result_1 = 'a' + 5 < c, result_2 = x - 5.25 <= x + y;
printf("%d, %d\n", result_1, -i - 2 * j >= k + 1);
printf("%d, %d\n", 1 < j < 5, result_2);
printf("%d, %d\n", i + j + k == -2 * j, k == j == i + 5);
return 0;
}
程序运行结果如下图所示:
目前考驾驶证的人越来越多了,报考驾驶证也是有一定要求的,当然,考不用型号的驾驶证对年龄要求也不同,例如考A1驾照要求年龄在 26~50
岁之间,B1驾照要求年龄是 21~50
岁之内,C1驾照要求年龄是 18~70
岁之内,在数学中可以如图8所示表示这些年龄区间,而在C语言中,需要如图9所示的形式表示年龄区间。
如图9所示的 &&
就是逻辑运算符,在C语言中,不仅有 &&
一个逻辑运算符,接下来我们就来详细介绍C语言中的逻辑运算符和逻辑表达式。
逻辑运算符有3种,如下表所示:
已经介绍过关系运算符可用于对两个操作数进行比较,使用逻辑运算符可以将多个关系表达式的结果合并在一起进行判断。其一般形式如下:
表达式 逻辑运算符 表达式
举例:
age > 18 && age < 70;//逻辑与运算,表示年龄大于18并且小于70
height > 155 || height < 190;//逻辑或运算,表示身高大于155cm或者小于190cm
!weight;//逻辑非运算,表示体重的非值
逻辑运算结果如下表所示:
逻辑与运算符和逻辑或运算符可以用于相当复杂的表达式中。一般来说,这些运算符用来构造条件表达式,用在控制程序的流程语句中,例如,在后续博文中介绍的 if,for,while
语句等。
注意:不要把逻辑与运算符
&&
和逻辑或运算符||
与后续小节要讲的位与运算符&
和位或运算符|
混淆。
逻辑运算符举例:
#include
int main() {
char c = 'k';
int i = 1, j = 2, k = 3;
float x = 3e+5, y = 0.85;
printf("%d,%d\n", !x * !y, !!!x); // 0, 0
//i && j ==> 2 x||2-3 ==> 只要有一个不为0 就返回1
// i < j && x < y ==> (i 1 && 0 ==> 0
printf("%d,%d\n", x || i && j - 3, i < j && x < y); // 1
printf("%d,%d\n", i == 5 && c && (j = 8), x + y || i + j + k);
return 0;
}
程序运行结果如下图所示:
学C语言可能总会听见,int占多少字节,char占多少字节,可能会有很多人疑问,字节是什么意思?那么在C语言中,说到字节就不得不说位。位是计算机存储数据的最小单位。一个二进制位可以表示两种状态(0和1),多个二进制位组合起来便可表示多种信息。
一个字节通常是由8位二进制数组成,当然有的计算机系统是由16位组成,本文中提到的一个字节指的是由8位二进制组成的。如下图所示,8位占一个字节,16位占两个字节。
了解了字节和位的关系,本小节我们就来详细的介绍位运算符和位运算表达式。
C语言既具有高级语言的特点,又具有低级语言的功能,C语言和其他语言的区别是完全支持按位运算,而且也能像汇编语言一样用来编写系统程序,这是C语言的特色之处。下表所示为C语言提供的位运算符:
假设有两个变量 m 和 n,用来保存逻辑结果,m 和 n 可以进行下表所示的4种逻辑运算。其中 1 表示 true(真),0 表示 false(假)。
说明:按位与,遇0则0;按位或,遇1则1;按位异或相同为1,不同为0。不要将逻辑运算符和位运算符混淆,逻辑运算符返回的值是真假值,位运算符是二进制运算的运算符,最终计算的是一个数值。
上表介绍了按位与、按位或、取反以及按位异或,接下来介绍左移和右移。左移
运算符 <<
是双目运算符。其功能是把 <<
左边的运算数的各二进位全部左移若干位,由 <<
右边的数指定移动的位数,高位丢弃,低位补0。例如,m<<2 即把 m 的各二进制位向左移动两位。假设 m=39,那么 m 在内存中的存放情况如下图所示:
若将 m 左移两位,则在内存中的存储情况如下图所示。m 左移两位后由原来的39变成了156。
右移运算符 >>
也是双目运算符。其功能是把 >>
左边的运算数的各二进制位全部右移若干位,>>
右边的数指定移动的位数。例如 m=30 在内存中的存储情况如下图所示:
m>>3 即 30 右移3位变成3,其存储情况如下图所示:
以上是正数移位,负数和正数在计算机存储形式不同,同样使用右移讲解负数移位,例如 m = -30,首先写出 -30,取绝对值,即 30 的二进制数,然后在这基础上按位取反,得到的二进制码再加一,得到下图所示的二进制数,这个数值就是 -30 在内存中存储的情况。
表达式 m>>3 的含义是将 -30 右移 3 位,结果是 -4,,其存储情况如下图所示:
提到逗号,它在我们心中是分隔符的作用,例如有这样一句话:救救我舅舅不救我舅舅我舅舅就没救了
,这句话看起来(读起来)有点晕,到底是救舅舅,还是不救舅舅,这时候,可以加上逗号,改为 救救我舅舅,不救我舅舅,我舅舅就没救了
,如果这样写就能明确意思了。那逗号在这里起到了一个断句的作用,在C语言中,逗号不仅可以用作分隔符,还可以用在表达式中。逗号表达式的一般形式如下:
表达式1,表达式2,…,表达式n
逗号表达式的求解过程是:先求解表达式1,再求解表达式2,一直求解到表达式n,整个逗号表达式的值是表达式n的值。 逗号表达式称为顺序求值运算符。就像数学中常常求几何问题一样,需要按顺序写解题步骤。例如下面使用逗号运算符的代码:
Value=1+3,1+2,15+27;
上面语句中 Value 所得到的值为4,而非 42。由于赋值运算符的优先级比逗号运算符的优先级高,因此先执行赋值的运算。如果要先执行逗号运算,则可以使用括号运算符,代码如下:
Value=(1+3,1+2,15+27);
这句代码最终的结果是 42,因为使用了括号运算符,因此先计算括号内的表达式,然后再执行赋值的运算。
复合赋值运算符是C语言一种缩写形式,可使得变量操作的描述方式更为简洁。例如 *
和 =
复合,如下图所示:
如果在程序中为一个变量赋值,代码如下:
Value=Value*3;
这一行语句是对一个变量进行赋值操作,值为这个变量本身与一个整数常量3相乘的结果值。使用复合赋值运算符可以实现同样的操作。例如上面的语句可以改写成如下语句:
Value*=3;
这种描述更为简洁。关于上面两种实现相同操作的语句,赋值运算符和复合赋值运算符的区别在于:
下表是其他复合赋值运算符:
在数学中,就规定了优先级,如果没有规定,就会出现争先恐后先计算情况。数学中规定:有括号先计算括号里的,没有括号先计算乘除再计算加减。那在C语言中,有这么多运算符,也需要进行规定,如下表所示是各个运算符的优先级和结合性,优先级1代表级别最高,优先级15表示级别最低。
当一个表达式中出现多个运算符时,C语言会先比较各个运算符的优先级,按照优先级从高到低的顺序依次执行;当遇到优先级相同的运算符时,再根据结合性决定先执行哪个运算符:如果是左结合性就先执行左边的运算符,如果是右结合性就先执行右边的运算符。C语言的运算符众多,每个运算符都具有优先级和结合性,还拥有若干个操作数,为了方便记忆和对比,我们在上表中将它们全部列了出来。对于没有学到的运算符,大家不必深究,一带而过即可,等学到时再来回顾。
至此今天的学习就到此结束了,笔者在这里声明,笔者写文章只是为了学习交流,以及让更多学习C语言的读者少走一些弯路,节省时间,并不用做其他用途,如有侵权,联系博主删除即可。感谢您阅读本篇博文,希望本文能成为您编程路上的领航者。祝您阅读愉快!
好书不厌读百回,熟读课思子自知。而我想要成为全场最靓的仔,就必须坚持通过学习来获取更多知识,用知识改变命运,用博客见证成长,用行动证明我在努力。
如果我的博客对你有帮助、如果你喜欢我的博客内容,请点赞
、评论
、收藏
一键三连哦!听说点赞的人运气不会太差,每一天都会元气满满呦!如果实在要白嫖的话,那祝你开心每一天,欢迎常来我博客看看。
编码不易,大家的支持就是我坚持下去的动力。点赞后不要忘了关注
我哦!