文章目录
每日英语:
abs = absolute:绝对
rel = relative:相对
priority:优先级
if:如果
else:否则
salary:薪资
min:最小
max:最大
typ:典型
switch:开关
break:中断,结束
continue:继续
回顾:
1.运算符和表达式
1.1 算数运算符 +, -, *, %
1.2 赋值运算符:=
1.3 复合运算符
+, -, *, /, %==, &=, |=
1.4 自增自减运算符:++,–
1.5 关系运算符:==,!=, <=, >=, <,>
1.6 逻辑运算符 &&,||, !
1.7 位运算符:&,|,^,~
1.8 移位运算符:<<,>>
1.9 实际开发两个重要公式
移的位数:1, 3, 7, f , 1f, 3f…
位清0:A &= ~(B << C);
位置1:A |= (B << C);
建议:如果要修改某些位(位数大于等于2位),先清0后置1
例如:
int a = 0xaacc7654;
// 要求将其中的cc76修改为ff78
a &= ~(0xffff << 8);
a |= (0xcc78 << 8);
1.10 取地址运算符&和解引用运算符*
1.11 条件运算符
-
语法格式:D = 表达式A ?表达式B :表达式C
语义:如果为位真,D=表达式B的结果,否则D=表达式C得到结果
例如:
int a = 1 > 2 ? 2 : 3; // a = 3 int a = 1 ? 2 : 3; // a = 2
案例:求一个数的绝对值(例如:-5的绝对值为5,5的绝对值为5)
参考代码:abs.c
1 /*条件运算符,求一个数的绝对值*/
2 #include <stdio.h>
3
4 int main(void)
5 {
6 int a = 1 > 2 ? 2 : 3;
7 printf("a = %d\n", a);
8
9 int b = -20;
10 b = b >= 0 ? b : 0 - b;
11 printf("b = %d\n", b);
12
13 return 0;
14 }
1.12 运算符的优先级(先算谁,后算谁)
例如:
printf("%d\n", 2 + 3 * 2); // 8
pritnf("%d\n", (2 + 3) * 2); // 10
结论:()它的优先级最高,不嫌多,()里面的内容先算
例如:
int a;
a = 1 > 2 && 2 < 3; // 0
等价于
a = (1 > 2) && (2 < 3); // 提高代码的可读性
2. 数据类型转换:隐式转换和强制转换
2.1 隐式转换
2.1.1 隐式转换特点
如果表达式中不同数字的数据类型不同,gcc编译器自动先将不同的数据类型转换成相同的数据类型之后再做运算
2.1.2 隐式转换分三种情形
- 隐式转换过程中必须把占内存小的转换成占内存大的类型
例如:
int a = 0, c; // 各占4字节
char b = 2; // 占1字节
c = a + b; // gcc编译器自动先将b的数据类型转换成int类型然后再和a做相加运算
-
如果既有整型数据类型,又有浮点数类型,gcc编译器自动将整型数据类型转换成浮点数据类型再做运算
-
如果既有无符号数据类型,又有有符号数据类型,gcc编译器自动将有符号转无符号类型再做运算
-
不推荐使用隐式转换,代码可读性极差
-
三字经总结:小转大,整转浮,有转无
2.2 强制转换(推荐使用,目的,提高代码的可读性)
2.2.1 语法
目标类型变量 = (目标类型)源类型变量
例如:
char a = 90;
int b = a; //隐式转换,强制转换a的数据类型位int
或者
int b = (int)a; //强制转换,强制转换a的数据类型为int
2.2.2 注意
强制数据类型转换可能会造成数据的丢失
例如:
char a = (char)300; //强制将int类型的300转换成char类型再赋值给变量,此过程发生了数据的丢失
//因为char类型的数字范围:-128~127
//变量a是存不下300的,只能存一部分
pritnf("a = %d\n", a);
所以:强制数据类型转换都是小转大或者相等转;隐式转换也遵循此道理,否则照样也会发生数据丢失
2.2.3 切记
不管那种类型转换都不会改变变量本身的值
例如:
int a = 555;
char b = (char)a;
pritnf("a = %d, b = %d\n", a, b); // a =555, b = 43
参考代码:type.c
/*数据类型转换演示*/
#include <stdio.h>
int main(void)
{
// 1.隐式转换
//整转浮
//由于里面有0.9这个数,gcc默认当double类型(8字节),所以gcc自动将其中的int转换成double类型
printf("sizeof(1?1:0.9)的结果是%d\n", sizeof(1?1:0.9));
// 有转无
// -7,3编译器默认都当成int类型,所以无需转换,直接做运算,结果-4>0
printf("-7 + 3 > 0的结果是%d\n", -7 + 3 > 0);
// 由于里面有3u,编译器当无符号,所以编译器先把-7转换成7在跟3做运算
printf("-7 + 3u > 0的结果是%d\n", -7 + 3u > 0);
// 问:-7有符号转换成无符号数是几呢?
//提示:不管是什么进制,不管有符号还是无符号,在内存中都是唯一相同的二进制,只是做了不同的表现
printf("-7的无符号是%u\n", -7);
//强制类型转换
printf("(char)300的结果是%d\n", (char)300); // a =44=300(太大了)-128=172(太大了)-128=44
int a = 555;
char b = (char)a;
printf("a = %d, b = %d\n", a, b);
int c = 520;
long d = (long)c; //相同转
printf("c = %d, d = %ld\n", c, d);
char i = 120;
int j = (int)i; //小转大
printf("j = %d, i = %hhd\n", j, i);
return 0;
}
结果:
sizeof(1?1:0.9)的结果是8
-7 + 3 > 0的结果是0
-7 + 3u > 0的结果是1
-7的无符号是4294967289
(char)300的结果是44
a = 555, b = 43
c = 520, d = 520
j = 120, i = 120
第六课:C语言流程控制
1.明确
C程序是一个结构化程序,由顺序结构,分支结构和循环结构构建的
单入口(main)和单出口(return 0)的程序
类比:
C程序---->一栋楼
顺序结构------>一居室
分支结构------->二居室
循环结构------->三居室
顺序结构就是CPU从main函数开始从上到下一次运行
2.分支结构
2.1分支结构的功能
实现多选一
2.2 分支结构的分类
条件分支和开关分支
2.3条件分支语法
形式1:
if(表达式){
语句;
}
语义:如果表达式运算结果为真,那么久执行语句,否则就不执行语句
例如:
int a = 1;
if(a == 1) { // 普通程序员
pritnf("a等于1\n");
}
// 好程序员代码
if(1 == a) {
printf("a等于1\n");
}
目的:为了防止将"=="误写成"="
if(a == 1)误写成if(a = 1),结果就是相等关系运算瞬间变成了赋值运算把1赋值给a,显然永远成立
if(1 == a)误写成if(1 = a),结果是gcc编译器报错,因为不能给常量赋值,让编译器帮你检查
参考代码:if.c
/*条件分支语句*/
#include <stdio.h>
int main(void)
{
int a = 1;
//普通程序员
if(a == 1){
printf("a等于1.\n");
}
// 高级写法
if(1 == a){
printf("a等于1.\n");
}
a = 2;
//对比验证
if(a=1) { //变成赋值运算符
printf("错误情形:a等于2.\n");
}
/*
if(1=a) {
printf("纠错:a=2.\n");
}
*/
if(1>2){
printf("1>2\n");
}
if(1>2) {
printf("1>2.\n");
}
return 0;
}
形式2:
if(表达式) {
语句1;
} else {
语句2;
}
语义:如果表达式为真,那么久运行语句1,否则语句2
int a = 1;
if(1 == a) {
printf("a等于1.\n");
} else {
pritnf("a不等于1.\n");
}
参考代码:if2.c
/*条件分支演示*/
#include <stdio.h>
int main(void) {
//形式2演示
int a = -20;
if(a >= 0) {
printf("a是非负数.\n");
} else {
printf("a是负数.\n");
}
return 0;
}
形式3:
if(表达式1) {
语句1;
} else if(表达式2) {
语句2;
} else if(表达式3) {
语句3;
}
……
else if(表达式N) {
语句N;
}
语义:如果表达式1为真,执行语句1,否则执行表达式2,如果表达式2为真
执行语句2,否则一次类推向下判断
注意:N个语句中只能执行一个
参考代码:if2.c
/*条件分支演示*/
#include <stdio.h>
int main(void) {
//形式2演示
int a = -20;
if(a >= 0) {
printf("a是非负数.\n");
} else {
printf("a是负数.\n");
}
//形式3演示:
if(a > 0) {
printf("a是正数\n");
} else if(0 == a) {
printf("hello world\n");
} else if(a < 0) {
printf("a是负数\n");
}
return 0;
}
案例:薪资和回家交通工具的选择
薪资 | 交通工具 |
---|---|
大于1000000 | 坐导弹 |
大于100000 | 坐飞机 |
大于10000 | 坐火车 |
等于0 | 走路 |
参考代码:if3.c
/*案例:薪资和交通工具*/
#include <stdio.h>
int main(void) {
int slary = 9999;
if(slary > 1000000) {
printf("我的回家方式是坐导弹\n");
} else if(slary > 100000) {
printf("我的回家方式是坐飞机\n");
} else if(slary > 10000) {
printf("我的回家方式是坐火车\n");
} else if(slary> 1000) {
printf("我的薪资是%d,所以我回家方式是走路\n", slary);
}
else if(slary = 0) {
printf("我的回家方式是走路\n");
}
return 0;
}
形式4:
if(表达式1) {
语句1;
} else if(表达式2) {
语句2;
} else if(表达式3) {
语句3;
}
……
else if(表达式N) {
语句N;
} else {
语句M;
}
语义:如果表达式1为真,执行语句1,否则执行表达式2,如果表达式2为真
执行语句2,否则一次类推向下判断,如果前面N个判断都不成立,最后
执行else执行的语句M
参考代码:if3.c
17 } else {
18 printf("不回家,加班还钱\n");
19 }
20 return 0;
2.4 条件分支只要注意事项
-
if和else配对原则:else或者else if和最近的if配对
-
如果语句只有一条,{}是可以省略的(由衷建议加上去)
if(1 == a) { pritnf("a=1\n"); } 等价于 if(1 == a) printf("a=1\n");
-
{}是否独占一行问题:根据公司的编码规范来定
linux操作系统程序员,花括号不会独占一行
很多公司的编码规范,花括号独占一行
案例:利用if…else判断三个数中的最小数
例如:int a = 2, b = 3, c = 1;
参考代码:min.c
/*求最小数演示*/
#include <stdio.h>
int main(void)
{
int a = 2, b = 3, c = 1;
if(a > b) { // 最小数在b和c之间
if(b > c) {
printf("最小数是c=%d\n", c);
} else {
printf("最小数b=%d\n", b);
}
} else { // b>a
if (a > c) {
printf("最小数是c=%d\n", c);
} else {
printf("最小数是a=%d\n", a);
}
}
// 版本2
if(a > b && b > c) {
printf("最小数是c=%d\n",c);
} else if (b > a && c > a) {
printf("最小数是a=%d\n",a);
} else if (a > b && c > b) {
printf("最小数是b=%d\n",b);
}
return 0;
}
2.5 开关分支语法:
switch(控制表达式) {
case 常量表达式1;
语句1;
break;
case 常量表达式2;
语句2;
break;
……
case 常量表达式N;
语句N;
break;
default:
语句M;
break;
}
执行流程:控制表达式的值和下面case对应的常量表达式的值一样(相等)
那么就会执行对应的语句,一旦遇到break,switch…case开关分支立刻结束
如果case都不满足,最后只能执行default对应的语句
参考代码:switch.c
/*switch...case演示*/
#include <stdio.h>
int main(void) {
int a = 200; //1.春天 2.夏天 3.秋天 4.冬天
switch(a) {
case 1:
printf("春天\n");
printf("春意盎然\n");
break;
case 2:
printf("夏天\n");
printf("生机勃勃\n");
break;
case 3:
printf("秋天\n");
printf("秋高气爽\n");
break;
case 4:
printf("冬天\n");
printf("白雪皑皑\n");
break;
default:
printf("你输入的数字不对\n");
break;
}
return 0;
}
2.6 开关分支使用注意事项
1.控制表达式被当成一个整数(int),可以是字符(本质还是单字节整数)
但不能是浮点数或者字符串(双引号括起来的:例如:“abc”)
例如:
int a = 4;
switch(a) // 可以
float b = 3.3;
switch(b) //不可以
switch("abc") //不可以
char c = 'c';
switch(c) //可以
2.常量表达式必须是常量,不能是变量,例如:‘a’, 5, 2 + 3等,不允许有重复的case分支
int cmd = 250;
switch(cmd) {
case 250;
break;
case 250; //gcc编译器报错
break;
}
3.如果其中某个case对应的break没有添加,当这个case成立时,并且运行其语句之后
会继续执行下一个case对应的所有语句,直到遇见break,switch…case才能结束
4.case分支或者default分支中如果定义变量,记得加{},并且这个变量只能在这个花括号内使用,出来花括号用不了
例如:
int cmd = 10;
switch(cmd) {
case 10:
{
char c = 'a'; // 定义字符变量
printf("%c\n", c);
break;
}
}
printf("%c\n",c); // gcc报错
参考代码:switch2.c
/*switch...case注意事项*/
#include <stdio.h>
int main(void) {
/* 异常情形
switch(1.0){ //不允许是浮点数
case 1.0;
break;
}
switch("abc"){ //不允许是字符串
case "abc";
break;
}
int a = 250;
switch(a) {
case 250;
break;
case 250;
break; // 不允许重复
} */
// 没有添加break的情形
int a =250;
switch(a) {
case 250:
printf("250\n");
case 251:
{
int b = 251; //定义一个变量
printf("251\n");
}
// printf("%d\n",b); // 报错提示未定义
case 252:
printf("252\n");
break;
default:
printf("default\n");
break;
}
return 0;
}
案例:用switch…case打印学生分数对应的等级
分数 | 等级 |
---|---|
90-100 | A |
80-89 | B |
70-79 | C |
60-69 | D |
60以下 | E |
3.总结:if…else和switch…case对比
-
switch…case能做的,if…else都能做
-
if…else能做的,switch…case不一定能做,例如:浮点数和字符串
-
switch…case某些场合使用极其繁琐
例如:判断0-1000的范围,如果用switch…case实现起来极其繁琐
但是if…else一条语句拿下:if(xxx > 0 && xxx < 1000)
-
用switch…case实现的代码经过gcc编译器翻译的可执行程序代码比if…else要少,所以switch…case代码执行效率高
4. C语言三大结构之循环结构
4.1 循环结构功能
将一组语句重复多次执行
4.2 循环结构三大类
for 循环,while循环,do…while循环
4.3 for循环
4.3.1 语法格式
for(表达式1; 表达式2; 表达式3) {
循环语句4;
}
执行流程:
-
首先执行表达式1(只做一次,一般做初始化的工作)
-
然后执行表达式2
如果表达式2的结果为真,那么就去执行循环语句
如果表达式2的结果为假,那么for循环立刻结束(花括号结束了)
-
如果表达式2的结果为真,并且执行完循环语句之后,再接着执行表达式3
表达式3执行完毕,重复2, 3,直到表达式2为假,for循环退出
4.3.2 for循环的使用形式
形式1:
int i;
for(i = 0; i < 5; i++) {
printf("i = %d\n", i);
}
注意:i变量又称为循环变量
形式2:
int i = 0;
for(;i < 5; i++) {
printf("i = %d\n", i);
}
形式3:
int i = 0;
for(; i < 5;) {
printf("i = %d\n", i);
i++;
}
形式4:死循环
for(;;) {
printf("hello world");
}
注意:按ctrl+c强制退出程序
// 退出死循环
int i = 0;
for(;;) {
printf("i = %d\n", i)
i++;
// 退出条件
if(i > 5) {
break; //中断for循环,for循环结束
}
}
形式5:
int i, j;
for(i = 0, j = 0; i < 5 && j < 5; i++, j ++){
printf("i = %d\n, j = %d\n ", i, j);
}
形式6:
for (int i = 0; i < 5; i++) {// 这里的i只能用于for循环,出来for循环就不能用
printf("i = %d\n", i);
}
printf("i = %d\n", i); //gcc报错,不能访问,以上形式可以
形式7:
int i;
for(i = 0; i < 5; i++) {
if(3 == i) {
continue; // 中断结束本次循环,继续下一次循环,也就是i = 3时条件成立,执行continue关键字,后面不再执行
// 直接去执行表达式3,也就是i++,然后重复下一次循环
}
printf("i = %d\n", i);
}
参考代码:for.c
/*for循环演示*/
#include <stdio.h>
int main(void) {
//形式1
int i;
for(i = 0; i < 5; i++) {
printf("i = %d\n", i);
}
//形式2
int j = 0;
for(; j < 5; j++) {
printf("j = %d\n", j);
}
//形式3
int m = 0;
for(; m < 5;) {
printf("m = %d\n", m);
m++;
}
/* //形式4
for(;;) {
printf("hello world\n");
}*/
//退出死循环
int n = 0;
for(;;) {
printf("n = %d\n", n);
n++;
if(n > 5) {
break;
}
}
// 形式5
int h, k;
for(h = 0, k = 0; h < 5 && k < 5; h++, k++) {
printf("h = %d\n, k = %d\n", h, k);
}
// 形式6
for(int t = 0; t < 5; t++) {
printf("t = %d\n", t);
}
// printf("t = %d\n", t); 报错
//形式7
int b;
for(b = 0;b < 5; b++) {
if(3 == b) {
continue;
printf("我竟然被执行了");
}
printf("b = %d\n", b);
}
return 0;
}
案例1:利用for循环求:1+2+3+…+100的和
/*1+2+3+....+100的和*/
#include<stdio.h>
int main(void) {
int i;
int sum = 0;
for(i = 0; i <= 100; i++) {
sum += i;
}
printf("sum = %d\n", sum);
return 0;
}
案例2:利用for循环打印10行20个*
共10行,一行20个*
提示:采用for循环嵌套
for(...) { //负责打印10行
for(...){ //负责打印一行20个*
}
}
/*用for循环打印10行20个*/
#include <stdio.h>
int main(void) {
int i, j;
for(i = 0; i < 10; i++) {
for(j = 0; j < 20; j++) {
printf("*");
}
printf("\n");
}
return 0;
}
关注公众号,获取更多精彩内容