C语言系统化精讲(五):C语言格式化输入和运算符与表达式

news2024/10/7 13:23:53

文章目录

  • 一、C语言格式化输入
    • 1.1 C语言scanf:读取从键盘输入的数据(含输入格式汇总表)
    • 1.2 C语言输入字符和字符串(所有函数大汇总)
      • 1.2.1 输入单个字符
      • 1.2.2 输入字符串
  • 二、运算符与表达式
    • 2.1 运算符与表达式
      • 2.1.1 运算符
      • 2.1.2 表达式
    • 2.2 赋值运算符与赋值表达式
      • 2.2.1 赋值运算符
      • 2.2.2 赋值表达式
    • 2.3 算术运算符与算术表达式
      • 2.3.1 算术运算符
      • 2.3.2 算术表达式
      • 2.3.3 自增/自减运算符
    • 2.4 关系运算符与关系表达式
      • 2.4.1 关系运算符
      • 2.4.2 关系表达式
    • 2.5 逻辑运算符与逻辑表达式
      • 2.5.1 逻辑运算符
      • 2.5.2 逻辑表达式
    • 2.6 位运算符与位表达式
      • 2.6.1 位运算符
      • 2.6.2 位运算表达式
    • 2.7 逗号运算符与逗号表达式
    • 2.8 复合赋值运算符
    • 2.9 优先级和结合性

一、C语言格式化输入

初学者牢记:只要用户输入的内容和格式控制字符串匹配,就能够正确提取。在后续讲解格式化输入所演示的奇怪问题,几乎都是因为输入的内容和格式控制字符串不匹配

1.1 C语言scanf:读取从键盘输入的数据(含输入格式汇总表)

程序是人机交互的媒介,有输出必然也有输入,在 C语言系统化精讲(三):C语言变量和数据类型-上篇 一文中讲解了如何将数据输出到显示器上,本小节我们开始讲解如何从键盘输入数据。在C语言中,有多个函数可以从键盘获得用户输入:

  1. scanf():和 printf() 类似,scanf() 可以输入多种类型的数据。
  2. getchar()、getche()、getch():这三个函数都用于输入单个字符。
  3. gets():获取一行数据,并作为字符串处理。

scanf() 是最灵活、最复杂、最常用的输入函数,但它不能完全取代其他函数,大家都要有所了解。本小节我们只讲解 scanf(),其它的输入函数将在下面几个小节进行讲解。

scanf 是 scan format 的缩写,意思是格式化扫描,也就是从键盘获得用户输入,和 printf 的功能正好相反。举例:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
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;
}

运行结果(↙表示按下回车键):

1260↙
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 <stdio.h>
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 <stdio.h>
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 <stdio.h>
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 <stdio.h>
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 <stdio.h>
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.926个英文字母的最后一个是 z。
我经营CSDN博客已经4年了,网址是 https://blog.csdn.net/xw1680,学习Python3入门与进阶专栏的价格是:69.9。

scanf() 和 printf() 虽然功能相反,但是格式控制符是一样的,单个字符、整数、小数、字符串对应的格式控制符分别是 %c、%d、%f、%s。scanf() 格式控制符汇总:
在这里插入图片描述
表格中,绿色的格式控制符是 C99 新引入的。

1.2 C语言输入字符和字符串(所有函数大汇总)

C语言有多个函数可以从键盘获得用户输入,它们分别是:

  1. scanf():和 printf() 类似,scanf() 可以输入多种类型的数据。
  2. getchar()、getche()、getch():这三个函数都用于输入单个字符。
  3. gets():获取一行数据,并作为字符串处理。

scanf() 是最灵活、最复杂、最常用的输入函数,上一小节我们已经进行了讲解,本节接着讲解剩下的函数,也就是字符输入函数和字符串输入函数。

1.2.1 输入单个字符

输入单个字符当然可以使用 scanf() 这个通用的输入函数,对应的格式控制符为 %c,上一小节已经讲到了。本节我们重点讲解的是 getchar()、getche() 和 getch() 这三个专用的字符输入函数,它们具有某些 scanf() 没有的特性,是 scanf() 不能代替的。
① getchar()。 最容易理解的字符输入函数是 getchar(),它就是 scanf("%c", c) 的替代品,除了更加简洁,没有其它优势了;或者说,getchar() 就是 scanf() 的一个简化版本。下面的代码演示了 getchar() 的用法:

#include <stdio.h>
int main()
{
    char c;
    c = getchar(); //合并: char c = getchar();
    printf("c: %c\n", c);
    return 0;
}

输入示例:

@↙
c: @

② getche()。 getche() 就比较有意思了,它没有缓冲区,输入一个字符后会立即读取,不用等待用户按下回车键,这是它和 scanf()、getchar() 的最大区别。请看下面的代码:

#include <stdio.h>
#include <conio.h>
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 <stdio.h>
#include <conio.h>
int main()
{
    char c = _getch();
    printf("c: %c\n", c);
    return 0;
}

输入 @ 后,getch() 会立即读取完毕,接着继续执行 printf() 将字符输出。但是由于 getch() 没有回显,看不到输入的 @ 字符,所以控制台上最终显示的内容为 c: @。注意,和 getche() 一样,getch() 也位于 conio.h 头文件中,也不是标准函数,默认只能在 Windows 下使用,不能在 Linux 和 Mac OS 下使用。对三个函数的总结:
在这里插入图片描述

1.2.2 输入字符串

输入字符串当然可以使用 scanf() 这个通用的输入函数,对应的格式控制符为 %s,上小节已经讲到了;本节我们重点讲解的是 gets() 这个专用的字符串输入函数,它拥有一个 scanf() 不具备的特性。gets() 的使用也很简单,请看下面的代码:

#include <stdio.h>
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() 的主要区别是:

  1. scanf() 读取字符串时以空格为分隔,遇到空格就认为当前字符串结束了,所以无法读取含有空格的字符串。
  2. gets() 认为空格也是字符串的一部分,只有遇到回车键时才认为字符串输入结束,所以,不管输入了多少个空格,只要不按下回车键,对 gets() 来说就是一个完整的字符串。

也就是说,gets() 能读取含有空格的字符串,而 scanf() 不能。

总结: C语言中常用的从控制台读取数据的函数有五个,它们分别是 scanf()、getchar()、getche()、getch() 和 gets()。其中 scanf()、getchar()、gets() 是标准函数,适用于所有平台;getche() 和 getch() 不是标准函数,只能用于 Windows。

  1. scanf() 是通用的输入函数,它可以读取多种类型的数据
  2. getchar()、getche() 和 getch() 是专用的字符输入函数,它们在缓冲区和回显方面与 scanf() 有着不同的特性,是 scanf() 不能替代的
  3. gets() 是专用的字符串输入函数,与 scanf() 相比,gets() 的主要优势是可以读取含有空格的字符串
  4. scanf() 可以一次性读取多份类型相同或者不同的数据,getchar()、getche()、getch() 和 gets() 每次只能读取一份特定类型的数据,不能一次性读取多份数据

关于缓冲区,我们将在后续的文章中展开讲解。

二、运算符与表达式

说到减肥就要运动,看似简单的运动也是能消耗热量的,那么本文的示例就来实现用行走的步数来计算消耗的热量值,具体代码如下所示:
在这里插入图片描述
从代码中可以看到,代码中的 int,printf,scanf 等函数,大家已经了解了,可能唯一看不懂的就是橙色框中的代码――运算符表达式,简单来说,它就是用来计算的,在数学中有各种运算符,例如:加、减、乘、除、大于等等。在C语言中也会用到各种运算符来计算数据。本小节就来详细讲解C语言中用到的运算符。

2.1 运算符与表达式

在C语言中,程序需要大量的运算,就必须利用表达式和运算符操纵数据,用来表示各种不同运算的符号称为运算符,而运算符和操作数组成的式子称为表达式。

2.1.1 运算符

在数学中,总会用到加、减、乘、除这四则运算,用符号表示分别是 +,-,*,÷ 同样,在C语言中也有加各种各样的运算符。例如,C语言中也有 加(+),减(-),乘(*),除(/) 当然除了这些运算符,还有其他运算符,如下表所示:

2.1.2 表达式

看到 表达式 就会不由自主地想到数学表达式,数学表达式是由数字、运算符和括号等组成,如下图所示:
在这里插入图片描述
数学表达式在数学当中是至关重要的,那么表达式在C语言中也同样重要,它是C语言的主体。在C语言中,表达式由操作符和操作数组成。根据表达式所含操作符的个数,可以把表达式分为简单表达式和复杂表达式两种,简单表达式是只含有一个操作符的表达式,而复杂表达式是包含两个或两个以上操作符的表达式,例如:

5+20;// 简单表达式
(iNumber+3)*Bate-2;// 复杂表达式

表达式本身什么事情也不做,只是返回结果值。在程序对返回的结果值不进行任何操作的情况下,返回的结果值不起任何作用。例如上面两行代码,这两个表达式只能得出结果,在表达式的左边没有常量和变量,那么,这个表达式得出的结果就不会被输出使用。表达式产生作用主要包括两种情况:

放在赋值语句的右侧
放在函数的参数中

表达式返回的结果值是有类型的。表结果值的数据类型取决于组成表达式的变量和常量的类型。

ps: 每个表达式的返回值都具有逻辑特性。如果返回值是非零的,那么该表达式返回真值,否则返回假值。通过这个特点,可以将表达式放在用于控制程序流程的语句中,这样就构建了条件表达式。

2.2 赋值运算符与赋值表达式

在开篇示例中,代码的第08行,使用了 = 这个运算符,
在这里插入图片描述
在数学中,它的含义是 等于,而在C语言中,与数学中的含义不同,它有另外的含义――赋值。 本小节就来介绍 = 在C语言的意义。

2.2.1 赋值运算符

在C语言中,= 就是赋值运算符,作用就是将一个数值赋给一个变量,如下图所示:
在这里插入图片描述
就像男孩给女孩礼物一样,女孩就相当于变量,男孩相当于赋值运算符,而礼物就相当于值。

2.2.2 赋值表达式

赋值表达式就是为变量赋值。在声明变量时,可以为其赋一个值,就是将一个常数或者一个表达式的结果赋值给一个变量,变量中保存的内容就是这个常量或者赋值语句中表达式的值。这就是为变量赋值。为变量赋值为常数的一般形式如下:

数据类型 变量名 = 常数; //举例:
char cChar = 'Z';
int iFirst = 1314;
float fPlace= 1314.520f;

赋值语句把一个表达式的结果值赋给一个变量。一般形式如下:

类型 变量名 = 表达式; //举例:float fPrice=fBase+Day*3;

这句代码得到赋值的变量 fPrice 称为左值,因为它出现的位置在赋值语句的左侧。产生值的表达式称为右值,因为它出现的位置在表达式的右侧。

注意: 这是一个重要的区别,并不是所有的表达式都可以作为左值,如常数只可以作为右值。

2.3 算术运算符与算术表达式

生活中,常常会遇到各种各样的计算,例如下图所示,某超市老板每天需要计算本日的销售金额,他就会将每种产品的销售额相加,来计算本日的总销售额,而此处的 相加 即为数学运算符的 ++ 在C语言中称为算术运算符。
在这里插入图片描述
除上图所示的 +,在开篇示例中,我们可以看到使用了 *,它也是运算符之一,那么,在C语言中除了这两种运算符,还有其他运算符吗?其实,C语言中有两个单目算术运算符、5个双目算术运算符。下面详细进行介绍。

2.3.1 算术运算符

算术运算符包括两个单目运算符(正和负),五个双目运算符,即乘法、除法、取模、加法和减法。具体符号和对应的功能如下表所示:
在这里插入图片描述
在上表中,取模运算符 % 用于计算两个整数相除得到的余数,并且取模运算符的两侧均为整数,如 7%4 的结果是3。其中的单目正运算符是冗余的,也就是为了与单目负运算符构成一对而存在的。单目正运算符不会改变任何数值,例如,不会将一个负值表达式改为正。

注意: 运算符 - 可作为减法运算符,此时为双目运算符。- 也可作负值运算符,此时为单目运算,如 -5 等;运算符 + 也是如此,当 + 作为加法运算符时,它为双目运算符,为正值运算符时,它为单目运算符。

2.3.2 算术表达式

如果在表达式中使用的是算术运算符,则将表达式称为算术表达式。下面是一些算术表达式的例子,其中使用的运算符就是之前所列出的算术运算符,代码如下:

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语言中的取余运算只能针对整数,也就是说,% 的两边都必须是整数,不能出现小数,否则编译器会报错。另外,余数可以是正数也可以是负数,由 % 左边的整数决定:

  1. 如果 % 左边是正数,那么余数也是正数;
  2. 如果 % 左边是负数,那么余数也是负数。

示例:

#include <stdio.h>
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;
}

程序运行结果如下图所示:
在这里插入图片描述

2.3.3 自增/自减运算符

在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 <stdio.h>
#include <stdio.h>
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 <stdio.h>
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 <stdio.h>
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;
}

程序运行结果如下图所示:
在这里插入图片描述

2.4 关系运算符与关系表达式

在数学中,经常会比较两个数的大小。例如,如下图所示,爸爸的身高是1.80米,儿子的身高是1.20米,很明显,爸爸的身高比儿子的身高要高。这句话中,隐含的意思用到了比较,那么这个比较就是关系运算符的一种,在C语言中,关系运算符的作用就是判断两个操作数的大小关系。
在这里插入图片描述

2.4.1 关系运算符

关系运算符包括大于、大于等于、小于、小于等于、等于和不等于,如下表所示:
在这里插入图片描述
注意: 符号 >=(大于等于)<=(小于等于) 的意思分别是大于或等于、小于或等于。

2.4.2 关系表达式

关系运算符用于对两个表达式的值进行比较,返回一个真值或者假值。返回真值还是假值取决于表达式中的值和所用的运算符。其中真值为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 <stdio.h>
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;
}

程序运行结果如下图所示:
在这里插入图片描述

2.5 逻辑运算符与逻辑表达式

目前考驾驶证的人越来越多了,报考驾驶证也是有一定要求的,当然,考不用型号的驾驶证对年龄要求也不同,例如考A1驾照要求年龄在 26~50 岁之间,B1驾照要求年龄是 21~50 岁之内,C1驾照要求年龄是 18~70 岁之内,在数学中可以如图8所示表示这些年龄区间,而在C语言中,需要如图9所示的形式表示年龄区间。
在这里插入图片描述
如图9所示的 && 就是逻辑运算符,在C语言中,不仅有 && 一个逻辑运算符,接下来我们就来详细介绍C语言中的逻辑运算符和逻辑表达式。

2.5.1 逻辑运算符

逻辑运算符有3种,如下表所示:
在这里插入图片描述

2.5.2 逻辑表达式

已经介绍过关系运算符可用于对两个操作数进行比较,使用逻辑运算符可以将多个关系表达式的结果合并在一起进行判断。其一般形式如下:

表达式 逻辑运算符 表达式

举例:

age > 18 && age < 70;//逻辑与运算,表示年龄大于18并且小于70
height > 155 || height < 190;//逻辑或运算,表示身高大于155cm或者小于190cm
!weight;//逻辑非运算,表示体重的非值

逻辑运算结果如下表所示:
在这里插入图片描述
逻辑与运算符和逻辑或运算符可以用于相当复杂的表达式中。一般来说,这些运算符用来构造条件表达式,用在控制程序的流程语句中,例如,在后续博文中介绍的 if,for,while 语句等。

注意:不要把逻辑与运算符 && 和逻辑或运算符 || 与后续小节要讲的位与运算符 & 和位或运算符 | 混淆。

逻辑运算符举例:

#include <stdio.h>
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<j) && (x<y) ==> 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;
}

程序运行结果如下图所示:
在这里插入图片描述

2.6 位运算符与位表达式

学C语言可能总会听见,int占多少字节,char占多少字节,可能会有很多人疑问,字节是什么意思?那么在C语言中,说到字节就不得不说位。位是计算机存储数据的最小单位。一个二进制位可以表示两种状态(0和1),多个二进制位组合起来便可表示多种信息。

一个字节通常是由8位二进制数组成,当然有的计算机系统是由16位组成,本文中提到的一个字节指的是由8位二进制组成的。如下图所示,8位占一个字节,16位占两个字节。
在这里插入图片描述
了解了字节和位的关系,本小节我们就来详细的介绍位运算符和位运算表达式。

2.6.1 位运算符

C语言既具有高级语言的特点,又具有低级语言的功能,C语言和其他语言的区别是完全支持按位运算,而且也能像汇编语言一样用来编写系统程序,这是C语言的特色之处。下表所示为C语言提供的位运算符:
在这里插入图片描述

2.6.2 位运算表达式

假设有两个变量 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,,其存储情况如下图所示:
在这里插入图片描述

2.7 逗号运算符与逗号表达式

提到逗号,它在我们心中是分隔符的作用,例如有这样一句话:救救我舅舅不救我舅舅我舅舅就没救了,这句话看起来(读起来)有点晕,到底是救舅舅,还是不救舅舅,这时候,可以加上逗号,改为 救救我舅舅,不救我舅舅,我舅舅就没救了,如果这样写就能明确意思了。那逗号在这里起到了一个断句的作用,在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,因为使用了括号运算符,因此先计算括号内的表达式,然后再执行赋值的运算。

2.8 复合赋值运算符

复合赋值运算符是C语言一种缩写形式,可使得变量操作的描述方式更为简洁。例如 *= 复合,如下图所示:
在这里插入图片描述
如果在程序中为一个变量赋值,代码如下:

Value=Value*3;

这一行语句是对一个变量进行赋值操作,值为这个变量本身与一个整数常量3相乘的结果值。使用复合赋值运算符可以实现同样的操作。例如上面的语句可以改写成如下语句:

Value*=3;

这种描述更为简洁。关于上面两种实现相同操作的语句,赋值运算符和复合赋值运算符的区别在于:

  1. 为了简化程序,使程序精练
  2. 为了提高编译效率。

下表是其他复合赋值运算符:
在这里插入图片描述

2.9 优先级和结合性

在数学中,就规定了优先级,如果没有规定,就会出现争先恐后先计算情况。数学中规定:有括号先计算括号里的,没有括号先计算乘除再计算加减。那在C语言中,有这么多运算符,也需要进行规定,如下表所示是各个运算符的优先级和结合性,优先级1代表级别最高,优先级15表示级别最低。
在这里插入图片描述
当一个表达式中出现多个运算符时,C语言会先比较各个运算符的优先级,按照优先级从高到低的顺序依次执行;当遇到优先级相同的运算符时,再根据结合性决定先执行哪个运算符:如果是左结合性就先执行左边的运算符,如果是右结合性就先执行右边的运算符。C语言的运算符众多,每个运算符都具有优先级和结合性,还拥有若干个操作数,为了方便记忆和对比,我们在上表中将它们全部列了出来。对于没有学到的运算符,大家不必深究,一带而过即可,等学到时再来回顾。

至此今天的学习就到此结束了,笔者在这里声明,笔者写文章只是为了学习交流,以及让更多学习C语言的读者少走一些弯路,节省时间,并不用做其他用途,如有侵权,联系博主删除即可。感谢您阅读本篇博文,希望本文能成为您编程路上的领航者。祝您阅读愉快!


在这里插入图片描述

    好书不厌读百回,熟读课思子自知。而我想要成为全场最靓的仔,就必须坚持通过学习来获取更多知识,用知识改变命运,用博客见证成长,用行动证明我在努力。
    如果我的博客对你有帮助、如果你喜欢我的博客内容,请 点赞评论收藏 一键三连哦!听说点赞的人运气不会太差,每一天都会元气满满呦!如果实在要白嫖的话,那祝你开心每一天,欢迎常来我博客看看。
 编码不易,大家的支持就是我坚持下去的动力。点赞后不要忘了 关注 我哦!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1136259.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

我用好说 AI 画出了漫画故事

现在的 AI 有多神奇&#xff1f;我已经可以用它来 “想故事、写分镜、画漫画” 了。 这里就来秀秀用 好说 AI 做出来的一些漫画&#xff1a; 大家多少都遇到过&#xff1a;曾经有个不错的想法&#xff0c;可能只是 “一个场景”、“一句话”&#xff0c;但真的就一闪而过&…

《算法通关村——黄金挑战数组问题》

《算法通关村——黄金挑战数组问题》 数组中出现次数超过一半的数字 描述 数组中有一个数字出现的次数超过数组长度的一半&#xff0c;请找出这个数字。 例如&#xff1a;输入如下所示的一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次&#xff0c;超过数…

软件测试面试1000问(含答案+文档)

Part1 1、你的测试职业发展是什么&#xff1f; 测试经验越多&#xff0c;测试能力越高。所以我的职业发展是需要时间积累的&#xff0c;一步步向着高级测试工程师奔去。而且我也有初步的职业规划&#xff0c;前3年积累测试经验&#xff0c;按如何做好测试工程师的要点去要求自…

Python基础教程:关于序列操作的方式方法

嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取 序列是指按照位置顺序来存储数据的数据结构&#xff0c;也就是说能通过数值索引进行操作。 实际上&#xff0c;python对序列的解释是&#xff1a;只要类型对象中…

C语言系统化精讲(七):C语言数组详解

文章目录 一、数组的基本概念二、一维数组2.1 一维数组的定义2.2 一维数组初始化2.3 一维数组的引用 三、二维数组3.1 二维数组的定义3.2 二维数组初始化3.3 二维数组的引用 四、C语言数组是静态的&#xff0c;不能插入或删除元素五、C语言数组的越界和溢出5.1 数组越界5.2 数组…

C++基础:函数模板

为了代码重用&#xff0c;代码必须是通用的&#xff1b;通用的代码就必须不受数据类型的限制。那么我们可以把数据类型改为一个设计参数&#xff0c;这种类型的程序设计称为参数化程序设计&#xff0c;软件模板有模板构造&#xff0c;包括函数模板和类模板。 函数模板可以用来…

分享一下怎么做一个签到积分的微信小程序

在微信小程序中&#xff0c;签到积分功能是一种非常实用的功能&#xff0c;它可以帮助企业吸引用户&#xff0c;增加用户的忠诚度和活跃度。下面将介绍如何设计和实现一个签到积分的微信小程序。 一、设计页面 签到积分微信小程序的页面设计应该简洁明了&#xff0c;操作简单。…

error: the following arguments are required: --model, --data 解决方法

错误原因&#xff1a;Windows下需要缺乏配置参数&#xff0c;需要进行相关参数配置。 解决办法&#xff1a;在Pycharm的编辑设置&#xff0c;加上–model--model ****,其中****为指定的模型名称&#xff0c;按照自己实际报错进行添加&#xff0c;比如我这里要跑的模型为bert&am…

如何让元素在页面中完美居中?看这篇文章就够了!

&#x1f3ac; 江城开朗的豌豆&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 &#x1f4dd; 个人网站 :《 江城开朗的豌豆&#x1fadb; 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! ​ 目录 ⭐ 专栏简介 &#x1f4d8; 文章引言 一…

Python爬虫防止被封的方法:动态代理ip

目录 前言 一、为什么需要使用动态IP代理 1.网站反爬虫机制 2.突破本地IP限制 3.获取更多数据 二、Python爬虫动态IP代理的实现方法 1.使用第三方库 2.使用爬虫框架 三、预防被封的方法 1.代理池管理 2.请求间隔设置 3.使用多个代理 总结 前言 在进行网站爬取时&…

四周年,创始人李亚飞写给 ShowMeBug 用户的一封信

见信如晤。 此刻&#xff0c;我心怀感激&#xff0c;感谢您对 ShowMeBug 的支持和鼓励。ShowMeBug 在这几年能够在剧烈竞争的环境中存活下来&#xff0c;证明了大家对我们的认可&#xff0c;在此由衷感谢大家。 今天我怀着前所未有的满足感&#xff0c;想分享下 ShowMeBug 成…

共享购模式:重新定义电商购物体验

在当今的电商市场&#xff0c;消费者对购物体验的需求日益增长&#xff0c;他们不再满足于传统的电商模式。为此&#xff0c;共享购模式应运而生&#xff0c;这种创新模式将线下实体商业与线上虚拟商城相结合&#xff0c;为用户带来全新的购物体验。本文将详细讲解共享购模式的…

Mac版好用的Git客户端 Fork 免激活

Fork是一款强大的Git客户端软件&#xff0c;在Mac和Windows操作系统上都可以使用。汇集了众多先进的功能和工具&#xff0c;可以帮助用户更方便地管理和控制Git仓库。 Fork的界面简洁直观&#xff0c;易于使用。它提供了许多高级的Git功能&#xff0c;如分支管理、合并、提交、…

基于机器视觉的手势检测和识别算法 计算机竞赛

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于深度学习的手势检测与识别算法 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f9ff; 更多资料, 项目分享&#xff1a; https://gitee.com/dancheng…

双十一电视盒子哪个好?数码小编揭晓2023最新电视盒子品牌排行榜

很多消费者反馈电视盒子用不了多久就死机、卡顿&#xff0c;广告超级多&#xff0c;不知道双十一买电视盒子要怎么选择&#xff0c;电视盒子哪个好&#xff1f;小编今天来给大家揭晓一下业内最新发布的电视盒子品牌排行榜&#xff0c;看看入围的都有哪些&#xff1a; 一、泰捷W…

freeRTOS学习day3-任务的挂起和恢复

任务挂起函数 vTaskSuspend(Task1_Handler); 任务恢复函数 vTaskResume(Task1_Handler); 任务挂起后 进入就绪态 会停止运行 实验&#xff1a;创建三个任务 任务一打印 任务二打印 任务三控制任务一的挂起和恢复 #include "sys.h" #include "delay.h&quo…

2024线性代数复习——矩阵代数

相似矩阵&#xff1a;存在可逆矩阵 P P P&#xff0c;使得 P − 1 A P B P^{-1} A PB P−1APB&#xff0c;则称矩阵 A A A&#xff0c; B B B 相似&#xff0c;特征值相等。注意只有相似矩阵 B B B 是对角阵&#xff0c;我们才说它是可以相似对角化的。 A A A 可以相似对角…

渲染对电脑伤害多大?怎么避免对本地电脑的损害?

长时间的渲染会对电脑造成伤害吗&#xff1f;我相信这对于许多从事动画和效果图设计的人来说是一个担忧。本文将详细探讨渲染可能对电脑造成的潜在影响&#xff0c;并提供一些避免这些问题的方法。 1.渲染对电脑伤害多大&#xff1f; 我们都知道&#xff0c;在渲染项目时&…

职场必看!性能测试响应很慢怎么排查?

大多数的性能测试工作人员分为以下三个阶段&#xff1a; 1、出了问题看资源&#xff0c;资源占用如果很高&#xff0c;报以窃喜的心态&#xff0c;恩&#xff0c;发现了&#xff0c;原理是资源瓶颈。 2、资源没有出现瓶颈&#xff0c;通过一些技术手段分析&#xff0c;发现是…

解锁娜扎副驾,年轻人的第一台车就选哪吒AYA

最近“娜扎哪吒”的CP组合可谓刷屏汽车圈,一个是高颜值、有气质的当红演员,一个是有实力、有能力的低调“理工男”,哪吒汽车和娜扎的携手,让不少朋友情不自禁地嗑起了CP,也开始更关注哪吒汽车的产品。 除了刚刚发布的“大气舒适新标杆”哪吒X,入手门槛低、颜值够高、智能体验远…