2.6 优先级与结合性

优先级与结合性

运算符的优先级与结合性的知识点,在前面的课程中记录过……

2.6.1 优先级与结合性.mp4 (79.94MB)

notes

简述

优先级与结合性:2.2 的笔记中记录的内容,涵盖了大部分 2.6 中老师在视频中介绍的点。
了解一下运算符分级原则
理解自动类型转换和强制类型转换

作业 | x = 3 + 5 * 2 / (4 - 1) 根据运算符的优先级与结合性,分析计算的详细步骤

  1. = 优先级最低,最后计算
  2. =是右结合的,先计算 = 右边表达式的值
    1. 3 + 5 * 2 / (4 - 1)
  3. () 优先级最高,最先计算
    1. 3 + 5 * 2 / 3
  4. * / 优先级高,先计算;由于 5 * 2 / 3 中的 */ 是同级的,此时它们的执行顺序得看结合性,它们都是左结合的,即左边先计算
    1. 3 + 10 / 3
    2. 3 + 3
  5. 执行加法运算
    1. 6
  6. 经过计算,得到表达式右边的结果为 6,最后执行赋值语句
    1. x = 6

image.png

运算符的优先级与结合性

引用:有关 C 语言中的运算符优先级和结合性的部分知识点在这篇文档(2.2 C 语言的符号)中。

运算符的分级原则

image.png

自动类型转换

  1. #include <stdio.h>
  2. int main() {
  3. int a = 10;
  4. float b = 3.14;
  5. // 整型和浮点型进行运算,会自动将整型转为浮点型
  6. float c = a + b;
  7. printf("c = %f\n", c); // 输出结果
  8. return 0;
  9. }
  10. /* 运行结果:
  11. c = 13.140000
  12. */

在这个示例代码中,我们定义了一个 int 类型的变量 a 和一个 float 类型的变量 b。接着我们将 a 和 b 相加并将结果赋值给一个 float 类型的变量 c。在这个计算过程中,由于 整型和浮点型进行运算,编译器会自动将 a 转换为浮点型,然后再进行计算,最后将结果赋给 c

  1. #include <stdio.h>
  2. int main() {
  3. int a = 5;
  4. float b = 73.2;
  5. char c = 'a';
  6. // 将 a 赋值给 b,自动进行类型转换
  7. b = a;
  8. // 将 b 赋值给 a,自动进行类型转换
  9. a = b;
  10. // 将 c 赋值给 a,自动进行类型转换
  11. a = c;
  12. printf("a 的值为:%d\n", a);
  13. printf("b 的值为:%.2f\n", b);
  14. printf("c 的值为:%c\n", c);
  15. return 0;
  16. }
  17. /* 运行结果:
  18. a 的值为:97
  19. b 的值为:5.00
  20. c 的值为:a
  21. */
  1. 将 a 赋值给 b,由于 b 是浮点型变量,因此 a 会自动转换成浮点型再赋值给 b
  2. 将 b 赋值给 a,由于 a 是整型变量,因此 b 会自动转换成整型再赋值给 a。(精度丢失)
  3. 将 c 赋值给 a,由于 a 是整型变量,因此 c 会自动转换成整型再赋值给 a

在自动类型转换中,C 语言会按照一定的规则进行类型转换,如 将小类型自动转换成大类型,或将无符号类型转换成有符号类型。在这个过程中,我们不需要手动进行强制类型转换,C 语言会 自动完成

  1. #include <stdio.h>
  2. int main() {
  3. double a = 3.14;
  4. int b;
  5. // double 类型转为 int 类型
  6. b = a;
  7. printf("b 的值为:%d\n", b); // 输出 b 的值
  8. return 0;
  9. }
  10. /* 运行结果:
  11. b 的值为:3
  12. */

因为 double 变量 a 的值是 3.14,自动转换为 int 类型时只保留了整数部分 3,因此变量 b 的值为 3。自动类型转换可能会导致数据 精度的损失,从而影响程序的正确性,因此在编写程序时应谨慎使用自动类型转换。

  • 自动类型转换是指:有些运算符在操作不同类型的数据时会自动进行类型转换
  • 自动类型转换也称:隐式类型转换
  • 自动类型转换会根据 C 语言中的一定规则进行

自动类型转换的常见规则:

  1. 如果两个操作数的类型不同,C 语言会将它们都转换为同一种类型,然后再进行运算。类型转换的顺序一般为 char -> short -> int -> unsigned int -> long -> unsigned long -> float -> double -> long double
    image.png
  2. 如果将浮点数赋值给整型变量,C 语言会将浮点数的小数部分截断,只保留整数部分,然后再进行赋值操作。
  3. 如果将整型值赋值给浮点型变量,C 语言会自动将整型值转换为浮点型。
  4. 如果将 char 类型的值赋值给整型变量,C 语言会自动将 char 类型的值转换为整型。
  5. 如果将浮点数和整型数进行运算,C 语言会自动将整型数转换为浮点数。
  6. 如果将两个不同类型的指针相减,C 语言会将两个指针转换为相同的整型数,然后再进行相减操作。

注意:

  1. 类型范围:在自动类型转换时,需要确保转换后的类型可以容纳转换前的类型,否则可能会导致数据精度丢失或者溢出的情况。
    1. 安全:取值范围 的赋值给取值范围
    2. 不安全:取值范围 的赋值给取值范围
      1. 若大类型的值在小类型能容纳的范围之内,则平安无事
      2. 但是浮点数转为整数,会丢弃小数部分,而非四舍五入
  2. 精度问题:在将 float 或 double 类型自动转换为 int 类型时,可能会出现精度丢失的问题,因此需要进行舍入操作。在进行浮点数和整数的混合运算时,也需要注意类型转换的问题,否则也可能会出现精度丢失的情况。
  3. 语法规范:在进行自动类型转换时,需要遵循 C 语言的语法规范,否则会出现编译错误。例如,不能将字符类型自动转换为指针类型。

强制类型转换

  1. #include <stdio.h>
  2. int main() {
  3. int a = 10;
  4. float b = 3.14;
  5. int c = (int)(a / b); // 强制将 a / b 的结果转换为整型
  6. float d = a / b;
  7. printf("(int)(a / b) 的结果为:%d\n", c);
  8. printf("a / b 的结果为:%.2f\n", d);
  9. return 0;
  10. }
  11. /* 运行结果:
  12. (int)(a / b) 的结果为:3
  13. a / b 的结果为:3.18
  14. */

在上述代码中,使用了强制类型转换 (int) 将浮点数除以整数得到的结果转换为整型,并将结果存储在变量 c 中。

注意:

  • 在进行强制类型转换时,可能会出现精度丢失的情况。
  • 在使用强制类型转换时,应该尽量避免精度丢失的情况,并在必要的情况下对结果进行四舍五入或其他处理。
  1. #include <stdio.h>
  2. int main() {
  3. int x = 10;
  4. float y;
  5. double z;
  6. y = float(x);
  7. z = double(x);
  8. printf("x:%d sizeof(x):%lu\n", x, sizeof(x));
  9. printf("y:%.2f sizeof(y):%lu\n", y, sizeof(y));
  10. printf("z:%.2f sizeof(z):%lu\n", z, sizeof(z));
  11. return 0;
  12. }
  13. /* 运行结果:
  14. x:10 sizeof(x):4
  15. y:10.00 sizeof(y):4
  16. z:10.00 sizeof(z):8
  17. */

2.7 输入与输出

输入与输出

2.7.1 输入与输出.mp4 (115.54MB)

输入与输出例子

2.7.2 输入与输出例子.mp4 (46.68MB)

notes

简述

  • 2.7.1:输入输出这一部分,细节比较多,不过在本节视频的结尾,有提到一个小 demo,先将 demo 掌握好再说。具体的知识点细节,暂且先不要刻意去记。
  • 2.7.2:本节介绍了一个小 demo —— 如何输出二进制的数据。打印二进制数据,在 2.5.4 扫地机器人 中有类似的 demo,可以结合在一起对比着看。
  • 掌握 4 个输入、输出函数 printf、putchar、scanf、getchar

数据的输入与输出

image.png

在 C 语言中,可以使用标准输入输出库中的函数来实现数据的输入和输出:

  • 输入函数包括 scanf() 和 getchar()
  • 输出函数包括 printf() 和 putchar()

printf

  • printf 是 C 语言中一个非常常用的函数
  • 函数原型:int printf(const char *format, ...);
  • 作用:将格式化的数据输出到标准输出或指定文件中
  • 参数:
    • 第一个参数 format 是格式化字符串,用于指定输出的格式
    • 后面的参数为可选参数,用于填充格式化字符串中的占位符
  • 返回值:除了格式化字符串和可选参数,printf 函数还有一个返回值,表示输出的字符数。如果出现错误,则返回负数。
  • 格式化字符串:以 % 开头的字符表示占位符,具体形式:%[flags][width][.precision][length]specifier flags、width、.precision、length 和 specifier 都是格式化参数,用于指定输出格式
  • 常用的格式化参数:
    • %d、%i:用于输出十进制整数。
    • %f:用于输出浮点数。
    • %s:用于输出字符串。
    • %c:用于输出字符。
    • %p:用于输出指针的地址。
    • %u:用于输出无符号整数。
    • %o:用于输出八进制整数。
    • %x、%X:用于输出十六进制整数。
    • %e、%E:用于输出科学计数法表示的浮点数。
    • %g、%G:用于输出浮点数,具体格式取决于数值的大小。
  • 在占位符中,各个格式化参数之间可以用 . - + # 0 等字符作为标志,用于进一步指定输出格式。

image.png

image.png

image.png

  1. #include <stdio.h>
  2. int main() {
  3. // 1. 字段宽度
  4. int i = 123;
  5. printf("[%6d]\n", i); // => [ 123]
  6. printf("[%06d]\n", i); // => [000123]
  7. // 2. 负号
  8. float x = 1234.567;
  9. printf("[%9.3f]\n", x); // => [ 1234.567]
  10. printf("[%-9.3f]\n", x); // => [1234.567 ]
  11. // 3. 字符*
  12. printf("[%*d]\n", 5, i); // => [ 123]
  13. int w = 6;
  14. printf("[%*d]\n", w, i); // => [ 123]
  15. printf("[%-*.*f]\n", 7, 2, x); // => [1234.57]
  16. return 0;
  17. }

image.png

  1. #include <stdio.h>
  2. int main() {
  3. // 使用转义字符控制输出格式
  4. int m = 3, n = 12;
  5. printf("num1 = %d \t num2 = %d\n", m, n); // => num1 = 3 num2 = 12
  6. // 打印百分号
  7. printf("%f%%\n", 95.6); // => 95.600000%
  8. return 0;
  9. }

putchar

  • putchar 是 C 标准库中的一个输出函数
  • 其定义在 stdio.h 头文件中
  • 函数原型:int putchar(int c);
  • 参数:c 表示要输出的字符
  • 作用:向标准输出流(通常是屏幕)输出一个字符
  • 返回值:输出的字符的 ASCII 码值
  • putchar 只能输出一个字符,如果需要输出字符串,需要使用其他函数,例如 puts 和 printf 等

Q:为什么参数是 int 类型?
因为 char 可能被编译器默认为 signed char 或 unsigned char,当输出一些比较大的字符时可能会发生截断

image.png

  1. #include <stdio.h>
  2. int main() {
  3. putchar('A');
  4. putchar(97);
  5. putchar(65);
  6. char c = '!';
  7. putchar(c);
  8. return 0;
  9. }
  10. // AaA!

scanf

  • scanf() 是 C 语言中的一个输入函数,用于从标准输入(键盘)或文件中读取数据,并将读取到的数据按照指定的格式保存到指定的变量中。
  • scanf 函数原型:int scanf(const char *format, ...);
  • 作用:scanf() 函数会按照指定的格式从标准输入中读取数据,并将其保存到对应的变量中。
  • 参数:
    • 第一个参数 format 表示读取数据的格式,是一个字符串,其可以包含转换说明符(%d、%f 等)
    • 从第二个参数开始,依次是要保存读取到的数据的变量名
  • 返回值:
    • 如果读取成功,scanf() 函数会返回成功读取的数据个数
    • 如果发生错误,scanf() 函数会返回 EOF(-1)
  • 读取数据时,需要输入与指定格式相符的数据,否则会发生错误
  • scanf() 函数在读取数据时会自动忽略空格、换行符等空白字符,但在格式字符串中使用空白字符时会强制要求输入中出现空白字符,否则会读取失败。
  • scanf() 函数不能读取超过指定长度的字符串,否则会发生缓冲区溢出。
  • scanf() 函数读取的数据类型必须与指定格式相符,否则会发生数据类型不匹配的错误。
  • scanf() 函数读取数据时遇到非法字符会停止读取,并将其放回输入流中,需要使用 getchar() 函数将其读取并丢弃。

image.png
image.png

  1. #include <stdio.h>
  2. int main() {
  3. int i, j;
  4. scanf("%d%d", &i, &j);
  5. printf("i = %d\tj = %d\n", i, j);
  6. scanf("%*d%d", &i, &j); // => warning: data argument not used by format string [-Wformat-extra-args]
  7. // scanf("%*d%d", &i);
  8. printf("i = %d\tj = %d\n", i, j);
  9. return 0;
  10. }

警告分析:warning: data argument not used by format string [-Wformat-extra-args]
scanf("%*d%d", &i, &j); 等效写法 scanf("%*d%d", &i);
这个警告就是在提示我们,我们写得 &j 多余了,在输入时会跳过它

image.png

image.png

  1. #include <stdio.h>
  2. int main() {
  3. int i, j;
  4. char ch;
  5. scanf("%d%d%c", &i, &j, &ch);
  6. printf("i = %d\tj = %d\tch = %c\n", i, j, ch);
  7. return 0;
  8. }

image.png

  1. #include <stdio.h>
  2. int main() {
  3. int i, j;
  4. char ch;
  5. scanf("%d%d %c", &i, &j, &ch);
  6. printf("i = %d\tj = %d\tch = %c\n", i, j, ch);
  7. return 0;
  8. }

image.png

  1. #include <stdio.h>
  2. int main() {
  3. int i, j;
  4. char ch;
  5. scanf("%d,%d,%c", &i, &j, &ch);
  6. printf("i = %d\tj = %d\tch = %c\n", i, j, ch);
  7. return 0;
  8. }

image.png

getchar

  • getchar 是 C 语言中的一个输入函数
  • 原型:int getchar(void);
  • 作用:从标准输入(通常是键盘)读取一个字符并返回
  • 返回值:getchar 函数读取用户在标准输入设备上输入的字符,然后将字符转换为整数形式返回,即返回其 ASCII 码值(int 类型)
  • getchar 函数会将输入的字符缓存起来,如果程序中有多次使用 getchar 函数,缓存中的字符将依次被读取
  • 如果读取的字符是回车符 \n,那么表示一行的输入结束,getchar 函数将读取并返回回车符
  • 如果读取的字符是文件结束符 EOF(End Of File,即 Ctrl+Z 或 Ctrl+D),那么表示输入结束,getchar 函数将返回 EOF,该值是一个负整数

image.png

  1. #include <stdio.h>
  2. int main() {
  3. char c;
  4. c = getchar();
  5. putchar(c);
  6. c = 007;
  7. putchar(c);
  8. putchar(007);
  9. return 0;
  10. }

image.png

image.png

  1. #include <stdio.h>
  2. int main() {
  3. char c;
  4. int c1, c2;
  5. c = getchar();
  6. c1 = c - 1; c2 = c + 1;
  7. printf("%c\t%c\t%c\n", c1, c, c2);
  8. printf("%d\t%d\t%d\n", c1, c, c2);
  9. return 0;
  10. }
  11. /* 运行结果:
  12. B
  13. A B C
  14. 65 66 67 */

将整数以二进制形式输出

  1. #include <stdio.h>
  2. /**
  3. * @brief 将 unsigned char 类型的数据以二进制形式输出
  4. * @param c 待输出的 unsigned char 类型的数据
  5. */
  6. void print_bin(unsigned char c) {
  7. printf("c: %d\n", c); // 输出待输出数据的十进制表示
  8. printf("对应的二进制: ");
  9. for (int i = 7; i >= 0; i--) {
  10. printf("%d", (c >> i) & 1); // 输出待输出数据的二进制表示
  11. }
  12. }
  13. int main() {
  14. char x = 0b01101111; // 定义一个二进制数值
  15. // 调用打印二进制函数,将 x 的二进制表示输出到控制台
  16. print_bin(x);
  17. return 0;
  18. }
  19. /* 运行结果:
  20. c: 111
  21. 对应的二进制: 01101111 */

解释:
上述代码定义了一个名为 print_bin 的函数,该函数将传入的 unsigned char 类型的数据以二进制形式输出到控制台。该函数使用了一个 for 循环,从二进制表示的最高位开始逐位输出,每次输出一个二进制位。

在 main 函数中,定义了一个 char 类型的变量 x,使用二进制表示的方式初始化为 0b01101111。然后调用 print_bin 函数,将 x 的二进制表示输出到控制台。

输入一个十进制数,输出这个数的八进制、十六进制、二进制

  1. #include <stdio.h>
  2. int main() {
  3. int num;
  4. printf("请输入一个十进制整数(小于 256):");
  5. scanf("%d", &num);
  6. printf("%d 转换成二进制是 ", num);
  7. // 将十进制数转换为二进制并打印
  8. for (int i = 7; i >= 0; i--) {
  9. int bit = (num >> i) & 1;
  10. putchar(bit + '0');
  11. }
  12. printf("\n");
  13. printf("%d 转换成八进制是 0%o\n", num, num); // 打印八进制数
  14. printf("%d 转换成十六进制是 0x%x\n", num, num); // 打印十六进制数
  15. return 0;
  16. }
  17. /* 运行结果:
  18. 请输入一个十进制整数(小于 256):195
  19. 195 转换成二进制是 11000011
  20. 195 转换成八进制是 0303
  21. 195 转换成十六进制是 0xc3*/

putchar(bit + '0');
参数 bit + ‘0’ 表示将二进制数位 0 或 1 转换成对应的字符 ‘0’ 或 ‘1’,然后使用 putchar() 输出这个字符。由于字符 ‘0’ 的 ASCII 码值为 48,因此加上 0 就能得到对应的字符。例如,当 bit 等于 1 时,表达式 bit + ‘0’ 的值就是 49(即字符 ‘1’ 的 ASCII 码值)。putchar() 函数将其输出到屏幕上,即输出 ‘1’。

2.8 编程实战

温度转换

2.8.1 温度转换.mp4 (71.92MB)

廉租房

2.8.2 廉租房.mp4 (54.56MB)

输入与输出

2.8.3 输入与输出.mp4 (14.14MB)

notes

简述

  • 2.8.1:独立完成“温度转换”的小 demo
  • 2.8.2:独立完成“廉租房”的小 demo
  • 2.8.3:通过写一个两整数求和的小 demo,思考 4 个问题,理解输入、输出。

温度转换

image.png

  1. #include <stdio.h>
  2. int main() {
  3. float fahrenheit, celsius;
  4. printf("请输入华氏温度:");
  5. scanf("%f", &fahrenheit);
  6. // 利用公式将华氏温度转换为摄氏温度
  7. celsius = (fahrenheit - 32) * 5 / 9;
  8. printf("对应的摄氏温度为:%.2f\n", celsius);
  9. return 0;
  10. }
  11. /* 运行结果:
  12. 请输入华氏温度:100
  13. 对应的摄氏温度为:37.78 */

廉租房

image.png

  1. #include <stdio.h>
  2. int main() {
  3. float father_income, mother_income, ming_income, total_income;
  4. float normal_rent, cheap_rent;
  5. float normal_rent_yearly, cheap_rent_yearly;
  6. float yearly_saving;
  7. // 输入爸爸、妈妈和小明的年收入
  8. printf("请输入爸爸的年收入:");
  9. scanf("%f", &father_income);
  10. printf("请输入妈妈的年收入:");
  11. scanf("%f", &mother_income);
  12. printf("请输入小明的年收入:");
  13. scanf("%f", &ming_income);
  14. // 计算小明家庭总年收入
  15. total_income = father_income + mother_income + ming_income;
  16. // 判断小明家庭年收入是否少于6万
  17. if (total_income < 60000) {
  18. printf("小明家庭年收入为 %.2f 元,符合廉租房要求\n", total_income);
  19. } else {
  20. printf("小明家庭年收入为 %.2f 元,不符合廉租房要求\n", total_income);
  21. }
  22. // 输入普通房租和廉租房租金
  23. printf("请输入普通房租每月的租金:");
  24. scanf("%f", &normal_rent);
  25. printf("请输入廉租房每月的租金:");
  26. scanf("%f", &cheap_rent);
  27. // 计算普通房和廉租房每年的租金
  28. normal_rent_yearly = normal_rent * 12;
  29. cheap_rent_yearly = cheap_rent * 12;
  30. // 计算小明家庭每年除房租外还剩余的钱
  31. yearly_saving = total_income - cheap_rent_yearly;
  32. // 输出小明家庭每年除房租外还剩余的钱
  33. printf("小明家庭每年除房租外还剩余 %.2f 元\n", yearly_saving);
  34. return 0;
  35. }
  36. /* 运行结果:
  37. 请输入爸爸的年收入:35000
  38. 请输入妈妈的年收入:15000
  39. 请输入小明的年收入:0
  40. 小明家庭年收入为 50000.00 元,符合廉租房要求
  41. 请输入普通房租每月的租金:1000
  42. 请输入廉租房每月的租金:300
  43. 小明家庭每年除房租外还剩余 46400.00 元 */

输入与输出

重点在于理解这 4 个问题

  1. #include <stdio.h>
  2. int main() {
  3. int x, y, sum;
  4. printf("请输入两个整数:\n");
  5. scanf("%d%d", &x, &y);
  6. sum = x + y;
  7. printf("x + y = %d\n", sum);
  8. return 0;
  9. }
  10. /* 运行结果:
  11. 请输入两个整数:
  12. 3 5
  13. x + y = 8 */

问题 1:测试实际运行过程中,用空格,回车符号,逗号分隔输入的 2 个整数分别输出什么? 使用空格符
使用回车符
使用逗号

问题 2:如果要求用回车分隔输入的 2 个数据,scanf(“%d\n%d\n” ,&x,&y); 这种格式正确吗?为什么? 不正确

原因:
在格式控制字符串 "%d\n%d\n"

  1. 第一个 %d 可以读取输入流中的一个整数,但紧跟其后的 \n 将导致 scanf 函数在读取第一个整数后就停止读取,并将剩余的回车符留在输入流中。
  2. 第二个 %d 将无法读取正确的输入值,而是读取了上一个输入行中留下的回车符。因此,程序的输出结果会异常。

修改:
如果要使用回车符作为输入值之间的分隔符,可以使用格式控制字符串:scanf("%d\n%d", &x, &y);

  1. 第一个 %d 会读取一个整数,并在读取完整数后立即舍弃掉之前输入流中的所有空格和回车符
  2. 接着的 \n 可以保证 scanf 函数正确识别输入值之间的回车符,并将其从输入流中清除
  3. 最后的 %d 将读取第二个整数,输入值之间不需要额外的空格或回车符

问题 3:如果要求输入符号 +,也就是输入 3 + 5,输出 3 + 5 = 8,怎么写程序?

  1. #include <stdio.h>
  2. int main() {
  3. int x, y, sum;
  4. char op;
  5. printf("请输入两个整数和一个符号(如3+5):\n");
  6. scanf("%d%c%d", &x, &op, &y);
  7. if (op == '+') {
  8. sum = x + y;
  9. printf("%d%c%d=%d\n", x, op, y, sum);
  10. } else {
  11. printf("输入有误!\n");
  12. }
  13. return 0;
  14. }

image.png
image.png

问题 4:scanf_s 对字符类型数据有没有什么特殊要求? scanf_s 对字符类型数据的输入没有特殊要求,可以像输入其他类型数据一样使用。

但是为了避免安全漏洞,建议使用带有长度限制的格式字符串。
例如:对于输入字符类型的数据,可以使用 %s 指定格式字符串,同时在后面指定字符串长度,如 %7s,表示最多读取 7 个字符。
这样可以防止输入字符长度超过了所分配的空间大小,导致缓冲区溢出,从而引发安全问题。

2.9 小结

小结

2.9.1 小结.mp4 (18.02MB)

notes

简述

视频中主要提及了 12 个问题,作为本章节的自测题,如果都能够理解,那么本章的知识点掌握得就 OK 了。

PPT | 自测题列表

回答以下 12 个问题:
image.png

问题 1:字符型数据的最大值和最小值分别是多少?

  1. #include <stdio.h>
  2. #include <limits.h>
  3. void print_binary(unsigned char c) {
  4. for (int i = 7; i >= 0; i--) {
  5. printf("%d", (c >> i) & 1);
  6. if (i % 4 == 0) printf(" ");
  7. }
  8. }
  9. int main() {
  10. printf("有符号字符型的最小值是:%d,二进制形式为:", SCHAR_MIN);
  11. print_binary(SCHAR_MIN);
  12. printf("\n有符号字符型的最大值是:%d,二进制形式为:", SCHAR_MAX);
  13. print_binary(SCHAR_MAX);
  14. printf("\n无符号字符型的最小值是:%d,二进制形式为:", 0);
  15. print_binary(0);
  16. printf("\n无符号字符型的最大值是:%d,二进制形式为:", UCHAR_MAX);
  17. print_binary(UCHAR_MAX);
  18. printf("\n");
  19. return 0;
  20. }
  21. /* 运行结果:
  22. 有符号字符型的最小值是:-128,二进制形式为:1000 0000
  23. 有符号字符型的最大值是:127,二进制形式为:0111 1111
  24. 无符号字符型的最小值是:0,二进制形式为:0000 0000
  25. 无符号字符型的最大值是:255,二进制形式为:1111 1111
  26. */

有符号的二进制 1000 0000 转为 10 进制:
有符号的二进制数 1000 0000 在补码表示法下表示的是 -128,因为符号位为1表示负数,其余位取反加1得到其对应的十进制值为:1000 0000 => - 0111 1111 + 1 => - 1000 0000

有符号的二进制 0111 1111 转为 10 进制:
根据最高位为 0 表示正数,最高位为 1 表示负数的规则,该二进制数为正数,因此直接将二进制数转换为十进制数即可。
2. 数据类型与表达式(4) - 图36

问题 2:2 字节的无符号整数的最大值是多少?

二进制表示:1111 1111 1111 1111
公式:2. 数据类型与表达式(4) - 图37
因为无符号整数的范围是从 0 开始,所以最终还需要 -1

补充:<limits.h> 中的 USHRT_MAX 存放的就是 2 字节的无符号整数的最大值。

  1. #include <stdio.h>
  2. #include <limits.h>
  3. int main() {
  4. unsigned short max = USHRT_MAX;
  5. printf("2字节无符号整数的最大值是:%hu\n", max);
  6. return 0;
  7. }
  8. /* 运行结果:
  9. 2字节无符号整数的最大值是:65535
  10. */

问题 3:'X'"X" 的区别?

‘X’ 和 “X” 的区别在于:’X’ 是一个字符类型,而 “X” 是一个字符串类型

  • 'X' 是一个 字符常量,只包含一个字符 'X'
  • "X" 是一个 字符串常量,实际上是由两个字符 'X''\0'(字符串结束符)组成的 字符数组

注意:

  • 单引号 括起来的是 字符常量
  • 双引号 括起来的是 字符串常量

问题 4:'0''\0' 相等吗?

不相等

原因分析:

  • '0' 表示数字 0 的 ASCII 码值 48
  • '\0' 表示 字符串的结束符,其值为 0,所以 ‘\0’ 在字符串中非常重要
  1. #include <stdio.h>
  2. int main() {
  3. printf("'0':%d\n", '0');
  4. printf("'\\0':%d\n", '\0');
  5. printf("NULL:%ld\n", NULL);
  6. return 0;
  7. }
  8. /* 运行结果:
  9. '0':48
  10. '\0':0
  11. NULL:0
  12. */

注意:

  • 字符串以 NULL 或者 '\0' 结尾

问题 5:SIZEOF(“COMPUTER”) 的值为多少?

SIZEOF("COMPUTER") 的值为 9

  • 其中包括 8 个字符和一个字符串结束符 '\0'
  • 当我们计算字符串长度的时候,需要将结尾的 NULL 或者 '\0' 符给减掉
  1. #include <stdio.h>
  2. #include <string.h>
  3. int main() {
  4. char str[] = "COMPUTER";
  5. printf("1. 使用 strlen 获取到字符串 \"COMPUTER\" 的长度是:%lu\n", strlen(str));
  6. printf("2. 使用 sizeof 计算字符串 \"COMPUTER\" 的长度是:%lu\n", sizeof(str) / sizeof(char) - 1);
  7. return 0;
  8. }
  9. /* 运行结果:
  10. 1. 使用 strlen 获取到字符串 "COMPUTER" 的长度是:8
  11. 2. 使用 sizeof 计算字符串 "COMPUTER" 的长度是:8
  12. */
  • strlen 函数返回字符串的实际长度,不包括 字符串末尾的 NULL 字符
  • sizeof 函数返回字符串占用的空间大小,包括 字符串末尾的 NULL 字符,因此计算结果 sizeof(str) / sizeof(char) 还需要 - 1

问题 6:INT I = 33 / 4; 请问 I 的值为多少?

I 的值为 8

公式:2. 数据类型与表达式(4) - 图38
因为 33 除以 4 等于 8 余 1,而在 C 语言中 整数除法会向下取整,所以 I 的值为 8

问题 7:=== 的区别?

  • == 是比较运算符,用于比较两个值是否相等;
  • = 是赋值运算符,用于将右边的值赋给左边的变量;

问题 8:&&& 的区别?||| 的区别?

逻辑运算、位运算

  • &&||逻辑运算符,它们用于将多个逻辑表达式组合在一起,得出一个新的逻辑值。
  • &|位运算符,它们用于将多个二进制位组合在一起,得出一个新的二进制值。

逻辑运算:

  • && 表示逻辑与,当且仅当所有逻辑表达式都为真时,结果为真
  • || 表示逻辑或,当且仅当至少有一个逻辑表达式为真时,结果为真
  • 存在短路现象:当某个逻辑表达式已经能够决定最终结果时,后面的表达式就不再执行。

位运算:

  • & 表示按位与,对应位都为 1 时,结果为 1
  • | 表示按位或,对应位都为 0 时,结果为 0
  • 无短路现象:它们会对所有的位进行运算,不会像逻辑运算符那样出现短路现象。

问题 9:位运算能对浮点型和双精度型变量操作吗?

不能

位运算只适用于整型数据类型,不能对浮点型和双精度型变量进行位运算操作。

因为浮点型和双精度型变量是按照 IEEE 754 标准存储的,其中包含符号位、指数和尾数等部分,而不是简单的二进制表示。

问题 10:数据类型转换,能否改变变量的数据类型和值?

这个问题的描述是有些许问题的,意思应该是在问: 如果在程序运行过程中,发生了类型转换(自动类型转换、强制类型转换),那么变量的类型和变量的值是否会发生变化。

变量类型转换 不会 改变变量的数据类型和变量的值。

变量的数据类型不可能变:
C 语言是一门强类型的语言,强类型意味着在 C 语言中,变量的类型是严格定义的,一旦定义就不能更改。每个变量都有其特定的数据类型,而且只能存储相应类型的数据。

变量没有被重新赋值,所以变量的值也没有变化。
虽然从结果来看,变量的值“貌似”变化了,但是这是在程序执行过程中做的处理,相当于已经把数据从那块地址(空间)中拿出来了,然后改变了数据的值,这过程中并没有直接修改那块地址中存放的内容。

浮点强制转整型,精度丢失,变量的数据变了吗? 精度丢失是指在进行数据类型转换时,一些信息的丢失,从而导致转换后的值不再和原值相等,但这并 不意味着变量的值被改变了

例如,当将一个 float 类型的变量赋值给一个 int 类型的变量时,由于 int 类型的变量无法保存 float 类型的小数部分,所以小数部分将被截断丢失,转换后的值会变成一个整数,但原来的 float 类型变量的值并没有被改变,只是赋值给了一个不同类型的变量,所以数据类型转换只是改变变量的数据类型,而不是改变变量的值。

  1. #include <stdio.h>
  2. int main() {
  3. float x = 3.14159;
  4. int y = (int) x; // 将 x 转换为 int 类型,并将结果存储在 y 中
  5. printf("x = %f\n", x);
  6. printf("y = %d\n", y);
  7. return 0;
  8. }
  9. /* 运行结果:
  10. x = 3.141590
  11. y = 3
  12. */

在一些特殊的情况下,如对 float 或 double 类型的变量进行简单的四则运算时,由于浮点数在计算机内部的存储方式和十进制表示方式有所不同,可能会出现一些意料之外的结果,但这并不意味着变量的值被改变了,而只是计算的结果不够准确。

建议 11:多用 () 运算符

对于那些我们想要让它优先运算的部分,使用 () 小括号将它们给括起来即可。

  1. #include <stdio.h>
  2. int main() {
  3. int a = 2, b = 3, c = 4;
  4. int result1 = a + b * c;
  5. int result2 = (a + b) * c;
  6. printf("result1 = %d\n", result1);
  7. printf("result2 = %d\n", result2);
  8. return 0;
  9. }
  10. /* 运行结果:
  11. result1 = 14
  12. result2 = 20
  13. */

a + b * c;
默认先乘除、后加减,所以默认会先计算 b * c,然后再加上 a

(a + b) * c;
如果我们想要让 a + b 先运算,只要加上小括号即可,这样就是先运算 a + b,然后再乘 c

问题 12:找出下述程序中的错误

  1. #include <stdio.h>
  2. int main() {
  3. int idata1;
  4. const char cdata1 = '0';
  5. unsigned char idata1;
  6. scanf("%d%c%f", idata1, &cdata1); // ,值是不可变的,
  7. return 0;
  8. }
  1. int idata1;``unsigned char idata1; 重复声明同名变量 idata1
  2. scanf("%d%c%f", idata1, &cdata1); idata1 前边少了一个取地址符 &
  3. const char cdata1 = '0';scanf("%d%c%f", idata1, &cdata1); cdata1 是一个常量,常量在程序运行过程中是不可修改的,所以无法直接将 scanf 读入的数据重新赋值给一个常量。
  1. #include <stdio.h>
  2. int main() {
  3. int idata1;
  4. const char cdata1 = '0';
  5. unsigned char idata2;
  6. scanf("%d%c", &idata1, &idata2);
  7. printf("idata1 = %d\n", idata1);
  8. printf("cdata1 = %c\n", cdata1);
  9. printf("idata2 = %d\n", idata2);
  10. return 0;
  11. }
  12. /* 运行结果:
  13. 1A
  14. idata1 = 1
  15. cdata1 = 0
  16. idata2 = 65
  17. */

2.10 华为 cloudIDE 开发编程

  • 题目:温度转换(前面学习过程中已写过)
  • 介绍华为 CloudIDE 的使用(先直接跳过)

本章有一个温度转换的 demo 练手。不过这东西在之前就已经练过了,快速过一遍就好。

2.10.1 华为cloudIDE编程与调试-179题-温度转换.mp4 (62.13MB) 题目描述

  1. #include <stdio.h>
  2. int main() {
  3. int celsius, fahrenheit;
  4. printf("请输入摄氏温度:");
  5. scanf("%d", &celsius);
  6. fahrenheit = 9 * celsius / 5 + 32;
  7. printf("华氏温度为:%d\n", fahrenheit);
  8. return 0;
  9. }
  10. /* 运行结果:
  11. 用例 1:
  12. 请输入摄氏温度:10
  13. 华氏温度为:50
  14. 用例 2:
  15. 请输入摄氏温度:100
  16. 华氏温度为:212
  17. 用例 3:
  18. 请输入摄氏温度:-10
  19. 华氏温度为:14
  20. 用例 4:
  21. 请输入摄氏温度:-50
  22. 华氏温度为:-58
  23. 用例 5:
  24. 请输入摄氏温度:-100
  25. 华氏温度为:-148
  26. */

可以将温度定义为实数,以获取更好的精度。

  1. #include <stdio.h>
  2. int main() {
  3. float celsius, fahrenheit;
  4. printf("请输入摄氏温度:");
  5. scanf("%f", &celsius);
  6. fahrenheit = celsius * 9.0 / 5.0 + 32.0;
  7. printf("摄氏温度 %.2f 对应的华氏温度是:%.2f\n", celsius, fahrenheit);
  8. return 0;
  9. }