前端学习C语言 - 第二篇(常量、运算符、控制和循环)
阅读原文时间:2023年08月15日阅读:1

前文我们写了第一个 c 语言程序,了解了基本的数据类型。本篇将继续学习:常量运算符控制语句循环语句

常量

#define 常量

#define是用来定义常量宏的预处理指令。定义常量的一般形式:#define 常量名 常量值。就像这样:

#define MAX_NUM 100   // 定义整型常量 MAX_NUM 并初始化为 100
#define PI 3.1415926   // 定义双精度浮点型常量 PI 并初始化为 3.1415926

使用 #define 宏定义所定义的常量是不可修改的(const 定义的能被修改),编译器在编译时会将所有代码中用宏定义表示的常量进行替换的操作,即文本替换。这意味着,程序在运行时并没有存储这些常量的值,而是直接使用它们的值来进行计算。例如,以下代码中:

#define MAX_NUM 100

int arr[MAX_NUM];  // 这里的 MAX_NUM 在编译时会被替换成 100

在编译期间,编译器会将代码中的 MAX_NUM 替换成 100,因此程序实际上是使用整数值 100 来声明数组 arr 的大小。

const

const 关键字所定义的常量是可以被修改的。虽然 const 常量不能作为左值(即不能出现在赋值语句的左边),但是通过指针可以修改其所指向的变量的值。请看示例:

#include<stdio.h>

int main() {
   // 使用 const 关键字定义一个常量
   const int val =5;

   // 使用指针访问该常量
   int *ptr= (int*)&val;
   *ptr=10;// 修改存储单元的值

   printf("val = %d\n",val);// 输出:val = 5
   printf("*ptr = %d\n", *ptr);// 输出:*ptr = 10

   return 0;
}

虽然我们通过指针访问了 val 的存储单元并将其中的值修改为了 10,但是 val 的值仍然是 5,这是因为 val 被定义为一个 const 常量,所以编译器会对其进行优化。具体来说,编译器可能会在程序中使用 val 的地方直接替换成常量 5,而不是实际读取 val 所在的存储单元的值。这样做可以提高程序的效率。

Tip:我们应该遵守 const 常量的规定,不要尝试通过任何方法来修改 const 常量。

const vs #define

在定义常量时,使用#defineconst关键字都可以实现相同的功能,但是建议优先使用 const 关键字来定义常量。原因如下:

1.类型安全:const 常量有具体的类型信息,而使用 #define 宏定义常量是不具备类型信息的。在使用 #define 定义常量时,如果某个常量错误地被用于另一种数据类型的处理过程中,编译器无法发现这样的错误。而使用 const 常量定义常量可以有效避免这种类型错误。

  1. 可读性可维护性:通过使用 const 来定义常量,可以使代码更加清晰和易于阅读,降低出错率,并且增强了程序的可维护性。相比之下,使用 #define 宏定义的常量只是简单的文本替换,执行代码后看不到定义的常量值,容易对程序的可读性造成影响,难以维护和扩展。

1.编译器优化:对于 const 常量,编译器会将其存放在内存的只读区域,从而避免了意外修改它们的值。而使用 #define 宏定义的常量只是简单的文本替换,这意味着编译器无法进行优化。

1.作用域:const 常量定义的作用域只局限于定义的文件和其他使用该声明的文件,不影响其他文件中的变量。而 #define 宏定义常量的作用域是全局的,容易导致重名等问题。

因此,总体来说,使用 const 关键字定义常量可以使代码更加清晰、安全、易于维护,并且可以得到编译器的优化作用,建议优先使用 const 来定义常量。

数值常量类型

  • 十进制整数常量:

    123 // 常量值为 123,默认是 int 类型
    -456 // 常量值为 -456,也是 int 类型
    1776L// 常量值为 1776,长整型

  • 进制整数常量:以数字 0 到 7 组成,必须以 0 开头,可以带有前缀 0 和同样的标志。例如:

    05   // 常量值为 5,八进制表示
    077 // 常量值为 63,八进制表示
    031UL// 常量值为 25,无符号长整型

  • 十六进制整数常量:以数字 0 到 9 和字母 A 到 F(或 a 到 f)组成,必须以 0x 或 0X 开头,并且可以带有同样的标志

    0x1A // 常量值为 26,十六进制表示
    0XF00L// 常量值为 3840,长整型
    0x3aUL// 常量值为 58,无符号长整型

  • 实型常量(即小数)

    const float PI =3.1415926;
    const double E =2.7182818284590452354;

字符常量类型

  • 字符常量:用单引号括起来的一个字符,可以是任意字符或转义序列,表示对应的 ASCII 码值或 ANSI 码值。例如:

    'A'   // 常量值为 65,ASCII 码值
    '\n' // 常量值为 10,换行符
    '\x41'// 常量值为 65,十六进制值 A 的 ASCII 码值

  • 字符串常量:用双引号括起来的一个字符串,包含多个字符,以 \0(空字符)结尾。例如:

    "This is a string"     // 常量值为字符串的地址
    "Hello, world!\n"     // 常量值为字符串的地址
    "Good\bmorning,\bKate"// 常量值为字符串的地址,其中 \b 表示退格符

"Hello" 字符串包含 5 个可见字符,但在 C 语言中,字符串末尾需要添加一个表示字符串结尾的 Null 字符 (\0),所以实际上这个字符串占用了 6 个字节的内存空间。

在内存中,该字符串被表示为一个字符数组,如下所示:

+---+---+---+---+---+---+
|H|e|l|l|o|\0|
+---+---+---+---+---+---+

其中,每个字符都占用一个字节(8 位),最后一个空字符 \0 表示字符串的结束,告诉程序不再继续处理该字符串。如果没有 Null 字符,程序就无法区分字符串何时停止,那么在处理字符串时可能会出现一些问题,特别是字符串拼接和复制时。

:在字符串拼接或复制时,目标字符串必须具有足够的内存空间,包括末尾的空字符 \0,否则可能导致缓冲区溢出或不可预期的结果。

定义一个字符串常量。请看示例:

#include<stdio.h>

int main() {
&nbsp; &nbsp;char*str="hello";
&nbsp; &nbsp;printf("您输入的字符是:%s\n",str);

&nbsp; &nbsp;return 0;
}

将字符串常量 "hello" 赋值给了指针变量 str,并使用 %s 格式符将其输出。输出结果为:您输入的字符是:hello

转义字符

最重要的就是\n(换行符)、\t(水平制表符)。

在计算机编程中,转义字符(Escape Character)是以反斜杠(\)开头的一个或多个字符的序列。这些字符被称作转义序列,可以将一些特殊的字符插入到字符串或字符常量中。

下面是一些常见的转义序列及其含义:

  • \' 单引号。用于在单引号字符常量中插入单引号本身,如:printf("'Hello' world\n");
  • \" 双引号。用于在双引号字符串中插入双引号本身,如:printf("a string with "double quotes"\n");
  • \\ 反斜杠。用于在字符串中插入反斜杠本身,如:printf("C:\Windows\System32\n");
  • \n 换行符。用于在字符串中插入一个换行符,如:printf("First line\nSecond line\n");
  • \t 水平制表符(Tab)。用于在字符串中插入一个水平制表符,如:printf("Column1\tColumn2\tColumn3\n");
  • \r 回车符。用于将光标位置移动至行首,如:printf("This is the first line.\rThis is the second line.\n");
  • \b 退格符。用于将光标向左移动一个字符位置,如:printf("ab\bcd\n"); // 输出 acd
  • \0 空字符。也叫空字符(Null character),通常用来表示字符串的结束

在编程中,使用转义字符可以方便地处理特殊字符,充分利用了计算机的二进制表示方法,并且让代码更加灵活。

水平制表符

水平制表符是一种控制字符,通常用来在文本输出中对齐内容或间隔数据字段。

当程序遇到 \t 转义字符时,会在输出中插入一个水平制表符,并将光标移动到下一个制表符位置。这通常相当于将光标向右移动到下一个 8、16 或 32 个字符的位置,具体取决于系统设置和屏幕宽度。

例如下面的代码会输出带有水平制表的两列数字:

printf("1\t2\t3\n");
printf("10\t20\t30\n");

输出如下:

1&nbsp; &nbsp; &nbsp; 2&nbsp; &nbsp; &nbsp; 3
10&nbsp; &nbsp; &nbsp;20&nbsp; &nbsp; &nbsp;30

第二行完美对齐了每个数字。这是因为第二行代码使用了水平制表符制作了对齐效果。

输入和输出

scanf()

下面通过 scanf() 输入一个数字和一个字符。请看示例:

#include<stdio.h>

int main() {
&nbsp; &nbsp;int num;
&nbsp; &nbsp;char ch;

&nbsp; &nbsp;printf("请输入一个整数和一个字符(用空格隔开):");
&nbsp; &nbsp;scanf("%d %c", &num, &ch);

&nbsp; &nbsp;printf("您输入的整数是:%d\n",num);
&nbsp; &nbsp;printf("您输入的字符是:%c\n",ch);

&nbsp; &nbsp;return 0;
}

运行:

请输入一个整数和一个字符(用空格隔开):123a
您输入的整数是:123
您输入的字符是:a

这里,我们使用了scanf函数来读取用户输入,读取的格式为 %d %c,其中 %d 表示读取一个整数%c 表示读取一个字符。通过&num&ch的地址传递,将从终端读取到的整数值和字符值分别存储到定义好的变量 num 和 ch 中。

printf()

printf() 函数是C语言中用于输出格式化字符串的标准库函数。下面是常用的一些输出格式:

  • %d:将参数作为有符号十进制整数打印。
  • %u:将参数作为无符号十进制整数打印。
  • %o:将参数作为无符号八进制数打印。
  • %x、%X:将参数作为无符号十六进制数打印,分别使用小写字母和大写字母表示。
  • %f、%e、%E:将参数作为浮点数打印。使用 %f 时按小数形式输出,使用 %e 或 %E 时按指数形式输出。
  • %c:将参数作为字符打印。
  • %s:将参数作为字符串打印。

在输出格式中还可以使用其他控制字符来控制打印格式(例如空格、正负号等),也可以用 %% 来打印百分号字符。

下面是一个示例程序:

#include<stdio.h>

int main() {
    int n = 10;
    float f= 3.14159;
    char c ='A';
    char*str = "hello, world!";
    printf("整数:%d\n",n);
    printf("八进制:%o\n",n);
    printf("十六进制:%x\n",n);
    printf("浮点数(小数形式):%f\n",f);
    printf("浮点数(指数形式):%e\n",f);
    printf("字符:%c\n",c);
    printf("字符串:%s\n",str);

    return 0;
}

输出结果:

整数:10
八进制:12
十六进制:a
浮点数(小数形式):3.141590
浮点数(指数形式):3.141590e+00
字符:A
字符串:hello,world!

运算符

算数运算符

C语言中常用算数运算符:

  • +:加法运算符。
  • -:减法运算符。
  • *:乘法运算符。
  • /:除法运算符。
  • %:取模(或取余)运算符。

:整数相除,只保留整数部分。例如 10/3 不会是 3.333

请看示例:

#include<stdio.h>

int main() {
&nbsp; &nbsp;int a = 10, b = 3;
&nbsp; &nbsp;float c = 4.5, d = 2.0;

&nbsp; &nbsp;printf("%d + %d = %d\n",a,b,a+b);
&nbsp; &nbsp;printf("%d - %d = %d\n",a,b,a-b);
&nbsp; &nbsp;printf("%d * %d = %d\n",a,b,a*b);
&nbsp; &nbsp;printf("%d / %d = %d\n",a,b,a/b);// 整数相除,只保留整数部分
&nbsp; &nbsp;printf("%d %% %d = %d\n",a,b,a%b);// 取余运算符 %
&nbsp; &nbsp;printf("%.2f * %.2f = %.2f\n",c,d,c*d);
&nbsp; &nbsp;printf("%.2f / %.2f = %.2f\n",c,d,c/d);// 浮点数相除,保留小数部分
&nbsp; &nbsp;return 0;
}

输出:

10+3=13
10-3=7
10*3=30
10/3=3
10%3=1
4.50*2.00=9.00
4.50/2.00=2.25

10 / 3 = 3。在C语言中,整数相除只保留整数部分是因为在计算机内部处理数据时使用的是二进制数(0和1),而二进制数是不能用来表示小数的。前面我们知道 float 表示数的方式和 int 不同。如果需要得到 3.3333,可使用浮点数进行除法运算。就像这样:

inta=10,b=3;
floatresult= (float)a/ (float)b;
练习

题目:输入数字并按照逆序输出

实现:

#include<stdio.h>

int main() {
&nbsp; &nbsp;int num,temp,reversed_num=0;

&nbsp; &nbsp;printf("请输入一个数字:");
&nbsp; &nbsp;scanf("%d", &num);

&nbsp; &nbsp;temp=num;
&nbsp; &nbsp;while(temp!=0) {// 循环计算逆序数
&nbsp; &nbsp; &nbsp; &nbsp;reversed_num=reversed_num*10+temp%10;
&nbsp; &nbsp; &nbsp; &nbsp;temp/=10;
&nbsp; &nbsp; }

&nbsp; &nbsp;printf("您输入的数字的逆序为:%d\n",reversed_num);

&nbsp; &nbsp;return 0;
}

测试:

请输入一个数字:6789
您输入的数字的逆序为:9876

关系运算符

关系运算符是用于比较两个值之间的大小或者关系的运算符,例如大于(>)、小于(<)、等于(==)、大于等于(>=)、小于等于(<=)和不等于(!=)

逻辑运算符

常见的有逻辑与 &&、逻辑或 ||、逻辑非 !

逻辑与和逻辑或都支持短路求值 :当左操作数已经能够决定整个表达式的结果时,右操作数将不会被计算。例如,在 if (a && b) 中,如果变量 a 的值为 false,则无论变量 b 的值是什么,整个表达式的结果一定是 false。

当逻辑非 ! 运算符作用于一个非零值时,其结果为 false;当它作用于零值时,其结果为 true。

位运算

  1. 按位(&):将两个操作数对应的位进行逻辑与操作,即两个位都为1时,结果才为1,否则为0。

    例如,6 & 3 = 2,二进制为 0110 & 0011 = 0010。

  2. 按位(|):将两个操作数对应的位进行逻辑或操作,即两个位中有一个为1时,结果就为1,否则为0。

    例如,6 | 3 = 7,二进制为 0110 | 0011 = 0111。

  3. 按位异或(^):将两个操作数对应的位进行逻辑异或操作,即两个位不相同时,结果为1,否则为0。

    例如,6 ^ 3 = 5,二进制为 0110 ^ 0011 = 0101。

  4. 按位取反(~):将操作数的每个位取反,即0变成1,1变成0。

    例如,~6 = -7,二进制为 ~0110 = 1001。

  5. 左位移(<<):将操作数的各二进制位全部左移若干位,高位丢弃,低位补0。

    例如,6 << 2 = 24,二进制为 0110 前移2位补0,变为 011000 = 24。左移动两位相当于乘以4。

  6. 右位移(>>):将操作数的各二进制位全部右移若干位,低位丢弃,高位补0(对于正数)或补1(对于负数)。

    例如,6 >> 2 = 1,二进制为 0110 后移2位补0,变为 0001 = 1。

练习1

题目:一组数据,例如 1 1 2 2 4 3 3,除了4出现1次,其他都出现两次,请用异或快速找到只出现一次的数字,这里是4。

分析:可以利用异或运算的性质来解决该问题。异或运算的规则是,相同的两个数异或结果为0,不同的两个数异或结果为1。

程序实现:

#include <stdio.h>  

int main()
{
    int arr[] = {1, 1, 2, 2, 4, 3, 3};
    int n = sizeof(arr) / sizeof(arr[0]);
    int result = 0;
    for (int i = 0; i < n; i++) {
        result ^= arr[i]; //异或操作
    }
    printf("只出现一次的数字是:%d\n", result);
    return 0;
}
  1. 第一次循环:result = 0 ^ 1 = 1;
  2. 第二次循环:result = 1 ^ 1 = 0;
  3. 第三次循环:result = 0 ^ 2 = 2;
  4. 第四次循环:result = 2 ^ 2 = 0;
  5. 第五次循环:result = 0 ^ 4 = 4; //  0 xor 100(4的二进制)得到 100(即十进制的4)
  6. 第六次循环:result = 4 ^ 3 = 7; //  100 xor 11(3的二进制)得到 111(即十进制的7)
  7. 第七次循环:result = 7 ^ 3 = 4。 //  111 xor 11 得到 100(即十进制的4)
练习2

题目:统计字符 a 在 ASCII 码表中对应的数字 97 的二进制表示中有多少个 1

程序实现:

#include <stdio.h>

int main()
{
&nbsp; &nbsp; int c = 97; // 字符 a 对应的ASCII码值为 97
&nbsp; &nbsp; int count = 0;
&nbsp; &nbsp; for(int i = 0; i < 8; i++) {
&nbsp; &nbsp; &nbsp; &nbsp; if(c & 1) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; count++;
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; c >>= 1;
&nbsp; &nbsp; }
&nbsp; &nbsp; printf("字符 a 在ASCII码表中对应的数字 97 的二进制表示中有 %d 个 1\n", count);
&nbsp; &nbsp; return 0;
}

结果分析

  • 97的二进制表示是 01100001

  • 字符是1个字节,也就是8位,所以循环8次

  • 接着就是用 a 的各位和1进行异或,如果结果是1,count 就加1。

    第一次循环:i = 0,c & 1 的结果为 1,count 的值加 1,然后 c 右移一位,变成 00110000。

    第二次循环:i = 1,c & 1 的结果为 0,不处理,然后 c 右移一位,变成 00011000。

    第三次循环:i = 2,c & 1 的结果为 0,不处理,然后 c 右移一位,变成 00001100。

    第四次循环:i = 3,c & 1 的结果为 0,不处理,然后 c 右移一位,变成 00000110。

    第五次循环:i = 4,c & 1 的结果为 0,不处理,然后 c 右移一位,变成 00000011。

    第六次循环:i = 5,c & 1 的结果为 0,不处理,然后 c 右移一位,变成 00000001。

    第七次循环:i = 6,c & 1 的结果为 1,count 的值加 1,然后 c 右移一位,变成 00000000。

    第八次循环:i = 7,c & 1 的结果为 1,count 的值加 1,然后 c 右移一位,变成 00000000。

赋值运算符

=

a += 10 等于 a = a + 10

三目运算符

例如:result = a > b ? a : b

Tip:单目运算符:例如 a++。双目运算符: 例如 a + b

自增自减运算符

请看示例:

#include <stdio.h>

int main()
{

&nbsp; &nbsp;int a = 1;
&nbsp; &nbsp; a += a++; // 笔者认为这句话等于:a = a + a; a++;
&nbsp; &nbsp; printf("%d", a); // 所以是 3

&nbsp; &nbsp; a = 1;
&nbsp; &nbsp; a += ++a; // &nbsp;笔者认为这句话等于:++a; a = a + a;
&nbsp; &nbsp; printf("%d", a); // 所以是4
&nbsp; &nbsp; return 0;
}

有的编译器对该代码报错如下:

/workspace/CProject-test/main.c:7:11: warning: unsequenced modification and access to 'a' [-Wunsequenced]
a += a++; // a = a + a; a++;
~~ ^
/workspace/CProject-test/main.c:11:10: warning: unsequenced modification and access to 'a' [-Wunsequenced]
a += ++a; // ++a; a = a + a;
~~ ^
2 warnings generated.

其中 warning: unsequenced modification and access to 'a' [-Wunsequenced] 这个警告出现的原因是因为在同一表达式中有多个操作,而且这些操作对同一个变量进行修改和访问,并且它们之间没有明确定义的顺序。这种情况下,代码的行为是未定义的。

在看这段代码,请问 k 是多少:

int i = 3, k;
k = (++i) + (++i) + (i++);

该代码在腾讯云和阿里云中结果不相同。建议避免这种类型的表达式。

逗号运算符

请看示例,这段代码会输出什么:

#include<stdio.h>
#include<unistd.h>

int main() {
&nbsp; &nbsp;inti;
&nbsp; &nbsp; // 逗号运算符
&nbsp; &nbsp;for(i=1; i<=3,printf("a"); i++) {
&nbsp; &nbsp; &nbsp; &nbsp;printf("%d\n", i);
&nbsp; &nbsp; &nbsp; &nbsp;sleep(1);
&nbsp; &nbsp; }
&nbsp; &nbsp;return 0;
}

该程序会一直运行下去。每秒输出一行:

a1
a2
a3
a4
a5
...&nbsp;

这是因为表达式 i <= 3, printf("a") 实际上等价于 (i <= 3), printf("a"),其结果总是为 true。

控制结构

if

题目:用 if、else if、else 实现2个数字的计算,数字和操作符都通过 scanf 函数输入。

程序实现(关注 if、else if、else 的用法即可):

#include<stdio.h>

int main() {
&nbsp; &nbsp;double num1, num2;
&nbsp; &nbsp;char operator;

&nbsp; &nbsp;printf("计算2个数的加减乘除。例如 33 * 2:\n");
&nbsp; &nbsp;scanf("%lf%c%*c%lf",&num1,&operator,&num2);

&nbsp; &nbsp;if(operator=='+') {
&nbsp; &nbsp; &nbsp; &nbsp;printf("%.2lf+%.2lf=%.2lf\n", num1, num2, num1+num2);
&nbsp; &nbsp; }elseif(operator=='-') {
&nbsp; &nbsp; &nbsp; &nbsp;printf("%.2lf-%.2lf=%.2lf\n", num1, num2, num1-num2);
&nbsp; &nbsp; }elseif(operator=='*') {
&nbsp; &nbsp; &nbsp; &nbsp;printf("%.2lf*%.2lf=%.2lf\n", num1, num2, num1*num2);
&nbsp; &nbsp; }elseif(operator=='/') {
&nbsp; &nbsp; &nbsp; &nbsp;if(num2==0) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;printf("错误:除数不能为0。\n");
&nbsp; &nbsp; &nbsp; &nbsp; }else{
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;printf("%.2lf/%.2lf=%.2lf\n", num1, num2, num1/num2);
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; }else{
&nbsp; &nbsp; &nbsp; &nbsp;printf("错误:无效的操作符。\n");
&nbsp; &nbsp; }

&nbsp; &nbsp;return 0;
}

测试:

开始运行...  

计算2个数的加减乘除。例如 33 * 2:
33 * 2
33.00 * 2.00 = 66.00  

运行结束。
  • %lf 用于读取双精度浮点数并存储到 num1 中。
  • %c 用于读取一个字符并存储到 operator 中。

switch

switch语句用来判断一个表达式的值与若干个常量值中的哪一个相等,并执行相应的代码(可以和if互换)。其一般的语法格式如下:

switch (表达式){
    case 常量1:        // 常量1匹配时执行的代码
        break;
    case 常量2:        // 常量2匹配时执行的代码
        break;
    ...
    default:        // 表达式的值与所有常量都不匹配时执行的代码
        break;
}

其中,表达式为需要被判断的值,而常量1、常量2等则为待匹配的常量值,需要用case关键字进行标识。如果表达式的值与某个常量相等,则执行相应的代码,并使用break语句跳出整个switch结构;如果表达式的值与所有常量都不匹配,则执行default后面的代码块,并同样使用break语句跳出整个switch结构。

需要注意的是,每个case语句都必须以一个常量值开头,且后面必须紧跟一个冒号(:)。另外,case语句中的代码块可以为空,但不能省略冒号。如果一个case语句的代码块为空,且没有接着的break语句,则程序会继续执行下一个case语句的代码块。

用 switch 重写上述 if 的示例:

#include<stdio.h>

int main()
{
&nbsp; &nbsp;doublenum1, num2;
&nbsp; &nbsp;charoperator;

&nbsp; &nbsp;printf("计算2个数的加减乘除。例如 33 * 2:\n");
&nbsp; &nbsp;scanf("%lf%c%*c%lf",&num1,&operator,&num2);

&nbsp; &nbsp;switch(operator)
&nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp;case'+':
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;printf("%.2lf+%.2lf=%.2lf\n", num1, num2, num1+num2);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;break;
&nbsp; &nbsp; &nbsp; &nbsp;case'-':
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;printf("%.2lf-%.2lf=%.2lf\n", num1, num2, num1-num2);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;break;
&nbsp; &nbsp; &nbsp; &nbsp;case'*':
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;printf("%.2lf*%.2lf=%.2lf\n", num1, num2, num1*num2);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;break;
&nbsp; &nbsp; &nbsp; &nbsp;case'/':
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if(num2==0) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;printf("错误:除数不能为0。\n");
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }else{
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;printf("%.2lf/%.2lf=%.2lf\n", num1, num2, num1/num2);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;break;
&nbsp; &nbsp; &nbsp; &nbsp;default:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;printf("错误:无效的操作符。\n");
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;break;
&nbsp; &nbsp; }

&nbsp; &nbsp;return 0;
}

测试:

这里故意输入一个无效操作符,进入 default。就像这样:

开始运行...  

计算2个数的加减乘除。例如 33 * 2:
1 # 2
错误:无效的操作符。  

运行结束。

:假如不小心将default输错,例如 defualt,再次测试 1 # 2,就不会在提示错误:无效的操作符。这是因为 defualt: 变成了 goto 标签。在大型项目中较难发现此问题。

goto

在 C 语言中,goto 是一种流程控制语句,它可以直接跳转到程序中的某个标识符(通常是标签 label),从而实现有选择的跳转或循环。语法格式如下:

// 定义标签
label: statement;

// 跳转到标签
goto label;

goto 语句使用时需要小心谨慎,因为它会破坏代码结构的正常逻辑,使得程序难以理解和调试,建议只在特定情况下使用。

使用 goto 语句来实现在用户输入错误时重新输入的功能。请看示例:

#include<stdio.h>

int main(void)
{
&nbsp; &nbsp;int num;

start:
&nbsp; &nbsp;printf("请输入一个正整数:");
&nbsp; &nbsp;scanf("%d",&num);

&nbsp; &nbsp;if(num<=0)
&nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp;printf("输入错误,请重新输入!\n");
&nbsp; &nbsp; &nbsp; &nbsp;goto start; &nbsp;// 跳转到 start 标签处重新开始输入
&nbsp; &nbsp; }

&nbsp; &nbsp;printf("您输入的正整数为:%d\n", num);

&nbsp; &nbsp;return 0;
}

测试:

开始运行...  

请输入一个正整数:-1
输入错误,请重新输入!
请输入一个正整数:-2
输入错误,请重新输入!
请输入一个正整数:-3
输入错误,请重新输入!
请输入一个正整数:4
您输入的正整数为:4  

运行结束。

循环语句

while

题目:假设房价为 100 万元,每年涨幅为 5%,工资为 20 万元,现在需要多少年才能买得起这套房子?

实现:

#include<stdio.h>

int main(void)
{
&nbsp; &nbsp;double house_price = 100.0; &nbsp;// 房价,单位:万元
&nbsp; &nbsp;const double interest_rate = 0.05; &nbsp;// 每年增长率为 5%
&nbsp; &nbsp;double annual_salary = 20.0; &nbsp;// 年薪,单位:万元
&nbsp; &nbsp;double savings= 0.0; &nbsp;// 积蓄,单位:万元
&nbsp; &nbsp;int year = 0;

&nbsp; &nbsp;while(savings < house_price)
&nbsp; &nbsp; {

&nbsp; &nbsp; &nbsp; &nbsp; house_price *= (1 + interest_rate); &nbsp;// 更新房价
&nbsp; &nbsp; &nbsp; &nbsp; savings += annual_salary; &nbsp;// 更新积蓄
&nbsp; &nbsp; &nbsp; &nbsp; year++;
&nbsp; &nbsp; &nbsp; &nbsp;printf("第%d年,房价为%.2lf万元,积蓄为%.2lf万元\n", year, house_price, savings);

&nbsp; &nbsp; }

&nbsp; &nbsp;printf("需要%d年后才能买得起这套房子\n", year);

&nbsp; &nbsp;return 0;
}

输出:

开始运行...  

第 1 年,房价为 105.00 万元,积蓄为 20.00 万元
第 2 年,房价为 110.25 万元,积蓄为 40.00 万元
第 3 年,房价为 115.76 万元,积蓄为 60.00 万元
第 4 年,房价为 121.55 万元,积蓄为 80.00 万元
第 5 年,房价为 127.63 万元,积蓄为 100.00 万元
第 6 年,房价为 134.01 万元,积蓄为 120.00 万元
第 7 年,房价为 140.71 万元,积蓄为 140.00 万元
第 8 年,房价为 147.75 万元,积蓄为 160.00 万元
需要 8 年后才能买得起这套房子  

运行结束。

for

题目:用for 重写 while 的示例

实现:

#include<stdio.h>

int main(void)
{
&nbsp; &nbsp;doublehouse_price=100.0; &nbsp;// 房价,单位:万元
&nbsp; &nbsp;constdoubleinterest_rate=0.05; &nbsp;// 每年增长率为 5%
&nbsp; &nbsp;doubleannual_salary=20.0; &nbsp;// 年薪,单位:万元
&nbsp; &nbsp;doublesavings=0.0; &nbsp;// 积蓄,单位:万元
&nbsp; &nbsp;intyear; &nbsp;//年份

&nbsp; &nbsp;for(year=1; savings<house_price; year++)
&nbsp; &nbsp; {

&nbsp; &nbsp; &nbsp; &nbsp; house_price*=(1+interest_rate); &nbsp;// 更新房价
&nbsp; &nbsp; &nbsp; &nbsp; savings+=annual_salary; &nbsp;// 更新积蓄
&nbsp; &nbsp; &nbsp; &nbsp;printf("第%d年,房价为%.2lf万元,积蓄为%.2lf万元\n", year, house_price, savings);

&nbsp; &nbsp; }

&nbsp; &nbsp;printf("需要%d年后才能买得起这套房子\n", year-1);

&nbsp; &nbsp;return 0;
}

输出与 while 相同:

开始运行...  

第 1 年,房价为 105.00 万元,积蓄为 20.00 万元
第 2 年,房价为 110.25 万元,积蓄为 40.00 万元
第 3 年,房价为 115.76 万元,积蓄为 60.00 万元
第 4 年,房价为 121.55 万元,积蓄为 80.00 万元
第 5 年,房价为 127.63 万元,积蓄为 100.00 万元
第 6 年,房价为 134.01 万元,积蓄为 120.00 万元
第 7 年,房价为 140.71 万元,积蓄为 140.00 万元
第 8 年,房价为 147.75 万元,积蓄为 160.00 万元
需要 8 年后才能买得起这套房子  

运行结束。

跳循环

continue只是跳过当前循环的一部分,而break是跳出循环(完全终止循环)。两者只能用于循环结构中,不能用于其他程序控制结构。

continue示例输出1 3 5 7 9

&nbsp; &nbsp;inti;
&nbsp; &nbsp;for(i=1; i<=10; i++)
&nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp;if(i%2==0)// 只输出奇数
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;continue;
&nbsp; &nbsp; &nbsp; &nbsp;printf("%d", i);// 输出奇数
&nbsp; &nbsp; }

break  示例输出1 2 3 4 5

&nbsp; &nbsp; intj;
&nbsp; &nbsp;for(j=1; j<=10; j++)
&nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp;if(j>5)// 只输出前5个数
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;break;
&nbsp; &nbsp; &nbsp; &nbsp;printf("%d", j);// 输出前5个数
&nbsp; &nbsp; }

练习

练习1

题目:定整数范围内找到能被 3 整除的数字,并按照每行5个数字的格式进行打印输出

实现:

#include<stdio.h>

int main(void)
{
&nbsp; &nbsp;inti;
&nbsp; &nbsp;intcount=0;
&nbsp; &nbsp;for(i=100; i<=200; i++)
&nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp;if(i%3==0){
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;printf("%d", i);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; count++;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if(count==5){
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;printf("\n");
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; count=0;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }

&nbsp; &nbsp; &nbsp; &nbsp; }

&nbsp; &nbsp; }
&nbsp; &nbsp;return 0;
}

练习2

题目:输入5个整数,求平均值

实现:

#include<stdio.h>

int main(void)
{
&nbsp; &nbsp;inti;
&nbsp; &nbsp;int num;
&nbsp; &nbsp;doublesum=0;
&nbsp; &nbsp;doubleaverage;

&nbsp; &nbsp;printf("请输入五个整数:\n");

&nbsp; &nbsp;for(i=0; i<5; i++)
&nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp;scanf("%d",&num);
&nbsp; &nbsp; &nbsp; &nbsp; sum+=num;
&nbsp; &nbsp; }

&nbsp; &nbsp; average=sum/5.0;

&nbsp; &nbsp;printf("这五个数的平均值为:%.1lf\n", average);

&nbsp; &nbsp;return 0;
}

测试(以空格区分数字或回车区分都可以):

开始运行...  

请输入五个整数:
1 2 3 4 5
这五个数的平均值为:3.0  

运行结束。


开始运行...  

请输入五个整数:
3
4
5
6
7
这五个数的平均值为:5.0  

运行结束。

练习3

题目:有1,2,3,4 四个数字,能组成多少个互不相同且无重复数字的三位数,都是多少

实现:

#include<stdio.h>

int main(void) {
&nbsp; &nbsp;intcount=0;

&nbsp; &nbsp;printf("由 1、2、3、4 组成的所有互不相同且无重复数字的三位数有:\n");

&nbsp; &nbsp;for(inti=1; i<=4; i++) {
&nbsp; &nbsp; &nbsp; &nbsp;for(intj=1; j<=4; j++) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;for(intk=1; k<=4; k++) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if(i!=j&&j!=k&&i!=k) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;int num=i*100+j*10+k;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;printf("%d", num);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; count++;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; }

&nbsp; &nbsp;printf("\n共计%d个三位数。\n", count);

&nbsp; &nbsp;return 0;
}

输出:

开始运行...  

由 1、2、3、4 组成的所有互不相同且无重复数字的三位数有:
123 124 132 134 142 143 213 214 231 234 241 243 312 314 321 324 341 342 412 413 421 423 431 432
共计 24 个三位数。  

运行结束。