C语言基础知识:输入输出、运算符、算数运算符、赋值运算符、比较运算符、位运算符、
类型转换、控制语句 if语句、if...else语句 、三目运算符、else...if 语句、switch语句、
跳转关键字、break、continue、goto.
一、 输出和输入
1.输出
- 输出:将程序的运行结果输出到控制台或终端窗口中
int printf (const char *format, ...);
-
- format参数是一个格式化字符串,包含了输出的格式控制信息
- 后面的参数
...
是可变参数,参数(要输出的数据)长度不固定
格式化占位符
示例代码:
#include <stdio.h>
int main() {
// 定义变量
char c = 'A';
short s = 10;
int i = 10;
long l = 10;
float f = 1.123456789123456789;
double d = 1.123456789123456789;
unsigned int ui = 20;
char *name = "bill";
// 格式化输出
// 整型
printf("c=%d\n", c);
printf("s=%hd\n", s);
printf("i=%d\n", i);
printf("l=%ld\n", l);
// 浮点类型
printf("f=%.10f\n", f);
printf("d=%.20lf\n", d);
// 无符号数据
printf("u=%u\n", ui);
// 八进制和十六进制 可以加上前缀# 自动输出八进制和十六进制0或0x
printf("o=%#o\n", 120);
printf("x=%#x\n", 120);
// 字符串类型
printf("s=%s\n", name);
// 内存地址
printf("ps=%p\n", &s);
return 0;
}
2.输入
- 输入:接收用户输入的数据的过程
scanf语法格式
scanf("格式化字符串", &变量1, &变量2,...);
-
- 格式化字符串用于指定输入的数据类型及其格式
- 变量1、变量2等是要读入的变量的地址
#include <stdio.h>
int main() {
// 定义int类型变量age,通过scanf输入内容,然后打印内容
int age;
printf("请输入年龄:");
scanf("%d", &age);
printf("age = %d\n", age);
return 0;
}
示例代码:
#include <stdio.h>
int main() {
// 1.定义常量pi
const double pi = 3.1415;
// 2.定义半径变量r
double r;
// 3.输入半径
printf("请输入半径:");
scanf("%lf", &r);
// 4.求周长和面积, * 为乘以运算符
double len = 2 * pi * r;
double area = pi * r * r;
// 5.输出周长和面积
printf("周长len = %lf, 面积area = %lf\n", len, area);
return 0;
}
二、运算符
1.算术运算符
示例代码:
#include <stdio.h>
int main() {
// 2数相除,要想得到小数的结果,分子分母必须有一个数是小数
double c = 5/2; // 5, 2个都是整数,只会取整
printf("c1 = %lf\n", c);
c = 5.0/2;
printf("c2 = %lf\n", c);
c = 5/2.0;
printf("c3 = %lf\n", c);
int i = 0;
// 前置++
// 先加后用
int b = ++i;
printf("前置:b = %d, i = %d\n", b, i);
// 后置++
// 先用后加
i = 0;
b = i++;
printf("后置:b = %d, i = %d\n", b, i);
return 0;
}
运行结果:
c1 = 2.000000
c2 = 2.500000
c3 = 2.500000
前置:b = 1, i = 1
后置:b = 0, i = 1
2.赋值运算符
示例代码:
#include <stdio.h>
int main() {
int a = 10;
a += 5;
printf("a = %d\n", a);
return 0;
}
运行结果:
a = 15
3.比较运算符
C 语言的比较运算中, “真”用数字“1”来表示, “假”用数字“0”来表示。
示例代码:
#include <stdio.h>
int main() {
int a = 10;
int b = 20;
printf("%d\n", a == b);
printf("%d\n", a != b);
printf("%d\n", a > b);
printf("%d\n", a < b);
printf("%d\n", a >= b);
printf("%d\n", a <= b);
return 0;
}
运行结果:
0
1
0
1
0
1
4.逻辑运算符
示例代码:
#include <stdio.h>
int main() {
// &&(与),可以理解为并且
// 案例:请判断班长和他女朋友是否符合法定结婚年龄
int boy = 25;
int girl = 21;
int result = boy >= 22 && girl >= 20;
printf("%d\n", result);
// ||(或),可以理解为或者
// 案例:班长女朋友玩原神没有原石了,请帮班长判断是否有足够的钱
double wx_money = 100;
double alipay_money = 300;
result = wx_money >= 398 || alipay_money >= 398 || wx_money+alipay_money >= 398;
printf("%d\n", result);
// !(非),可以理解为不是
printf("%d\n", !0);
printf("%d\n", !!1);
// 短路规则
// && 左边为假,右边不执行
0 && printf("我是右边\n");
// || 左边为真,右边不执行
1 || printf("我是右边\n");
return 0;
}
5.位运算符
常见的位运算符号有&、|、^、~、>>、<<,分别代表着如下含义:
取反、左右位移运算需要在补码的基础上运算。
5.1& -(与运算)
按位与(&)运算:位与位进行比较,如果都为1,则为1,否则为0;
/**
* 按位与(&)运算:位与位进行比较,如果都为1,则为1,否则为0;
* 示例:
* 40 & 15 = 8
* 0010 1000
* & 0000 1111
* -------------------
* 0000 1000
*/
printf("40 & 15 = %d\n", 40 & 15);
5.2| -(或运算)
按位或(|)运算:位与位进行比较,如果都为0,则为0,否则为1;
/**
* 按位或(|)运算:位与位进行比较,如果都为0,则为0,否则为1;
* 示例:
* 40 | 15 = 47
* 0010 1000
* | 0000 1111
* ----------------
* 0010 1111
*/
printf("40 | 15 = %d\n", 40 | 15);
5.3^ -(异或运算)
按位异或运算:位与位进行比较,相同为0,不同为1;
/**
* 按位异或运算:位与位进行比较,相同为0,不同为1;
* 示例:
* 40 ^ 15 = 39
* 0010 1000
* ^ 0000 1111
* ------------------
* 0010 0111
*/
printf("40 ^ 15 = %d\n", 40 ^ 15);
5.4~ -(取反运算)
按位取反运算:补码取反,再将取反后的补码转为原码;
无符号的数据,取反后最高位为1,也不需要逆运算。
/**
* 按位取反运算:补码取反,再将取反后的补码转为原码。
* 1、正数取反:由于正数的原码和补码是相同的,取反的方式简便了些。
* 补码(原码) -> 取反 -> 补码逆运算 -> 反码逆运算(符号位不变) -> 取反后的原码
* 2、负数取反:
* 原码 -> 反码 -> 补码 -> 取反 -> 取反后的补码即原码
* 示例:
* 原码(补码) 取反的补码 补码逆运算-1 反码逆运算
* ~40 = 0010 1000 -> 1101 0111 -> 1101 0110 -> 1010 1001 = -41
*
* 原码(补码) 取反的补码 补码逆运算-1 反码逆运算
* ~15 = 0000 1111 -> 1111 0000 -> 1110 1111 -> 1001 0000 = -16
*
* 原码 反码 补码 取反
* ~-15 = 1000 1111 -> 1111 0000 -> 1111 0001 -> 0000 1110 = 14
*/
printf("~40 = %d\n", ~40);
printf("~15 = %d\n", ~15);
printf("~-15 = %d\n", ~(-15));
5.5<< -(左移运算符)
将数字的二进制补码全部向左移动,空出来的位置补0,超出范围的二进制数丢弃;
有符号的数据左移后最高位如果为1,则需要进行逆运算;
注意事项:
- 无符号的数据,左移后最高位为1,也不需要逆运算;
- -128:1000 0000 特殊情况也不需要逆运算;
/**
* 示例:
* 40 << 4 = 0010 1000 << 4 = 1000 0000 = -128 (特殊的不需要逆运算)
* 41 << 4 = 0010 1001 << 4 = 1001 0000 = 1000 1111 = 1111 0000 = -112
* 7 6 5 4 3 2 1 0
* 1 0 0 1 0 0 0 0
*/
int8_t p = 40;
p <<= 4; // p = p << 4;
printf("40 << 4 = %d\n", p);
5.6>> -(右移运算符)
将数字的二进制补码全部向右移动,空出来的位置补什么,取决于原来的最高位是什么。原来的最高是1就补1, 原来的最高位是0 就补0 。也可以转化成这样的一句话: 正数补0, 负数补1;
/*
23: 0001 0111【原码】 ---- 0001 0111【反码】 ---- 0001 0111 【补码】
>> 2
-----------------------------------------------
0000 0101【补码】 ---> 5
*/
printf(" 23 >> 2 = %d \n" , 23 >> 2) ;
/*
123: 1001 0111【原码】 ---- 1110 1000【反码】---- 1110 1001【补码】
>> 2
-----------------------------------------------
1111 1010【补码】 ---> 1111 1001【反码】- ----- 1000 0110 【原码】===> -6
*/
printf(" -23 >> 2 = %d \n" , -23 >> 2) ;
示例代码:
#include <stdio.h>
#include <inttypes.h>
int main() {
uint8_t a = 3; // 0000 0011
uint8_t b = 10; // 0000 1010
// 打印显示2个字符,个数不够,左边补0
printf("%02x\n", a & b); // 0000 0010,16进制为02
printf("%02x\n", a | b); // 0000 1011,16进制为0b
printf("%02x\n", a ^ b); // 0000 1001,16进制为09
uint8_t c = 10; // 0000 1010
uint8_t temp = ~c; // 1111 0101
printf("%02x\n", temp); // 1111 0101,16进制为f5
printf("%02x\n", c << 1); // 0001 0100,16进制为14
printf("%02x\n", c >> 1); // 0000 0101,16进制为05
return 0;
}
运行结果:
02
0b
09
f5
14
05
案例需求:
// 将变量a的第2位设置为1,其他位保持不变
uint8_t a = 0b10110011; // 0xb3;
// 将变量b的第2位、第6位设置为1,其他位保持不变
uint8_t b = 0b10110011; // 0xb3;
// 将变量c的第5位设置为0,其他位保持不变
uint8_t c = 0b10110011; // 0xb3;
// 将变量d的第0~3位设置为0,其他位保持不变
uint8_t d = 0b11111111; // 0xff;
// 将变量e的第2位取反,其他位保持不变
uint8_t e = 0b10110011; // 0xb3;
// 将变量f取出8-15位
uint32_t f = 0x12345678;
示例代码:
#include <stdio.h>
#include <inttypes.h>
int main() {
// 将变量a的第2位设置为1,其他位保持不变
uint8_t a = 0b10110011; // 0xb3;
a |= (1 << 2); // 或者 x = x | (1 << 2);
printf("%02x\n", a); // b7, 10110111
// 将变量b的第2位、第6位设置为1,其他位保持不变
uint8_t b = 0b10110011; // 0xb3;
b |= (1 << 2 | 1 << 6);
printf("%02x\n", b); // f7,11110111
// 将变量c的第5位设置为0,其他位保持不变
uint8_t c = 0b10110011; // 0xb3;
c &= ~(1 << 5);
printf("%02x\n", c); // 93,10010011
// 将变量d的第0~3位设置为0,其他位保持不变
uint8_t d = 0b11111111; // 0xff;
d &= ~(1 << 0 | 1 << 1 | 1 << 2 | 1 << 3);
printf("%02x\n", d); // f0,11110000
// 将变量e的第2位取反,其他位保持不变
uint8_t e = 0b10110011; // 0xb3;
e ^= (1 << 2);
printf("%02x\n", e); // b7, 10110111
// 将变量f取出8-15位
uint32_t f = 0x12345678;
uint32_t temp = (f & 0x0000ff00) >> 8;
printf("%#x\n", temp);
return 0;
}
6.运算符优先级
- 不同的运算符默认具备不同的优先级,符号较多不用记,现用现查就可以。
- 当无法确定谁的优先级高时,加一个小括号就解决了。
三、类型转换
数据有不同的类型,不同类型数据之间进行混合运算时涉及到类型的转换问题。
●转换的方法有两种:
○自动转换(隐式转换):遵循一定的规则,由编译系统自动完成
○强制类型转换:把表达式的运算结果强制转换成所需的数据类型
■语法格式: (类型)变量或常量
●类型转换的原则:
○占用内存字节数少(值域小)的类型,向占用内存字节数多(值域大)的类型转换,以保证精度不降低。
#include <stdio.h>
int main() {
// 隐式转换(自动转换):编译器自动转换
int a = 11;
double b = a; //将a的值,11,转换为11.0000,再给b赋值
printf("b = %lf\n", b);
printf("a = %d\n", a);
// 强制类型转换,用户转换, (类型)变量或常量
int m = 3;
int n = 2;
b = (double)m/n;
printf("b = %lf\n", b);
b = (double)3/2;
printf("b = %lf\n", b);
// 类型转换原则:数据类型小的往数据类型大的转
int c = 100;
char d = (char)c; //没有问题
printf("d = %d\n", d);
// 大的往小的转,数据可能会丢失
c = 129;
d = (char)c;
printf("d = %d\n", d);
// 小的往大的转
long long big = (int)c;
printf("big = %lld\n", big);
return 0;
}
运行结果:
b = 11.000000
a = 11
b = 1.500000
b = 1.500000
d = 100
d = -127
big = 129
四、控制语句
1.程序执行的三大流程
- 顺序 : 从上向下, 顺序执行代码
- 分支 : 根据条件判断, 决定执行代码的分支
- 循环 : 让特定代码重复的执行
2.分支语句
条件语句用来根据不同的条件来执行不同的语句,C语言中常用的条件语句包括if语句和switch语句。
2.1 if 语句
语法格式:
if (条件) {
条件成立时,要做的事
……
}
案例需求:
- 定义一个整数变量记录年龄
- 判断是否满 18 岁 (>=)
- 如果满 18 岁,允许进网吧嗨皮
示例代码:
#include <stdio.h>
int main() {
// 定义一个整数变量记录年龄
int age;
printf("请输入年龄:");
scanf("%d", &age);
// 判断是否满 18 岁 (>=)
if (age >= 18) {
// 如果满 18 岁,允许进网吧嗨皮
printf("允许进网吧嗨皮\n");
}
return 0;
}
2.2 if ......else 语句
语法格式:
if (条件) {
条件成立时,要做的事
……
} else {
条件不成立时,要做的事
……
}
案例需求:
- 定义一个整数变量记录年龄
- 判断是否满 18 岁 (>=)
- 如果满 18 岁,允许进网吧嗨皮
- 否则,提示回家写作业
示例代码:
#include <stdio.h>
int main() {
// 定义一个整数变量记录年龄
int age;
printf("请输入年龄:");
scanf("%d", &age);
// 判断是否满 18 岁 (>=)
if (age >= 18) {
// 如果满 18 岁,允许进网吧嗨皮
printf("允许进网吧嗨皮\n");
} else {
// 否则,提示回家写作业
printf("回家写作业\n");
}
return 0;
}
2.3三目运算符
示例代码:
#include <stdio.h>
int main() {
// 通过三目运算符求2个数的最大值
int a = 10;
int b = 20;
int c = a > b ? a : b;
printf("最大值:%d\n", c);
return 0;
}
2.4if…else if…else语句
语法格式:
if (条件1) {
条件1成立时,要做的事
……
} else if(条件2) {
条件2成立时,要做的事
……
} else {
条件不成立时,要做的事
……
}
案例需求:
- 天猫超市双 11 推出以下优惠促销活动:
-
- 购物满 50 元,打 9 折;
-
- 购物满 100 元,打 8 折;
- 购物满 200 元,打 7 折;
- 购物满 300 元,打 6 折;
- 编程计算当购物满 s 元时,实际付费多少?
示例代码:
#include <stdio.h>
int main() {
// 1.定义变量记录购买金额 定义变量记录实际费用
float money, real_money;
// 2.输入购买金额
printf("请输入购买金额:");
scanf("%f", &money);
// 3.根据购买金额判断折扣
if (money >= 50 && money < 100) {
// 购物满 50 元,打 9 折;
real_money = money * 0.9;
} else if (money >= 100 && money < 200) {
// 购物满 100 元,打 8 折;
real_money = money * 0.8;
} else if (money >= 200 && money < 300) {
// 购物满 200 元,打 7 折;
real_money = money * 0.7;
} else if (money >= 300) {
// 购物满 300 元,打 6 折;
real_money = money * 0.6;
} else {
// 不满50 原价
real_money = money;
}
printf("购买金额: %f 实际价格: %f\n", money, real_money);
return 0;
}
优化后代码:
#include <stdio.h>
int main()
{
// 1.定义变量记录购买金额 定义变量记录实际费用
float money, real_money;
// 2.输入购买金额
printf("请输入购买金额:");
scanf("%f", &money);
// 3.根据购买金额判断折扣
if (money >= 300) {
// 购物满 300 元,打 6 折;
real_money = money * 0.6;
} else if (money >= 200) {
// 购物满 200 元,打 7 折;
real_money = money * 0.7;
} else if (money >= 100) {
// 购物满 100 元,打 8 折;
real_money = money * 0.8;
} else if (money >= 50) {
// 购物满 50 元,打 9 折;
real_money = money * 0.9;
} else {
// 不满50 原价
real_money = money;
}
printf("购买金额: %f 实际价格: %f\n", money, real_money);
return 0;
}
2.5switch语句
- 测试一个表达式是否等于一些可能的值,并根据表达式的值执行相应的代码块,可以使用switch语言实现
- switch可以支持数据类型:
-
- int
- 枚举类型
- char类型
- switch和if区别:
-
- 需要根据布尔条件来执行不同的代码块,则应使用if语句
- 需要根据表达式的值来执行不同的代码块,则应使用switch语句
语法格式:
switch (expression) {
case value1:
// 代码块1
break;
case value2:
// 代码块2
break;
default:
// 代码块3
}
案例需求:
- 输入int类型的数据,根据结果输出不同的内容
-
- 输入1:输出Monday
- 输入2:输出Tuesday
- 输入3:输出Wednesday
- 输入4:输出Thursday
- 输入5:输出Friday
- 输入6:输出Saturday
- 输入7:输出Sunday
- 输入其它:输出error
示例代码:
#include <stdio.h>
int main() {
// 1. 定义变量保存数据
int i;
// 2. 输入数据
printf("请输入1~7的数字:");
scanf("%d", &i);
// 3. 使用switch根据数据不同输出不同的结果
switch (i) {
case 1:
printf("Monday\n");
break;
case 2:
printf("Tuesday\n");
break;
case 3:
printf("Wednesday\n");
break;
case 4:
printf("Thursday\n");
break;
case 5:
printf("Friday\n");
break;
case 6:
printf("Saturday\n");
break;
case 7:
printf("Sunday\n");
break;
default:
printf("error\n");
}
return 0;
}
2.6分支综合案例
案例需求:
- 输入:年份(整数)和月份(整数)
- 输出:该月份的天数(整数)
思路分析:
1.定义变量保存年份、月份、天数
2.输入年份和月份
3.根据月份输出天数
1、3、5、7、8、10、12月 31天
4、6、9、11月 30天
2月 非闰年 28天 闰年 29天
闰年判断:能被4整除,但不能被100整除的;或者能被400整除的年份
示例代码:
#include <stdio.h>
int main() {
// 1.定义变量保存年份、月份、天数
int year, month, day;
// 2.输入年份和月份
scanf("%d %d", &year, &month);
// 3.根据月份输出天数
switch (month) {
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
day = 31;
break;
case 4:
case 6:
case 9:
case 11:
day = 30;
break;
case 2:
// 2月 非闰年 28天 闰年 29天
// 闰年判断:能被4整除,但不能被100整除的;或者能被400整除的年份
if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) {
day = 29;
} else {
day = 28;
}
break;
default:
day = -1;
}
printf("%d 年 %d 月 有 %d 天\n", year, month, day);
return 0;
}
3.循环语句
- 什么是循环
-
- 重复执行代码
- 为什么需要循环
- 循环的实现方式
-
- while
- do...while
- for
1.while语句
语法格式:
while (条件) {
循环体
……
}
需求:跑步5圈
示例代码:
#include <stdio.h>
int main() {
// 需求跑步5圈
// 1. 条件变量的定义
int i = 1;
// 2. while 控制重复的次数
while (i <= 5) {
// 3. 打印跑步第几圈
printf("跑步第 %d 圈\n", i);
// 4. 条件改变
i++;
}
return 0;
}
2.do...while语句
语法格式:
do {
循环体
……
} while (条件);
- do-while 循环语句是在执行循环体之后才检查 条件 表达式的值
- 所以 do-while 语句的循环体至少执行一次,do…while也被称为直到型循环
需求:跑步5圈
示例代码:
#include <stdio.h>
int main() {
// 需求跑步5圈
// 1. 条件变量的定义
int i = 1;
do {
// 2.1 打印跑步第几圈
printf("跑步第 %d 圈\n", i);
// 2.2 条件改变
i++;
} while ( i <= 5 ); // 3. 控制重复的次数
return 0;
}
3.for语句
语法格式:
for ( init; condition; increment ) {
循环体
}
- init会首先被执行,且只会执行一次
- 接下来,会判断 condition,如果条件condition为真
- 在执行完 for 循环主体后,控制流会跳回上面的 increment 语句
- 条件再次被判断
需求:跑步5圈
示例代码:
#include <stdio.h>
int main() {
// 需求跑步5圈
for (int i = 1; i <= 5; i++) {
printf("跑步第 %d 圈\n", i);
}
return 0;
}
4.死循环
- 条件永远为真的循环就是死循环
- 示例代码
#include <stdio.h>
int main() {
// while (1) {
// printf("根本停不下来\n");
// }
for(;;) {
printf("根本停不下来\n");
}
return 0;
}
5.循环案例
实现1+2+3……100所有数字的累加
#include <stdio.h>
int main() {
// 需求:实现1+2+3……100所有数字的累加
// 定义条件变量初始值为1
int i = 1;
// 定义累加变量,初始值为0
int sum = 0;
// 循环控制100次
while (i <= 100) {
// 累加
sum += i;
// 条件改变
i++;
}
// 循环外面,打印最终的累加和
printf("sum = %d\n", sum);
// for循环实现
int temp = 0;
for (int i = 1; i <= 100; i++) {
temp += i;
}
printf("temp = %d\n", temp);
return 0;
}
6.循环嵌套
需求如下:
通过for循环嵌套打印内容:
* * * * *
* * * * *
* * * * *
* * * * *
* * * * *
说明:
1)每行有5个*,总共有5行
2)*之间有空格隔开
3)printf()1次只能打印1个*
示例代码:
#include <stdio.h>
int main() {
for (int i = 0; i <= 4; i++) {
for (int j = 0; j <= 4; j++) {
printf("* ");
}
printf("\n");
}
return 0;
}
4.跳转关键字
- 循环和switch专属的跳转:break
- 循环专属的跳转:continue
- 无条件跳转:goto
1.break
- 循环的break说明
-
- 某一条件满足时,不再执行循环体中后续重复的代码,并退出循环
- 需求:一共吃5碗饭, 吃到第3碗吃饱了, 结束吃饭动作
示例代码:
#include <stdio.h> int main() { // 需求: 一共吃5碗饭, 吃到第3碗吃饱了, 结束吃饭动作 // 1. 定义条件变量,为了查看方便, 计数器从 1 开始 int i = 1; while (i <= 5) { // 2. 控制条件 if (i == 3) { printf("吃饱了,不吃了\n"); break; // 结束循环,退出循环 } printf("吃第 %d 碗饭\n", i); // 3. 条件变量改变 i++; } return 0; }
2.continue
- 某一条件满足时,不再执行本次循环体中后续重复的代码,但进入下一次循环判断
-
- while循环,continue 之前一定要修改计数器(条件变量),否则,导致死循环
- 需求:一共吃5个苹果,吃到第3个遇到虫子,这个跳过不吃,下一个继续
示例代码:
#include <stdio.h>
int main() {
// 需求: 一共吃5个苹果,吃到第3个遇到虫子,这个跳过不吃,下一个继续
// 1. 定义条件变量,为了查看方便, 计数器从 1 开始
int i = 1;
while (i <= 5) { // 2. 控制条件
if (i == 3) {
printf("这个有虫子,不吃了\n");
i++; // continue 之前一定要修改计数器(条件变量),否则,导致死循环
continue;; // 跳过本次循环,下次继续
}
printf("吃第 %d 个苹果\n", i);
// 3. 条件变量改变
i++;
}
return 0;
}
3.goto
- goto用于无条件跳转
-
- 在一种情况下可以使用goto语句:从一组嵌套的循环中跳出
- goto语句可以导致代码不易理解和维护,并且容易引入不必要的错误。因此,除非必要,最好不要使用goto语句
示例代码:
#include <stdio.h>
int main() {
int i = 0;
while (i < 3) {
if(i == 1) {
goto End; // 跳转到End标签
}
printf("i = %d\n", i);
i++;
}
End:
printf("this is ending!\n");
return 0;
}