2020/7/4
在C里, 想搬起石头砸自己的脚极为容易
进制和转换
存储知识
- 回顾:计算机存储的最小单元为 内存单元 大小为1字节 = 8个二进制位。
- 计算机只能使用2进制。
- 10进制、8进制、16进制 是给人类使用,计算机不能使用。
2进制
- 逢2进1,只有0和1
10进制转2进制
除2反向取余法
2转10
8进制
- 程序中,用 0 开头 0123
- 使用格式匹配符 %o、%#o
- 每一位取值范围为:0–7
10转8 和 8转10
2转8
将二进制数,自右向左,每3位一组划分,不足3位的在前面填充0。每组使用 421转换
10111011101
10 111 011 101
010 111 011 101
2 7 3 5
二进制数 :10111011101 对应的 8 进制为:02735
1101001101
1 101 001 101
001 101 001 101
对应的8进制数为:01515
8转2
自左向右,将8进制数的每一位,按照 421的方式,展开为二进制
0167354
001 110 111 011 101 100
对应的二级制数为:1110111011101100
057431
对应的二级制数为:101 111 100 011 001
16进制
- 使用0x开头 0x123
- 使用%x
- 取值范围:0-9,a、b、c、d、e、f
16转2
2转16
将二进制数,自右向左,每4位分为一组,不足4位用0填充。每组使用 8421转换
1011011011110101101
101 1011 0111 1010 1101
0101 1011 0111 1010 1101
5 b 7 A d
对应的16进制: 0x5b7ad
1111011011111011011000 1110
0011 1101 1011 1110 1101 1000 1110
对应的16进制:0x3dbed8e
16转2
自左向右,将16进制数的每一位,按照 8421 的方式展开为2进制数
0x3Fc7e2
对应的二进制:0011 1111 1100 0111 1110 0010
0xa9dc4e
对应的二进制:1010 1001 1101 1100 0100 1110
小结
- C程序中,使用频率 10进制>16进制>8进制
- C程序中,不允许直接给变量用2进制赋值,也不能打印变量的二级制形式
int a = 123; // 10进制赋初值
int b = 0123; // 8进制赋初值
int c = 0x123; // 16进制赋初值
int d = 101101; // 此数不是二进制数。会被编译器理解为10进制。
int e = 01101; // 此数不是二进制数。会被编译器理解为8进制。
- 同一个数据,有不同的表现形式(10进制、8进制、16进制)
输入输出函数
输入函数
- printf函数结合 格式匹配符 输出
- 已学过的格式匹配符
- %d 有符号整型 int
- %c 字符型 char
- %x 16进制
- %u 无符号整型 unsigned int
- %f 单精度浮点型 float
- %#x 16进制 带0x前缀
- %ld 长整型 long
- %lf 双精度浮点型 double
- %llu 无符号长长整型 unsigned long long
- %lld 有符号长长整型 long long
- %lu 无符号长整型 unsigned long
- %hhd char
- %hhu unsigned char
- %hd 短整型 short
- %hu 无符号短整型 unsigned long
- %o 8进制
- %#o 8进制 带0前缀
- 已学过的格式匹配符
- putchar函数
- 向屏幕输出一个字符
putchar('a'); == putchar(97); ---> 向屏幕输出一个字符 a
//只能输出一个字符,不是ASCII码,而是一个字符
putchar('\n'); 向屏幕输出一个换行符。
printf("\n"); 向屏幕输出一个换行符。
输入函数
-
getchar函数
- 在程序运行期间,从键盘(标准输入stdin设备)动态获取用户输入的一个字符。
- 将该字符对应的ASCII码返回
- 将该字符对应的ASCII码返回
- 在程序运行期间,从键盘(标准输入stdin设备)动态获取用户输入的一个字符。
-
scanf函数
- 在程序运行期间,结合格式匹配符,动态获取用户键盘数据,保存到变量中
- 注意:
- 格式匹配串中,不能随意添加\n
- 与格式匹配符对应的变量,加&
- 不想看到scanf出的警告
- 用变量接收scanf的返回值
- 用void强制转换
-
获取一个整数
int main(void)
{
int a = 10; // 定义变量a, 用来存储 键盘获取的 整型数据。
// scanf函数的格式匹配串中,不能随意添加 \n
// int ret = scanf("%d", &a); // &a 代表 a变量的内存空间。 用来存储变量值。
// int ret 作用: 接收scanf返回值,防止出现警告。
(void)scanf("%d", &a); // 会覆盖a的原值。
// (void) 作用: 将 scanf 函数的返回值,强转为 空。 防止出现警告。
printf("scanf接收的变量值为:%d\n", a);
system("pause");
return EXIT_SUCCESS;
}
- 获取多个整数
- 获取字符
C4996报错
- VS中,默认 scanf 函数不能使用,会报错 C4996。
- 在.c 第一行添加
#define _CRT_SECURE_NO_WARNINGS
宏,解决此错误!!
运算符
运算符类型 | 作用 |
---|---|
算术运算符 | 用于处理四则运算 |
赋值运算符 | 用于将表达式的值赋给变量 |
比较运算符 | 用于表达式的比较,并返回一个真值或假值 |
逻辑运算符 | 用于根据表达式的值返回真值或假值 |
逗号运算符 | 用于实现逗号表达式 |
三目运算符 | 用于实现三目运算 |
sizeof运算符 | 用于求字节数长度 |
算数运算符
- 先乘除取余,后加减。 (* / % + -)
- 整数的除法运算,得到结果赋值给 int 变量,只取整数部分。int m = 39 / 10; ----> 3
- 除0不允许。printf("%d\n", 39 / 0);
- 对0取余不允许。printf("%d\n", 39 % 0);
- 不允许对小数取余。printf("%d\n", 39 % 3.4);
- 对负数取余,结果为余数的绝对值。printf("%d\n", 39 % -5); —> 4
- 取余运算:
赋值运算符
- = 赋值运算符,也叫单向赋值运算符。右边 赋值给 左边。
- += 加等于。
a += 5 等价于 a = a + 5;
- -= 减等于。
a -= 5 等价于 a = a - 5;
- *= 乘等于。
a *= 5 等价于 a = a * 5;
- /= 除等于。
a /= 5 等价于 a = a / 5;
- %= 取余等于。
a %= 5 等价于 a = a % 5;
—— 所有赋值运算符,都是带有 副作用 的运算。运算结束后变量值会被修改。
自增自减运算符
- 自减、自减运算符,都是带有副作用的运算符。
int a = 10;
a += 1; // 等价于
a = a + 1; // 等价于
a++; // 等价于
++a;
int b = 8;
b -= 1; // 等价于
b = b-1; // 等价于
b--; // 等价于
--b; // 等价于
- 前缀自增 —— 先自增,再取值。
int a = 10;
int b = ++a;
printf("b = %d\n", b); // b = 11
printf("a = %d\n", a); // a自增完成,变为 11
int c = 20;
printf("++c = %d\n", ++c); // ++c = 21
- 后缀自增 —— 先取值,再自增。
int a = 10;
int b = a++; // 先将a值取出给b赋值,再增1
printf("b = %d\n", b); // b = 10;
printf("a = %d\n", a); // a = 11;
int c = 20;
printf("c++ = %d\n", c++); // 先将c的值取出,匹配给 %d, c再增1
printf("c = %d\n", c); // 上面完成了自增,c = 21
比较运算符
- 含有比较运算符的算式,运算后的结果只有两种:
- 成立:真 —— 1
- 不成立:假 —— 0
运算符 | 术语 | 示例 | 结果 |
---|---|---|---|
== | 相等于 | 4 == 3 | 0 |
!= | 不等于 | 4 != 3 | 1 |
< | 小于 | 4 < 3 | 0 |
> | 大于 | 4 > 3 | 1 |
<= | 小于等于 | 4 <= 3 | 0 |
>= | 大于等于 | 4 >= 1 | 1 |
-
计算机中 判等 使用的 ==, 不使用 =*
-
数学运算中的 : 13 < var < 16 。 在程序中,不能这么使用。改写成 逻辑运算。var > 13 && var < 16
逻辑运算符
- 通常用作,将多个比较表达式连接在一起,形成一个表达式。
- 0:为假
- 非0:为真。( 1为真、-34为真、4.5676为真 )
运算符 | 术语 | 示例 | 结果 |
---|---|---|---|
! | 非 | !a | 若a为假,则!a为真;若a为真,则!a为假。 |
&& | 与 | a && b | 若a和b都为真,则结果为真,否则为假。 |
|| | 或 | a || b | 若a和b有一个为真,则结果为真,二者同为假,结果为假。 |
- 逻辑运算符使用 8字箴言:
- 逻辑非:
- 非真为假,非假为真。
- 非真为假,非假为真。
- 逻辑非:
- 逻辑与(并且)
- 同真为真,其余为假。
- 同真为真,其余为假。
- 逻辑或(或)
- 有真为真,同假为假。
- 有真为真,同假为假。
短路运算
- 当有多个表达式时,若左边的表达式的值,足矣确定越算结果,则右边的表达式不计算。
逗号运算符
- 含有 “,” 运算符的表达式,称之为 逗号表达式。整个逗号表达式运算结果,取最后一个子表达式结果。
// 逗号运算符
int main(void)
{
int x, y, z;
int a = (x = 1, z = 3, y = 2);
printf("a = %d\n", a); // a = 2
return 0;
}
三目运算符
语法:表达式1 ?表达式2 :表达式3
- 表达式1,通常是一个判别表达式。(或者当成判别表达式用)
- 表达式1判别结果为 真:
- 整个三目运算取 表达式2的值。
- 表达式1判别结果为 假:
- 整个三目运算取 表达式3的值。
- 整个三目运算取 表达式3的值。
- 表达式1判别结果为 真:
- 注意:三目运算语法允许嵌套。但是!在编程时,不推荐使用。—— 极大的影响代码可读性。
运算符优先级
[]、() > 负号、自增自减、强转、逻辑非、sizeof、*、& > 算数运算符(先乘除取余,后加减) > 比较运算符 > 逻辑运算符 > 三目运算符 > 赋值运算符 > 逗号运算符
if 分支语句
用来实现模糊匹配。 用来匹配一定的数据范围。
- if … else 分支
int main(void)
{
int var;
printf("请输入一个整数:");
(void)scanf("%d", &var);
if (var > 0)
{
printf("var > 0\n");
}
else
{
printf("var <= 0\n");
}
return 0;
}
- if … else if … else if … else if … else
// 语法:
if (判别表达式1) // 此处不能添加;
{
判别表达式1成立(为真),执行的代码。
}
else if (判别表达式2) // 此处不能添加;
{
判别表达式1不成立(为假), 判别表达式2成立(为真),执行的代码。
}
else if (判别表达式3) // 此处不能添加;
{
判别表达式1、2均不成立(为假), 判别表达式3成立(为真),执行的代码。
}
。。。
else
{
以上所有判别表达式都为假。 执行的代码。
}
// 输入学生成绩,打印优良可差。90+优秀、70-90良好、60+可以、60- 不及格
int main(void)
{
int score; // 代表学生成绩
printf("请输入学生成绩:");
(void)scanf("%d", &score);
if (score >= 90)
{
printf("优秀!\n");
}
else if (score >= 70 && score < 90)
{
printf("良好!\n");
}
else if (score >= 60 && score < 70)
{
printf("及格\n");
}
else
{
printf("不及格\n");
}
system("pause");
return EXIT_SUCCESS;
}
switch 分支语句
- 用来实现精确匹配。匹配具体的一个数值。不能直接用来判断区间。
// 基本语法。
switch (匹配表达式)
{
case 常量1:
执行语句1;
break; // 结束当前 switch 分支,防止 case 穿透。
case 常量2:
执行语句2;
break; // 结束当前 switch 分支,防止 case 穿透。
case 常量3:
执行语句3;
break; // 结束当前 switch 分支,防止 case 穿透。
。。。
case 常量N:
执行语句N;
break; // 结束当前 switch 分支,防止 case 穿透。
default:
上述所有 case 都不满足的其他情况。
break;
}
使用 switch 语句实现 :打印学生成绩 优良可差。
int main(void)
{
int score; // 代表学生成绩
printf("请输入学生成绩:");
(void)scanf("%d", &score);
switch (score/10) // 确定具体的匹配数据。
{
case 10: // 做具体匹配
printf("优秀!\n");
break;
case 9: // 做具体匹配
printf("优秀!\n");
break;
case 8: // 做具体匹配
printf("良好!\n");
break;
case 7:
printf("良好!\n");
break;
case 6:
printf("可以!\n");
break;
default:
printf("差劲\n");
break;
}
return EXIT_SUCCESS;
}
case 穿透
- 一个case分支,如果没有 break 关键字。 那么它会继续向下执行下一个 case 分支。这叫做:case 穿透。
- 实际编程中,case 穿透可以加以利用。
// 输入学生成绩,打印优良可差。90+优秀、70-90良好、60+可以、60- 不及格
int main(void)
{
int score; // 代表学生成绩
printf("请输入学生成绩:");
(void)scanf("%d", &score);
switch (score/10)
{
case 10: // 没有break,会发送case穿透。
case 9:
printf("优秀!\n");
break;
case 8: // 没有break,会发送case穿透。
case 7:
printf("良好!\n");
break;
case 6:
printf("可以!\n");
break;
default:
printf("差劲\n");
break;
}
return EXIT_SUCCESS;
}
编程练习
1.进制转换
(1)写出 16进制 数 0xc5af 对应的二进制数。
(2)写出 2进制 数 110101001 对应的10进制数 和 16进制数。
//0x5af
//010110101111
//1 1010 1001
//425
//0x1a9
2.已知3个整数 a、b、c, 使用 三目运算 或 if语句 找出 3个数中的最小值。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main(void)
{
int a, b, c;
//三目运算
printf("请输入三个整数a,b,c:\n");
(void)scanf("%d %d %d",&a,&b,&c);
int min;
min = b < c ? b : c;
min = a < min ? a : min;
printf("三个数中的最小值为:%d\n",min);
return 0;
}
3.接收用户输入一个数字作为年份,判断是否为闰年。提示:能被4整除,且不能被100整除为闰年。能被400整除也为闰年。
- 示例:1900 不是闰年。 2000 年是闰年。 2004 年是闰年。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main(void)
{
unsigned int year;
printf("请输入一个年份:\n");
(void)scanf("%u",&year);
if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
{
printf("%u年是闰年\n",year);
}
else
{
printf("%u年不是闰年\n",year);
}
return 0;
}