# 1 基本概述🎄
# C 语言特点🌳
- 优点
- 代码量小
- 执行速度快
- 功能强大
- 编程自由
- 缺点
- 写代码实现周期长
- 可移植性较差
- 过于自由,经验不足易出错
- 对平台库依赖较多
# 1 编写 C 语言代码🌳
#include <stdio.h> | |
int main(void) | |
{ | |
// 这是第一个 C 语言代码 | |
printf("hello world"); | |
return 0; | |
} |
C 语言的源代码文件是一个普通的文本文件,但扩展名必须是 .c
。
# 1.1 代码分析🌲
# 1.1.1 include 头文件包含🌴
#include
的意思是头文件包含,#include
<stdio.h> 代表包含stdio.h
这个头文件- 使用 C 语言库函数需要提前包含库函数对应的头文件,如这里使用了 printf () 函数,需要包含 stdio.h 头文件。
- 可以通过 man 3 printf 查看 printf 所需的头文件。
#include<>
与 #include ""
的区别:
<>
表示系统直接按系统指定的目录检索“”
表示系统先在“”
指定的路径 (没写路径代表当前路径) 查找头文件,如果找不到,再按系统指定的目录检索
# 1.2 通过 gcc 编译.c 代码文件🌲
# 1.2.1 一步编译🌴
比如要编译的 文件为: 代码文件.c
格式: gcc -o 代码文件.exe[空格]代码文件.c
前面我们先将这个代码文件的后缀改为编译后的后缀名 ,后面就是要编译的源文件。
也可以一次编译多个 .c
文件,格式: gcc -o hello.exe hello1.c hello2.c
<font color='red'> 注意 </font>:如果编译时代码中的 for 循环中 int 定义在了 for () 里面编译时会出现 c99 的错误,因为这种写法在 c99 中不支持 编译时需要加上
gcc -o hello.exe hello1.c hello2.c -std=c99
即可
<font color='red'> 注意 </font>:编译文件与源代码文件之间使用空格 隔开。
<img src="https://raw.githubusercontent.com/PigPigLetsGo/imeages/master/202309210744701.gif" alt="test" style="zoom:50%;" />
# 2 System 函数🌳
编写格式:
#include <stdlib.h> | |
int system(const char *command); | |
---------------------------------------- | |
功能:在已经运行的程序中执行另外一个外部程序 | |
参数:外部可执行程序名 | |
返回值 | |
成功:0 | |
失败:任意数字 |
简单代码演示:
#include <stdlib.h> | |
int main(void) | |
{ | |
//cmd 命令 执行 calc 打开计算机 | |
system("calc"); | |
//system 可以打开一个外部的应用程序,如果带 路径需要使用 \ 进行转移所以要写两个 \ 或者使用一个 / | |
// 如下是启动程序后打开了电脑中的酷我音乐软件 | |
//system ("D:\\ 酷我音乐 \\kuwo\\kuwomusic\\9.2.0.0_W6\\KwMusic.exe"); | |
//system ("D:/ 酷我音乐 /kuwo/kuwomusic/9.2.0.0_W6/KwMusic.exe"); | |
// 执行 cmd 的关机命令 | |
system("shutdown -s -t 0"); | |
return 0; | |
} |
运行就会打开系统的计算机然后就关机了。。。
# 3.1 C 语言编译过程🌳
# 3.1.2 通过 gcc 编译 C 代码🌲
# 3.1.2.1 gcc 编译器介绍🌴
编辑器 (如 vi,记事本) 是指我们用它来写程序的 (编辑代码) ,而我们写的代码语句,电脑是不懂的,我们需要把它转换成电脑能懂的语句,编译器就是这样的转化工具。就是说,== 我们用编辑器编写程序,由编辑器编译后才可以让电脑运行!== 编辑器是易于编写,阅读和维护的高级计算机语言翻译为计算机能解读,运行的低级机器语言的程序。
gcc (GUN Compliler Collection,GUN 编译器套件) ,是由 GUN 开发的编程语言编译器。gcc 原本作为 GUN 操作系统的官方编译器,现已被大多数类 Unix 操作系统 (如 Linux,BSD,Mac OS X 等) 采纳为标准的编译器,gcc 同样适用于微软的 Windows
gcc 最初用于编译 C 语言,随着项目的发展 gcc 已经成为了能够编译 C,C++,Java,Ada,fortran,Object C,Object C++,Go 语言的编译器大家族。
编译命令格式:
gcc [-optionl] ... <filename>
g++ [-optionl] ... <filename>
- 命令,选项和源文件之间使用空格分隔
- 一行命令中可以有零个,一个或多个选项
- 文件名可以包含文件的绝对路径,也可以使用相对路径
- 如果命令中不包含输出可执行文件的文件名,可执行文件的文件名会自动生成一个默认名,Linux 平台为 a.out,Windows 平台为 a.exe
# 3.1.3 C 程序编译步骤🌲
C 代码编译成可执行程序经过 4 步:
- <span alt='solid'> 预处理 </span>:宏定义展开,头文件展开,条件编译等,同时将代码中的注释删除,这里并不会检查语法.
- <span alt='solid'> 编 译 </span>:检查语法,将预处理后文件编译生成汇编文件.
- <span alt='solid'> 汇 编 </span>:将汇编文件生成目标文件 (二进制文件)
- <span alt='solid'> 链 接 </span>:C 语言写的程序是需要依赖各种库的,所以编译之后还需要把库链接到最终的可执行程序中去
# 3.1.4 gcc 编译过程🌲
# 1 分步编译🌴
预处理: gcc -E hello.c -o hello.i
编 译: gcc -S hello.i -o hello.s
汇 编: gcc -c hello.s -o hello.o
链 接: gcc hello.o -o hello/hello.exe
不写 .exe
也是可执行程序 / 表示 或者
gcc,g++ 编译常用选项说明:
选项 | 含义 |
---|---|
-E (大写) | 只进行预处理 |
-S (大写) | 只进行预处理和编译 |
-c (小写) | 只进行预处理,编译和汇编 |
-o file | 指定生成的输出文件名为 file |
文件后缀 | 含义 |
-------- | ------------------- |
.c | C 语言文件 |
.i | 预处理后的 C 语言文件 |
.s | 编译后的汇编文件 |
.o | 编译后的目标文件 |
# 4 程序执行过程🌳
# 5 查找程序所依赖的动态库🌳
<span alt='solid'> 操作系统问题 </span>:操作系统之间的库内容是不同的,所以可移植性比较差,在 windows 中编写的程序在 linux 上运行不了,但是如果将源代码传递到 linux 上去运行的话是可以运行的。
<span alt='solid'> 兼容性问题 </span>:windows32,windows64 之间,高版本兼容低版本。而低版本不兼容高版本 (向下兼容)
使用 windows 平台下的软件:Depends.exe 如下图所示:
官网下载地址:https://dependencywalker.com/
# 6 CPU 内部结构与寄存器🌳
# 6.1 64 位和 32 位系统区别🌲
- 寄存器是 CPU 内部最基本的存储单元.
- CPU 对外是通过总线 (<font color='red'> 地址 </font>,<font color='red'> 控制 </font>,<font color='red'> 数据 </font>) 来和外部设备交互的,<font color='red'> 总线的宽度是 8 位,同时 CPU 的寄存器也是 8 位 </font>,那么这个 CPU 就叫 8 位 CPU
- 如果总线是 32 位,寄存器也是 32 位的,那么这个 CPU 就是 32 位 CPU
- 有一种 CPU 内部的寄存器是 32 位的,但是总线是 16 位,准 32 位 CPU
- 所有的 64 位 CPU 兼容 32 位的指令,32 位要兼容 16 位的指令,所以在 64 位的 CPU 上是可以识别 32 位的指令
- 在 64 位的 CPU 架构上运行了 64 位的软件操作系统,那么这个系统是 64 位
- 在 64 位的 CPU 架构上,运行了 32 位的软件操作系统,那么这个系统就是 32 位
# 6.2 寄存器名字🌲
8 位 | 16 位 | 32 位 | 64 位 |
---|---|---|---|
A | AX | EAX | RAX |
B | BX | EBX | RBX |
C | CX | ECX | RCX |
D | DX | EDX | RDX |
# 6.3 寄存器,缓存,内存 三者关系🌲
按与 CPU 远近来分,离得最近的是寄存器,然后缓存 (CPU 缓存) ,最后内存。
<font color='red'>CPU 计算时 </font>,预先把要用的数据从硬盘读到内存,然后再把即将要用的数据读到寄存器。于是 CPU <---->
寄存器 <---->
内存,这就是它们之间的信息交换。
那为什么有缓存呢?
因为如果经常操作内存中的同一地址的数据,就会影响速度。于是就在寄存器与内存之间设置一个缓存。
由此可以看出,从远近来看:CPU <---->
寄存器 <---->
缓存 <---->
内存。
# 7 汇编语言🌳
# 7.1 C 语言中嵌套汇编代码🌲
#include <stdio.h> | |
int main(void) | |
{ | |
/*int a = 10; | |
int b = 20; | |
int c = a + b;*/ | |
// 定义三个变量 | |
int a; | |
int b; | |
int c; | |
//__asm:表示编译后的.s 汇编文件 | |
__asm | |
{ | |
// 将 10 存储到变量 a | |
mov a, 10 | |
// 将 20 存储到变量 b | |
mov b, 20 | |
// 由于计算需要使用到寄存器所以将 a 变量存储到 32 位的寄存器中 | |
mov eax, a | |
// 再将 b 添加到寄存器中与 a 数值进行相加 | |
add eax, b | |
// 将寄存器中计算的结果存储到 c 变量中 | |
mov c, eax | |
} | |
printf("%d\n", c); | |
return 0; | |
} |
# 8 VS2013 的 C4996 错误 scanf🌳
由于微软在 VS2013 中不建议使用 C 的传统库函数 scanf,strcpy,sprintf 等,所以直接使用这些函数会提示 C4996 错误;
VS 建议采用带_s 的函数,如 scanf_s,strcpy_s,但这些并不是标准 C 函数。
要想继续使用此函数,需要在源文件中添加以下指令就可以比避免这个错误提示:
#define _CRT_SECURE_NO_WARNINGS // 这个宏定义最好要放到.c 文件的第一行否则无效果 | |
// #pragma warning (disable:4996) // 或者使用这个 | |
#include <stdio.h> | |
int main(void) | |
{ | |
int a = 0; | |
// 通过键盘输入赋值 | |
// & 运算符,表示取地址运算符,得到 a 变量的地址然后进行赋值 | |
scanf("%d", &a); | |
printf("%d\n", a); | |
return 0; | |
} |
# 2 数据类型🎄
# 2.1 常量与变量🌳
# 2.1.1 关键字🌲
C 的关键字共有 32 个
- 数据类型关键字 (12 个)
char,short,int,long (long long 在 C99 库中才有),float,double,unsigned,signed,struct,union,enum,void
- 控制语句关键字 (12 个)
if,else,switch,case,default,for,do,while,break,continue,goto,return
- 存储类关键字 (5 个)
auto,extern,register,static,const
- 其它关键字 (3 个)
sizeof,typedef,volatile
# 2.1.2 数据类型🌲
数据类型的作用:编译器预算对象 (变量) 分配的内存空间大小。
# 2.1.3 常量🌲
常量:
- 在程序运行过程中,其值不能被改变的量
- 常量一般出现在表达式或赋值语句中
整型常量 | 100,200,-100,0 |
---|---|
实型常量 | 3.14,0.125,-3.123 |
字符型常量 | ‘a’,‘b’,‘1’,‘\n’ |
字符串常量 | ‘a’,‘ab’,’123456 |
定义常量方式:
const 数据类型 常量名 = 值 (不安全) | |
#define 常量名 值 |
代码演示:
#include <stdio.h> | |
// 宏定义常量 | |
#define PI 3.141596f | |
#define R 3.4f | |
int main03(void) | |
{ | |
// 常量浮点数据类型 | |
//const float r = 3.4f; //(不安全) 建议使用宏定义常量这么写不安全 | |
// 宏定义常量的末尾如果加上了分号则会如下进行计算 | |
// PI; * r * r; 显然这样做会报错的,所以宏定义的变量末尾不要带上分号 (;) | |
// 在定义局部变量时可以在数据类型前加上修饰符 auto 表示为局部变量,也可以不加 | |
auto float s = PI * R * R; | |
float l = 2 * PI * R; | |
// 占位符 % f 表示输出 一个浮点类型 float 默认保留六位小数 会四舍五入 | |
printf("圆的面积:%.2f\n", s); | |
printf("圆的周长:%.2f\n", l); | |
return 0; | |
} | |
//------------------------ 运行结果 ------------------------ | |
圆的面积:36.32 | |
圆的周长:21.36 | |
E:\C\Demo\Project3\Project2\Debug\Project2.exe (进程 19636)已退出,代码为 0。 | |
按任意键关闭此窗口. . . |
# 2.1.4 变量🌲
变量:
- 在程序运行过程中,其值可以改变
- 变量在使用前必须先定义,定义变量前必须有相应的数据类型.
标识符命名规则:
- 标识符不能是关键字
- 标识符只能由字母,数字,下划线组成
- 标识符中字母区分大小写
变量特点:
- 变量在编译时为其分配相应的内存空间
- 可以通过其名字和地址访问相应内存
定义和声明的区别:
为变量分配地址和存储空间的称为定义,不分配地址的称为声明。
变量定义:用于为变量分配存储空间,还可为变量指定初始值,程序中,变量有且仅有一个定义。
变量声明:用于向程序表明变量的类型和名字。
定义也是声明:当定义变量时我们声明了它的类型和名字。
代码演示:
#include <stdio.h> | |
#define price 3 | |
int main(void) | |
{ | |
// 黄瓜 3 元 / 斤 购买 5 斤 | |
// 常量 在程序运行过程中,其值不能发生改变的量,称为常量 | |
//const int price = 3; // 常量 只读变量, 不能写,但是不建议这样使用(不安全) | |
// price = 5; // error | |
// 变量 在程序运行过程中,其值可以发生改变的量,称为变量 | |
int weight = 5; // 5 斤 | |
// 求出 买 5 斤采的价格是多少 | |
int sum = price * weight; | |
printf("%d\n", sum); | |
return 0; | |
} | |
//------------------------ 运行结果 ------------------------ | |
15 | |
E:\C\Demo\Project3\Project2\Debug\Project2.exe (进程 22220)已退出,代码为 0。 | |
按任意键关闭此窗口. . . |
# 2.2 整型🌳
# 2.2.1 整型变量的定义和输出🌲
打印格式 | 含义 |
---|---|
%d | 输出一个有符号的 10 进行 int 类型 |
% o (字母 o) | 输出 8 进制的 int 类型 |
%x | 输出 16 进制的 int 类型,字母以小写输出 |
%X | 输出 16 进制的 int 类型,字母以大写输出 |
%u | 输出一个 10 进制的无符号数 |
代码演示:
#include <stdio.h> | |
int main(void) | |
{ | |
// 数据类型 标识符 = 值 | |
// 无符号 unsigned 有符号 signed (可以省略,默认就是有符号) | |
signed int b = 10; | |
unsigned int a = -10; | |
unsigned int a1 = 10; | |
printf("b= %d\n", b); | |
printf("a= %u\n", a); | |
printf("a1= %u\n", a1); | |
return 0; | |
} | |
//------------------------------- 运行结果 ------------------------------- | |
b= 10 | |
a= 4294967286 | |
a1= 10 | |
E:\C\Demo\Project3\Project2\Debug\Project2.exe (进程 21928)已退出,代码为 0。 | |
按任意键关闭此窗口. . . |
# 2.2.2 进制 - 简单了解🌲
#include <stdio.h> | |
int main(void) | |
{ | |
// 进制 | |
// 进制数分为:二进制 0-1 | 八进制 0-7 | 十进制 0-9 | 十六进制 0-9 (a-f A-F) | |
// 因为十六进制如果使用 0-15 无法表示所有 0-9 其余的两位数使用了字母表示,不区分大小写 | |
printf("===========================\n"); | |
int a = 10; | |
printf("十进制_有符号:%d\n", a); | |
printf("十进制_无符号:%u\n", a); | |
printf("八进制:%o\n", a); | |
printf("十六进_小写:%x\n", a); | |
printf("十六进_大写:%X\n", a); | |
printf("===========================\n"); | |
int b = 0123; | |
int c = 0x123; | |
printf("十进制打印八进制:%d\n", b); | |
printf("八进制打印八进制:%o\n", b); | |
printf("十六进制打印八进制:%x\n", b); | |
printf("===========================\n"); | |
printf("十进制打印八进制:%d\n", c); | |
printf("八进制打印八进制:%o\n", c); | |
printf("十六进制打印八进制:%x\n", c); | |
printf("===========================\n"); | |
return 0; | |
} | |
//------------------------------- 运行结果 ------------------------------- | |
=========================== | |
十进制_有符号:10 | |
十进制_无符号:10 | |
八进制:12 | |
十六进_小写:a | |
十六进_大写:A | |
=========================== | |
十进制打印八进制:83 | |
八进制打印八进制:123 | |
十六进制打印八进制:53 | |
=========================== | |
十进制打印八进制:291 | |
八进制打印八进制:443 | |
十六进制打印八进制:123 | |
=========================== | |
E:\C\Demo\Project3\Project2\Debug\Project2.exe (进程 16180)已退出,代码为 0。 | |
按任意键关闭此窗口. . . |
# 2.2.3 short,int,long,long long🌲
数据类型 | 占用空间 |
---|---|
short (短整型) | 2 字节 |
int (整型) | 4 字节 |
long (长整型) | Windows 为 4 字节,Linux 为 4 字节 (32 位),8 字节 (64 位) |
long long (长长整型) | 8 字节 |
<font color='red'> 注意 </font>:
需要注意的是,整型数据在内存中占的字节数与所选择的操作系统有关。
虽然 C 语言标准中没有明确规定整型数据的长度,但 long 类型整数的长度不能短于 int 类型,short 类型整数的长度不能长于 int 类型。
当一个小的数据类型赋值给一个大的数据类型,不会出错,因为编译器会自动转化。但当一个大的类型赋值给一个小的数据类型,那么就会丢失高位。
整型常量 | 所需类型 |
---|---|
10 | 代表 int 类型 |
整型常量 | 所需类型 |
------------ | ---------------------------- |
10l,10L | 代表 long 类型 |
l0ll,10LL | 代表 long long 类型 |
10u,10U | 代表 unsigned int 类型 |
10ul,10UL | 代表 unsigned long 类型 |
10ull,10ULL | 代表 unsigned long long 类型 |
打印格式 | 含义 |
---------------------------- | ----------------------------------------------- |
%hd | 输出 short 类型 |
<font color='red'>%d</font> | <font color='red'> 输出 int 类型 </font> |
%ld | 输出 long 类型 |
%lld | 输出 long long 类型 |
%hu | 输出 unsigned short 类型 |
<font color='red'>%u</font> | <font color='red'> 输出 unsigned int 类型 </font> |
<font color='red'>%lu</font> | <font color='red'> 输出 unsigned long 类型 </font> |
%llu | 输出 unsigned long long 类型 |
#include <stdio.h> | |
int main(void) | |
{ | |
// 整型变量 | |
int a = 10; | |
// 短整型变量 | |
short b = 20; | |
// 长整型变量 | |
long c = 30; | |
// 长长整型 | |
long long d = 40; | |
printf("整型变量:%d\n", a); | |
printf("短整型变量:%hd\n", b); | |
printf("长整型变量:%ld\n", c); | |
printf("长长整型变量:%lld\n", d); | |
return 0; | |
} | |
//------------------------------ 运行结果 ------------------------------ | |
整型变量:10 | |
短整型变量:20 | |
长整型变量:30 | |
长长整型变量:40 |
# 2.2.4 有符号和无符号数区别🌲
# 2.2.4.1 有符号数🌴
有符号数是最高位数为符号位 (也称带符号位),0 代表正数,1 代表负数
#include <stdio.h> | |
int main(void) | |
{ | |
signed int a = -1089474374; // 定位有符号整型变量 | |
printf("%X\n", a);// 结果为 BF0FF0BA | |
// B F 0 F F 0 B A | |
// 1011 1111 0000 1111 1111 0000 1011 1010 | |
return 0; | |
} | |
//-------------------------------- 运行结果 -------------------------------- | |
BF0FF0BA | |
E:\C\Demo\Project3\Project2\Debug\Project2.exe (进程 4316)已退出,代码为 0。 | |
按任意键关闭此窗口. . . |
# 2.2.4.2 有符号数🌴
<font color='red'> 无符号最高位不是符号位 </font>,而就是数的一部分,无符号数不可能是负数。
1011 1111 0000 1111 1111 0000 1011 1010
:
如果当无符号数看待,那么它是一个正数的原码:
原码: 1100 0000 1111 0000 0000 1111 0100 0110
C 0 F 0 0 F 4 6
十进制数:3236958022
#include <stdio.h> | |
int main(void) | |
{ | |
unsigned int a = 3236958022; // 定义无符号整型变量 a | |
printf("%x\n", a); // c0f00f46 | |
return 0; | |
} | |
//-------------------------------- 运行结果 -------------------------------- | |
c0f00f46 | |
E:\C\Demo\Project3\Project3\x64\Debug\Project3.exe (进程 8308)已退出,代码为 0。 | |
按任意键关闭此窗口. . . |
当我们写程序要处理一个不可能出现负值的时候,一般用无符号数,这样可以增大数的表达最大值
# 2.2.4.3 有符号和无符号整型取值范围🌴
数据类型 | 占用空间 | 取值范围 |
---|---|---|
short | 2 字节 | -32768 到 32767 (-215 ~ 215 - 1) |
int | 4 字节 | -2147483648 到 2147483647 (-231 ~ 231 - 1) |
long | 4 字节 | -2147483648 到 2147483647 (-231 ~ 231 - 1) |
unsigned short | 2 字节 | 0 到 65535 (0 ~ 216 - 1) |
unsigned int | 4 字节 | 0 到 4294967295 (0 ~ 232 - 1) |
unsigned long | 4 字节 | 0 到 4294967295 (0 ~ 232 - 1) |
# 2.3 sizeof 关键字🌳
- sizeof 不是函数,所以不需要包含任何头文件,它的功能是计算一个数据类型的大小,单位为字节
- sizeof 的返回值为 size_t
- size_t 类型在 32 位操作系统下是 unsigned int,是一个无符号的整数
#include <stdio.h> | |
int main(void) | |
{ | |
//sizeof 计算数据类型在内存中占的字节 (byte) 大小 | |
//sizeof (数据类型) sizeof (变量名) sizeof 变量名 | |
unsigned int intLen = sizeof(int); | |
unsigned int shortLen = sizeof(short); | |
unsigned int longLen = sizeof(long); | |
unsigned int longlongLen = sizeof(long long); | |
printf("整数类型在内存中占的字节大小为:%d\n", intLen); | |
printf("短整数类型在内存中占的字节大小为:%d\n", shortLen); | |
printf("长整数类型在内存中占的字节大小为:%d\n", longLen); | |
printf("长长整数类型在内存中占的字节大小为:%d\n", longlongLen); | |
return 0; | |
} | |
//------------------------------ 运行结果 ------------------------------ | |
整数类型在内存中占的字节大小为:4 | |
短整数类型在内存中占的字节大小为:2 | |
长整数类型在内存中占的字节大小为:4 | |
长长整数类型在内存中占的字节大小为:8 |
# 2.4 字符型 char🌳
# 2.4.1 字符变量的定义和输出🌲
字符型变量用于存储一个单一字符,在 C 语言中用 char 表示,其中每个字符变量都会占用 1 个字节。在给字符型变量赋值时,需要用一对英文半角格式的单引号 ('')
把字符括起来。
字符变量实际上并不是把该字符本身放到变量的内存单元中去,而是将该字符对应的 ASCII 编码放到变量的存储单元中。<font color='red'>char 的本质就是一个 1 字节大小的整型 </font>。
#include <stdio.h> | |
int main(void) | |
{ | |
// 字符型变量 | |
char ch = 'd'; | |
// 打印字符型变量的值 | |
printf("%c\n", ch);// d | |
printf("打印字符型的字节大小:%d\n", sizeof(char)); // 大小为:1 | |
printf("%c\n", ch - 32); //d: 98 - 32 = 66: A 对应 ASCII 编码 | |
return 0; | |
} | |
//------------------------------ 运行结果 ------------------------------ | |
d | |
打印字符型的字节大小:1 | |
D | |
E:\C\Demo\Project3\Project2\Debug\Project2.exe (进程 18608)已退出,代码为 0。 | |
按任意键关闭此窗口. . . |
# 2.4.2 字符变量的输入🌲
#define _CRT_SECURE_NO_WARNINGS | |
#include <stdio.h> | |
int main(void) | |
{ | |
// 字符型变量 | |
char ch = 'd'; | |
// 取地址运算符赋值字符变量 | |
scanf("%c", &ch); | |
// 打印运算结果对应的 ASCII 编码 | |
printf("%c\n", ch - 32); | |
return 0; | |
} | |
//---------------------------- 运行结果 ---------------------------- | |
a | |
A | |
E:\C\Demo\Project3\Project2\Debug\Project2.exe (进程 14556)已退出,代码为 0。 | |
按任意键关闭此窗口. . . |
# 2.5 实型 (浮点型):float,double🌳
实型变量也可以称为浮点型变量,浮点型变量是用来存储小数数值的。在 C 语言中,浮点型变量分为两种:单精度浮点数 (float),双精度浮点数 (double),<u > 但是 double 型变量所表示的浮点数比 float 型变量更精确 </u>。
由于浮点型变量是由有限的存储单元组成的,因此只能提供有限的有效数字。
在有效位以外的数字就会被舍去,这样可能会产生一些误差
不以 f 结尾的常量是 double 类型,以 f 结尾的常量 (如 3.14f) 是 float 类型
#include <stdio.h> | |
int main(void) | |
{ | |
float a = 3.14;// 3.140000 | |
double b = 3.14;// 3.140000 | |
printf("%f\n", a); | |
printf("%f\n", b); | |
// 科学发赋值 | |
float a1 = 3210.456; // 3.210456e+03 | |
float b1 = 3.2e3; // 3200.000000 | |
printf("%e\n", a1); | |
printf("%f\n", b1); | |
return 0; | |
int a = 10; | |
float b = 3.14; | |
// % p 占位符: 表示输出一个变量对应的内存地址编号 (无符号十六进制整型数) | |
printf("%p\n", &a); | |
printf("%p\n", &b); | |
} | |
//---------------------------- 运行结果 ---------------------------- | |
3.140000 | |
3.140000 | |
3.210456e+03 | |
3200.000000 | |
008FFEC4 | |
008FFEB8 |
# 2.6 进制🌳
进制也就是进位制,是人们规定的一种进位方法。对于任何一种进制 —X 进制,就表示某一位置上的数运算时是逢 X 进一位。十进制是逢十进一,十六进制是逢十六进一,二进制就是逢二进一,以此类推,X 进制就是逢 X 进位。
# 2.6.1 二进制🌲
二进制是计算机技术中广泛采用的一种进制数,二进制数据是用 0 和 1 两个数码来表示的数。它的基数为 2,进位规则是 “逢二进一” ,借位规则是 “借一当二”。
当前的计算机系统使用的都是二进制系统,<font color='red'> 数据在计算机中主要是以补码的形式存储的 </font>。
术语 | 含义 |
---|---|
bit (比特) | 一个二进制 (bit) 代表一位,一个位只能表示 0 或 1 两种状态。数据传输是习惯以 “位” (bit) 为单位。 |
Byte (字节) | 一个字节为 8 个二进制 (bit),称为 8 位,<font color='red'> 计算机中存储的最小单位是字节 </font>。数据存储是习惯以 “字节” (Byte) 为单位。 |
word (双字节) | 2 个字节,16 位。 |
dword | 两个 word,4 个字节,32 位 |
1b | 1bit,1 位 |
1B | 1Byte ,1 字节,8 位 |
1k,1K | 1024 |
1M (1 兆) | 1024k,1024 *1024 |
1G | 1024M |
1T | 1024G |
1Kb (千位) | 1024bit,1024 位 |
1Mb (兆位) | 1024Kb = 1024 * 1024bit |
1MB (兆字节) | 1024KB = 1024 * 1024Byte |
十进制转化二进制的方法:用十进制数除以 2,分别取余数和商数,商数为 0 的时候,将余数倒着数就是转化后的结果。
十进制的小数转换成二进制:小数部分和 2 相乘,取整数,不足 1 取 0,<font color='red'> 每次相乘都是小数部分 </font>,顺序看取整后的数就是转化后的结果。
# 2.6.2 C 语言如何表示相应进制数🌲
十进制:以正常数字 1-9 开头,如 123
八进制:以数字 0 开头,如 0123
十六进制:以 0x 开头,如 0x123
二进制:C 语言不能直接书写二进制
#include <stdio.h> | |
int main(void) | |
{ | |
// 十进制数 | |
int a = 123; | |
// 八进制数 | |
int b = 0123; | |
// 十六进制数 | |
int c = 0xabc; | |
// 打印十进制数值 | |
printf("十进制:%d\n", a); | |
// 打印八进制数值 | |
printf("八进制:%o\n", b); | |
// 打印小写的十六进制数值 | |
printf("十六进制:%x\n", c); | |
return 0; | |
} | |
//---------------------------- 运行结果 ---------------------------- | |
十进制:123 | |
八进制:123 | |
十六进制:abc | |
E:\C\Demo\Project3\Project2\Debug\Project2.exe (进程 15732)已退出,代码为 0。 | |
按任意键关闭此窗口. . . |
# 2.7 计算机内存数值存储方式🌳
# 2.7.1 原码🌲
一个数的原码 (原始的二进制码) 有如下特点:
- 最高位做为符号位,0 表示正,1 表示负
- 其它数值部分就是数值本身绝对的二进制数
- 负数的原码是在其绝对值的基础上,最高位变为 1 下面数值以 1 字节的大小描述
十进制数 | 原码 |
---|---|
+15 | 0000 1111 |
-15 | 1000 1111 |
+0 | 0000 0000 |
-0 | 1000 0000 |
原码表示法简单易懂,与带符号数本身转换方便,只要符号还原即可,但当两个正数相减或不同符号数相加时,必须比较两个数哪个绝对值大,才能决定谁减谁,才能确定结果是正还是负,所以原码不便于加减运算。
# 2.7.2 反码🌲
- 对于正数,反码与原码相同
- 对于负数,符号位不变,其它部分取反 (1 变 0, 0 变 1)
十进制数 | 反码 |
---|---|
+15 | 0000 1111 |
-15 | 1111 0000 |
+0 | 0000 0000 |
-0 | 1111 1111 |
反码运算也不方便,通常用来作为求补码的中间过渡。
# 2.7.3 补码🌲
在计算机系统中,数值一律用补码来存储
补码特点:
- 对于正数,原码,反码,补码相同
- 对于负数,其补码为它的反码加 1
- 补码符号位不动,其它位求反,最后整个数加 1,得到原码
十进制数 | 补码 |
---|---|
+15 | 0000 1111 |
-15 | 1111 0001 |
+0 | 0000 0000 |
-0 | 0000 0000 |
# 2.7.4 补码的意义🌲
实例 1:用 8 位二进制数分别表示 +0 和 -0
十进制数 | 原码 |
---|---|
+0 | 0000 0000 |
-0 | 1000 0000 |
十进制数 | 反码 |
-------- | --------- |
+0 | 0000 0000 |
-0 | 1111 1111 |
不管以原码方式存储,还是以反码方式存储,0 也有两种表示形式。为什么同样一个 0 有两种不同的表示方法呢?
但是如果以补码方式存储,补码统一了零的编码:
十进制数 | 补码 |
---|---|
+0 | 0000 0000 |
-0 | 1000 0000 由于只用 8 位描述,最高位 1 丢弃,变为 0000 0000 |
实例 2:计算 9-6 的结果
因为计算机中没有减法,只有加法所以是 九减六 也就是 9 + -6
以原码方式相加:
十进制数 | 原码 |
---|---|
9 | 0000 1001 |
-6 | 1000 0110 |
以补码方式相加:
补码在反码的基础上 +1 进一位
十进制数 | 补码 |
---|---|
9 | 0000 1001 |
-6 | 1111 1010 |
<font color='red'> 在计算机系统中,数值一律用补码来存储 </font>,主要原因是:
- 统一了零的编码
- 将符号位和其它位统一处理
- 将减法运算转变为加法运算
- 两个用补码表示的数相加时,如果最高位 (符号位) 有进位,则进位被舍弃
演示:
减法 (本质上 加法)
76 - 32 计算机底层的计算方式如下:
76 正数,三码一致
原码:0100 1100
反码:0100 1100
补码:0100 1100
-32 负数,补码在反码基础上+1
原码:1010 0000
反码:1101 1111
补码:1110 0000
补码76:0100 1100
补码32:1110 0000
补码结果:1 0010 1100
高位溢出舍弃:0010 1100
结果:44 ,那么 76 - 32的结果就是44
计算一个结果为负数:需要将负数进行还原进行一步操作
76 - 82 计算机底层的计算方式如下:
76 正数,三码一致
原码:0100 1100
反码:0100 1100
补码:0100 1100
-82 负数,补码在反码基础上+1
原码:1101 0010
反码:1010 1101
补码:1010 1110
两个数的补码:
补码76:0100 1100
补码82:1010 1110
补码结果:1111 1010
两个补码相加是个负数需要进行还原
反码在补码的基础上 -1
补码:1111 1010
反码:1111 1001
原码:1000 0110
结果:-6,那么 76 - 82 = -6
加法:
10 + 10 计算机底层的计算方式如下:
10 正数,三码一致
原码:0000 1010
反码:0000 1010
补码:0000 1010
10 正数,三码一致
补码:0000 1010
补码:0000 1010
补码结果:0001 0100
结果: 20
10 + -10 计算机底层的计算方式如下:
10 正数,三码一致
原码:0000 1010
反码:0000 1010
补码:0000 1010
10 负数,补码在反码基础上+1
原码:1000 1010
反码:1111 0101
补码:1111 0110
补码10:0000 1010
补码-10:1111 0110
补码结果:1 0000 0000
高位溢出被舍弃:0000 0000
结果: 0
# 2.7.5 数值溢出🌲
当超过一个数据类型能够存放最大的范围时,数值会溢出。
有符号位最高位溢出的区别:符号位溢出会导致数的正负发生改变,但最高位的溢出会导致最高位丢失。
数据类型 | 占用空间 | 取值范围 |
---|---|---|
char | 1 字节 | -128 到 127 (-27 ~ 27 - 1) |
unsigned char | 1 字节 | 0 到 255 (0 ~ 28 - 1) |
#include <stdio.h> | |
int main(void) | |
{ | |
char ch = 127; | |
ch = ch + 1; // 0111 1111 8 位其中最高位为符号位 0 表示正,1 表示负 +1 后 128 为 1111 1111 | |
printf("有符号位:ch = %d\n", ch); // 结果:-128 | |
unsigned char ch1 = 255; | |
ch1 = ch1 + 1; // 1111 1111 只有 8 位 最高位溢出 1 0000 0000 | |
printf("无符号位:ch = %d\n", ch1);// 结果:0 | |
char ch2 = 255; | |
// 1111 1111 从原码得出这个数值是负数,所以三码不一致需要进行 原码,反码, 补码得出结果 | |
// 原码:1111 1111 | |
// 反码:1000 0000 | |
// 补码:1000 0001 = -1 | |
printf("%d\n", ch2);// -1 | |
ch2 = ch2 + 1; // 1 0000 0000 最高位溢出 | |
printf("%d\n", ch2);// 0 | |
return 0; | |
} |
# 2.8 类型限定符🌳
限定符 | 含义 |
---|---|
extern | 声明一个变量,extern 声明的变量没有建立存储空间。<br>extern int a; // 变量在定义的时候创建存储空间。<br > 一般用于声明一个函数,声明变量没有实际价值。 |
const | 定义一个常量 ,常量的值不能修改。<br>const int a = 10; |
volatile | 防止编译器优化代码 |
register | 定义寄存器变量,提高效率。register 是建议型的指令,而不是 <br> 命令型的指令,如果 CPU 空闲寄存器,那么 register 就生效,如果没有 < br > 空闲寄存器,那么 register 无效。 |
# 2.9 字符串格式化输出和输入🌳
# 2.9.1 字符串常量🌲
- 字符串是内存中一段连续的 char 空间,以 ‘\0’ (数字 0) 结尾。
- 字符串常量是由双引号括起来的字符序列,如 “china” ,“C program”,“$12.5” 等都是合法的字符串常量
字符串常量与字符常量的不同:
每个字符串的结尾,编译器会自动的添加一个结束标志位 ‘\0’,即 “a” 包含两个字符 ‘a’ 和 ‘\0’。
#include <stdio.h> | |
int main(void) | |
{ | |
char *ch = "hello world"; | |
// 占位符 % s:表示输出一个字符串,遇到 \0 停止 | |
printf("%s\n", ch); // hello world | |
char ch1[11] = "hello world"; | |
printf("%s\n", ch1); //hello world 烫烫烫烫烫烫烫烫烫烫烫烫烫烫蘌鱒? | |
return 0; | |
} | |
//------------------------- 运行结果 ------------------------- | |
hello world | |
hello world烫烫烫烫烫烫烫烫烫烫烫烫烫烫?霪~ | |
E:\C\Demo\Project3\Project3\x64\Debug\Project3.exe (进程 4900)已退出,代码为 0。 | |
按任意键关闭此窗口. . . |
# 2.9.2 printf 函数和 putchar 函数🌲
printf 是输出一个字符串,putchar 输出一个 char。
#include <stdio.h> | |
int main(void) | |
{ | |
char ch = 'a'; | |
printf("%c\n", ch); | |
// 输出字符 可以是 变量,字符,数字 (对应 ASCII) | |
putchar(ch); | |
putchar('\n'); | |
putchar(97); | |
putchar('\n'); | |
putchar('A'); | |
return 0; | |
} | |
//----------------------- 运行结果 ----------------------- | |
a | |
a | |
a | |
A | |
E:\C\Demo\Project3\Project3\x64\Debug\Project3.exe (进程 7804)已退出,代码为 0。 | |
按任意键关闭此窗口. . . |
printf 格式字符:
打印格式 | 对应数据类型 | 含义 |
---|---|---|
%d | int | 接受整数值并将它表示为有符号的十进制整数 |
%hd | short int | 短整数 |
%hu | unsigned short | 无符号短整数 |
%o | unsigned int | 无符号 8 进制整数 |
%u | unsigned int | 无符号 10 进制整数 |
%x,%X | unsigned int | 无符号 16 进制整数,x 对应的是 abcdef,X 对应的是 ABCDEF |
%f | float | 单精度浮点数 |
%lf | double | 双精度浮点数 |
%e,%E | double | 科学计数法表示的数,此处 “e” 的大小写代表在输出时调用的 “e” 的大小写 |
%c | char | 字符型,可以把输入的数字按照 ASCII 码相应转换为对应的字符 |
%s | char * | 字符串,输出字符串中的字符直至字符串中的空字符 (字符串以 ‘\0’ 结尾,这个 ‘\0’ 即为空字符) |
%p | void * | 以 16 进制形式输出指针 |
%% | % | 输出一个百分号 |
printf 附加格式:
字符 | 含义 |
---|---|
l (字母 l) | 附加在 d,u,x,o 前面,表示长整数 |
- | 左对齐 |
m (代表一个整数)<br>0 (数字 0) | 数据最小宽度 <br> 将输出的前面不上 0 直到沾满指定列宽为止不可以搭配使用 - |
m.n (代表一个整数) | m 指域宽,即对应的输出项在输出设备上所占的字符数。n 指精度,<br> 用于说明输出的实型数的小数位数。对数值型的来说,未指定 n 时,<br > 隐含的精度为 n = 6 位。 |
#include <stdio.h> | |
int main(void) | |
{ | |
int a = 123456; // 如果变量的数值大于 %-5d 则正常输出 | |
printf("===%-5d===\n", a); // ===123456=== | |
int b = 10; | |
printf("===%-5d===\n", b); // ===10 === | |
// 12345 | |
int c = 10; | |
printf("===%5d===\n", c); // === 10=== | |
// 12345 | |
int d = 10; | |
printf("===%05d===\n", d); // ===00010=== | |
float e = 3.15; // 小数点保留一位 5 进行四舍五入 1 为 2 结果 3.2 | |
printf("===%7.1f===\n", e); // === 3.2=== | |
// 1234567 | |
return 0; | |
} | |
//----------------------------- 运行结果 ----------------------------- | |
===123456=== | |
===10 === | |
=== 10=== | |
===00010=== | |
=== 3.2=== | |
E:\C\Demo\Project3\Project3\x64\Debug\Project3.exe (进程 16052)已退出,代码为 0。 | |
按任意键关闭此窗口. . . |
# 2.9.3 scanf 函数与 getchar 函数🌲
- getchar 是从标准输入设备读取一个 char
- scanf 通过 % 转义的方式可以得到用户通过标准输入设备输入的数据
#define _CRT_SECURE_NO_WARNINGS | |
#include <stdio.h> | |
int main(void) | |
{ | |
int a, b; | |
//scanf 中两个 % d 占位符之间不能使用 \n,如果使用,分隔输入的时候必须使用,号形式比如: 12,12 | |
//scanf ("% d,% d", &a, &b); // 输入 12,12 输出 12 12 | |
// %3d 表示约束前面的只能接受 3 个数值 剩余的给后面的。 | |
scanf("%3d%d", &a, &b); // 输入 1234 输出 123 4 | |
printf("%d\t%d", a, b); | |
return 0; | |
} |
#define _CRT_SECURE_NO_WARNINGS | |
#include <stdio.h> | |
int main(void) | |
{ | |
char ch; | |
// 接收键盘获取字符 | |
ch = getchar();// 输入 abc 三个字符 只取一个 | |
putchar(ch);// 输出 a | |
return 0; | |
} | |
//------------------------ 运行结果 ------------------------ | |
abc | |
a | |
E:\C\Demo\Project3\Project3\x64\Debug\Project3.exe (进程 5808)已退出,代码为 0。 | |
按任意键关闭此窗口. . . |
# 3 运算符与表达式🎄
# 3.1 常用运算符分类🌳
运算符类型 | 作用 |
---|---|
算术运算符 | 用于处理四则运算 |
赋值运算符 | 用于将表达式的值赋值给变量 |
比较运算符 | 用于表达式的比较,并返回一个真值或假值 |
逻辑运算符 | 用于根据表达式的值返回真值或假值 |
位运算符 | 用于处理数据的位运算 |
sizeof 运算符 | 用于求字节数长度 |
# 3.2 算术运算符🌳
<font color='red'> 加,乘,除,取余 对指针都不能进行操作 ,但是可以减 </font>。
运算符 | 描述 | 实例 |
---|---|---|
+ | 把两个操作数相加 | A + B 将得到 30 |
- | 从第一个操作数中减去第二个操作数 | A - B 将得到 -10 |
* | 把两个操作数相乘 | A * B 将得到 200 |
/ | 分子除以分母 | B / A 将得到 2 |
% | 取模运算符,整除后的余数 | B % A 将得到 0 |
++ | 自增运算符,整数值增加 1 | A++ 将得到 11 |
-- | 自减运算符,整数值减少 1 | A-- 将得到 9 |
# 3.3 赋值运算符🌳
运算符 | 描述 | 实例 |
---|---|---|
= | 简单的赋值运算符,把右边操作数的值赋给左边操作数 | C = A + B 将把 A + B 的值赋给 C |
+= | 加且赋值运算符,把右边操作数加上左边操作数的结果赋值给左边操作数 | C += A 相当于 C = C + A |
-= | 减且赋值运算符,把左边操作数减去右边操作数的结果赋值给左边操作数 | C -= A 相当于 C = C - A |
*= | 乘且赋值运算符,把右边操作数乘以左边操作数的结果赋值给左边操作数 | C *= A 相当于 C = C * A |
/= | 除且赋值运算符,把左边操作数除以右边操作数的结果赋值给左边操作数 | C /= A 相当于 C = C / A |
%= | 求模且赋值运算符,求两个操作数的模赋值给左边操作数 | C %= A 相当于 C = C % A |
<<= | 左移且赋值运算符 | C <<= 2 等同于 C = C << 2 |
>>= | 右移且赋值运算符 | C >>= 2 等同于 C = C >> 2 |
&= | 按位与且赋值运算符 | C &= 2 等同于 C = C & 2 |
^= | 按位异或且赋值运算符 | C ^= 2 等同于 C = C ^ 2 |
|= | 按位或且赋值运算符 | C |= 2 等同于 C = C | 2 |
# 3.4 比较运算符🌳
运算符 | 描述 | 实例 |
---|---|---|
== | 检查两个操作数的值是否相等,如果相等则条件为真。 | (A == B) 为假。 |
!= | 检查两个操作数的值是否相等,如果不相等则条件为真。 | (A != B) 为真。 |
> | 检查左操作数的值是否大于右操作数的值,如果是则条件为真。 | (A> B) 为假。 |
< | 检查左操作数的值是否小于右操作数的值,如果是则条件为真。 | (A < B) 为真。 |
>= | 检查左操作数的值是否大于或等于右操作数的值,如果是则条件为真。 | (A>= B) 为假。 |
<= | 检查左操作数的值是否小于或等于右操作数的值,如果是则条件为真。 | (A <= B) 为真。 |
# 3.5 逻辑运算符🌳
运算符 | 描述 | 实例 |
---|---|---|
&& | 称为逻辑与运算符。如果两个操作数都非零,则条件为真。 | (A && B) 为假。 |
|| | 称为逻辑或运算符。如果两个操作数中有任意一个非零,则条件为真。 | (A || B) 为真。 |
! | 称为逻辑非运算符。用来逆转操作数的逻辑状态。如果条件为真则逻辑非运算符将使其为假。 | !(A && B) 为真。 |
# 3.6 运算符的优先级别🌳
类别 | 运算符 | 结合性 |
---|---|---|
后缀 | () [] -> . ++ - - | 从左到右 |
一元 | + - ! ~ ++ - - (type)* & sizeof | 从右到左 |
乘除 | * / % | 从左到右 |
加减 | + - | 从左到右 |
移位 | << >> | 从左到右 |
关系 | < <= > >= | 从左到右 |
相等 | == != | 从左到右 |
位与 AND | & | 从左到右 |
位异或 XOR | ^ | 从左到右 |
位或 OR | | | 从左到右 |
逻辑与 AND | && | 从左到右 |
逻辑或 OR | || | 从左到右 |
条件 | ?: | 从右到左 |
赋值 | = += -= *= /= %=>>= <<= &= ^= |= | 从右到左 |
逗号 | , | 从左到右 |
# 3.7 类型转换🌳
数据有不同的类型,不同类型数据之间进行混合运算时必然涉及到类型的转换问题。
转换的方法有两种:
- 自动转换 (隐式转换): 遵循一定的规则,由编译系统自动完成
- 强制类型转换:把表达式的运算结果强制转换成所需的数据类型
类型转换的原则:占用内存字节数少 (值域小) 的类型,向占用内存字节数多 (值域大) 的类型转换,以保证精度不降低。
隐式类型转换:
#include <stdio.h> | |
int main(void) | |
{ | |
float a = 3.14; | |
int b = 2; | |
// 隐式类型转换 | |
//int:4 个字节,float:4 个字节,double:8 个字节 | |
//int = float => double 4 个字节的放入 8 个字节的 是足够的 | |
// 系统自动完成类型转换叫:隐式类型转换 | |
double sum = a * b; | |
printf("%lf\n", sum); // 6.280000 | |
return 0; | |
} | |
//------------------------- 运行结果 ------------------------- | |
6.280000 | |
E:\C\Demo\Project3\Project3\x64\Debug\Project3.exe (进程 15672)已退出,代码为 0。 | |
按任意键关闭此窗口. . . |
强制类型转换:
#include <stdio.h> | |
int main(void) | |
{ | |
float a = 3.55; | |
int b = 2; | |
// 强制类型转换 | |
// (数据类型):强制类型转换运算符 说明:不会四舍五入 | |
short sum = (short) a * b; | |
printf("%d\n", sum); // 6 | |
return 0; | |
} | |
//------------------------- 运行结果 ------------------------- | |
6 | |
E:\C\Demo\Project3\Project3\x64\Debug\Project3.exe (进程 19628)已退出,代码为 0。 | |
按任意键关闭此窗口. . . |
# 4 程序流程结构🎄
# 4.1 概述🌳
C 语言支持最基本的三种程序运行结构:顺序结构,选择结构,循环结构。
- 顺序结构:程序按顺序执行,不发生跳转。
- 选择结构:依据是否满足条件,有选择的执行相应功能
- 循环结构:依据条件是否满足,循环多次执行某段代码
# 4.2 选择结构🌳
# 4.2.1 if else 语句🌲
概念:
判断是否满足某种条件,如果满足则执行某种处理,可以配合 else 进行多分支判断处理更多的操作。
#define _CRT_SECURE_NO_WARNINGS | |
#include <stdio.h> | |
int main(void) | |
{ | |
int num; | |
scanf("%d", &num); | |
if (num >= 100 && num <= 199) | |
{ | |
printf("111\n"); | |
} | |
else if(num >= 200 && num <= 210) | |
{ | |
printf("222\n"); | |
} | |
else | |
{ | |
printf("****\n"); | |
} | |
return 0; | |
} | |
//----------------------- 运行结果 ----------------------- | |
220 | |
**** | |
E:\C\Demo\Project3\Project3\x64\Debug\Project3.exe (进程 17532)已退出,代码为 0。 | |
按任意键关闭此窗口. . . |
# 4.2.2 三目运算符🌲
概念:
判断表达式 1 是否满足某种条件,满足返回 表达式 2,不满足返回 表达式 3
#define _CRT_SECURE_NO_WARNINGS | |
#include <stdio.h> | |
int main(void) | |
{ | |
int num = 10; | |
char *str = num <= 40 ? "该打\0" : num <= 60 ? "加大作业量" : num <= 80 ? "继续加油" : "奖励旅游一周"; | |
printf("%s\n", str); | |
return 0; | |
} | |
//---------------------- 运行结果 ---------------------- | |
该打 | |
E:\C\Demo\Project3\Project3\x64\Debug\Project3.exe (进程 14356)已退出,代码为 0。 | |
按任意键关闭此窗口. . . |
#define _CRT_SECURE_NO_WARNINGS | |
#include <stdio.h> | |
#define MAX(a, b) (a) > (b) ? (a) : (b) | |
int main(void) | |
{ | |
int a = 10; | |
int b = 20; | |
printf("%d\n", MAX(a, b)); | |
return 0; | |
} | |
//----------------- 运行结果 ----------------- | |
20 | |
E:\C\Demo\Project3\Project3\x64\Debug\Project3.exe (进程 14356)已退出,代码为 0。 | |
按任意键关闭此窗口. . . |
# 4.2.3 switch 判断表达式语句🌲
概念:
由一个控制表达式和多个 case,break 组成。case 为目标值匹配表达式传入的满足条件的值然后做对应的操作,break 起到跳出程序的作用。如果不跳出它还会继续执行里面其它的操作这是不合理的。default 作用是当 case 目标值没有可匹配的时则执行 default 来做出一些提示。
#define _CRT_SECURE_NO_WARNINGS | |
#include <stdio.h> | |
int main(void) | |
{ | |
// 提示用户可以进行哪些操作 | |
printf("1. 打开计算机\n2.打开记事本\n3.打开画板\n0.退出\n>-_>> "); | |
// 声明变量标识用户选择了哪个操作 | |
int num; | |
// 键盘输入将用户想要操作的数值赋值到标识变量中 | |
scanf("%d", &num); | |
// 通过标识变量进行对应的操作 | |
switch (num) | |
{ | |
case 1: // 目标值,匹配标识变量对应的目标值进行执行操作 | |
system("calc"); // 具体的操作 | |
break; // 跳出,执行完毕操作后跳出程序,防止 switch 穿透,不写 break 会继续往下执行 default | |
case 2: | |
system("notepad"); | |
break; | |
case 3: | |
system("mspaint"); | |
break; | |
case 0: | |
printf("程序退出!"); | |
exit(); | |
break; | |
default: // 如果以上的所有目标值均没有所对应的则执行 default | |
printf("请选择可用的选项 =_="); | |
} | |
return 0; | |
} | |
//-------------------------- 运行结果 -------------------------- | |
1. 打开计算机 | |
2.打开记事本 | |
3.打开画板 | |
0.退出 | |
>-_>> |
# 4.3 循环语句🌳
# 4.3.1 while 循环语句🌲
概念:
判断循环语句,先进行判断是否满足条件,满足则循环,不满足则停止循环。
场景:
不知道要循环多少次的情况下可以使用让它在一定的条件下停止工作。
#include <stdio.h> | |
int main(void) | |
{ | |
int i = 1; | |
// 判断循环从 i 也就是 1 到 100 次 | |
while (i <= 20) | |
{ | |
// 判断是否为 7 的倍数 | |
if (i % 7 == 0 || i % 10 == 7 || i / 10 == 7) | |
{ | |
printf("敲桌子\n"); | |
} | |
else | |
{ | |
printf("%d\n", i); | |
} | |
i++; | |
} | |
return 0; | |
} | |
//--------------------------- 运行结果 --------------------------- | |
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
敲桌子 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
敲桌子 | |
15 | |
16 | |
敲桌子 | |
18 | |
19 | |
20 | |
E:\C\Demo\Project3\Project3\x64\Debug\Project3.exe (进程 14844)已退出,代码为 0。 | |
按任意键关闭此窗口. . . |
# 4.3.2 do while 循环语句🌲
概念:
先无条件的执行一次循环,然后在进行判断条件是否满足,不满足下次就停止工作,满足则下次还继续工作。
它总是在最后才知道是否要工作。
#define _CRT_SECURE_NO_WARNINGS | |
#include <stdio.h> | |
int main(void) | |
{ | |
// 定义了一个变量赋值为 0 | |
int i = 0; | |
// 计数变量 | |
int num = 1; | |
do | |
{ | |
// 提示信息看用户是否希望通过修改变量 i 来达到它的目的 | |
printf("您是否希望继续工作...\n如果不希望输入0\n如果希望请输入1-9\n生产了 [%d] 个产品\n", num); | |
scanf("%d", &i); | |
// 自增 | |
num++; | |
} while (i); // 判断 i 为 0 结果为 假 并不满足条件 | |
printf("停止了工作..."); | |
return 0; | |
} | |
//------------------------- 运行结果 ------------------------- | |
您是否希望继续工作... | |
如果不希望输入0 | |
如果希望请输入1-9 | |
生产了 [1] 个产品 | |
8 | |
您是否希望继续工作... | |
如果不希望输入0 | |
如果希望请输入1-9 | |
生产了 [2] 个产品 | |
0 | |
停止了工作... | |
E:\C\Demo\Project3\Project3\x64\Debug\Project3.exe (进程 18884)已退出,代码为 0。 | |
按任意键关闭此窗口. . . |
# 4.3.2.1 变量并不会死循环🌴
#include <stdio.h> | |
int main(void) | |
{ | |
short i = 65535; | |
do | |
{ | |
printf("你觉得我会一直执行下去吗?\n"); | |
// 无条件循环,将 65535 + 1 高位溢出 为 0 为假 停止下次的循环 | |
i++; | |
} while (0); | |
/*** | |
* 解释为什么不会死循环。 | |
* 非零即为真! | |
* 当程序执行循环,原本为 0 的 i 变成了 1 结果就为真了。 | |
* 那你可能就觉得这不就死循环下去了吗?NoNoNo! | |
* 并不会,因为 short 是 2 个字节 16 个 bit 2^15-1 = 32,767 | |
* 0111 1111 1111 1111 当 循环到 32,767 再继续循环 32,768 带符号位被改变 | |
* 1111 1111 1111 1111 但是负数也是真 再循环 直到 2 个自己 8 个 bit 的最大值 | |
* 65535 的时候二进制数据如为:1111 1111 1111 1111 次时 i 的值为 - 1 当进行 + 1 | |
* 65536 二进制:1 0000 0000 0000 0000 逢二进一,高位溢出! 当前 i 的值为 0 | |
* 停止工作! | |
*/ | |
printf("我觉得你不会!\n"); | |
return 0; | |
} | |
//---------------------- 运行结果 ---------------------- | |
你觉得我会一直执行下去吗? | |
我觉得你不会! | |
E:\C\Demo\Project3\Project3\x64\Debug\Project3.exe (进程 16936)已退出,代码为 0。 | |
按任意键关闭此窗口. . . |
# 4.3.2.1 水仙花🌴
输出所有的 “水仙花数”,所谓的 “水仙花数” 是指一个三位数其各位数字的立方和等于该数本身,例如 153 是 “水仙花数”,因为:153 = 13 + 53 + 33。
#include <stdio.h> | |
int main(void) | |
{ | |
int i = 100; | |
do | |
{ | |
// 个位数 | |
int a = i % 10; | |
// 十位数 | |
int b = i / 10 % 10; | |
// 百位数 | |
int c = i / 100; | |
if (a * a * a + b * b * b + c * c * c == i) | |
{ | |
printf("%d\n", i); | |
} | |
i++; | |
} while (i <= 999); | |
return 0; | |
} |
# 4.3.3 for 循环语句🌲
概念:
在已知的情况下进行循环
#include <stdio.h> | |
int main(void) | |
{ | |
int i = 0; // 在 c99 中 int 变量才能写在 for () 里面 c99 之前在 Linux 编译不过 | |
for (;i <= 10;i ++) | |
{ | |
printf("%d\n", i); | |
} | |
return 0; | |
} |
<font color='red'> 注意 </font>:在 c99 中 int 变量才能写在 for () 里面 c99 之前在 Linux 编译不过
# 4.3.3.1 无限循环🌴 (无条件循环)
#include <stdio.h> | |
int main(void) | |
{ | |
short i = 65500; | |
for (;;) | |
{ | |
i++; | |
printf("%d\n", i); | |
} | |
return 0; | |
} |
# 4.3.3.2 猜测随机数🌴
#define _CRT_SECURE_NO_WARNINGS | |
#include <stdio.h> | |
#include <time.h> | |
#include <stdlib.h> | |
int main(void) | |
{ | |
// 产生随机数 | |
// 1. 导入头文件 time.h stdlib.h | |
// 2. 添加随机数种子 | |
// 3. 获取随机数 | |
srand((unsigned int)time(NULL)); | |
// 取模 10 随机产生 0-9 之间的数值 | |
int r = rand() % 10; | |
// 用户输入的数值变量 | |
int a; | |
printf("请输入一个数值来猜测一个数值\n"); | |
// 死循环,无条件所以一直循环 | |
for (;;) | |
{ | |
printf(" >>> "); | |
// 键盘输入 | |
scanf("%d", &a); | |
// 判断输入的数值与随机数的大小 | |
if (a < r) | |
{ | |
printf("您输入的数太小了\n"); | |
} | |
else if(a > r) | |
{ | |
printf("您输入的数太大了\n"); | |
} | |
else | |
{ | |
printf("恭喜你答对了\n"); | |
break; | |
} | |
} | |
printf("游戏结束!\n"); | |
return 0; | |
} | |
//-------------------------- 运行结果 -------------------------- | |
请输入一个数值来猜测一个数值 | |
>>> 9 | |
您输入的数太大了 | |
>>> 8 | |
您输入的数太大了 | |
>>> 6 | |
您输入的数太大了 | |
>>> 5 | |
恭喜你答对了 | |
游戏结束! | |
E:\C\Demo\Project3\Project4\x64\Debug\Project4.exe (进程 1836)已退出,代码为 0。 | |
按任意键关闭此窗口. . . |
# 4.3.4 嵌套循环🌲
概念:
嵌套循环是多层的循环体,外层循环一次内层循环 n 次
# 4.3.4.1 while🌴
#include <stdio.h> | |
int main(void) | |
{ | |
int i = 1; | |
while (i <= 10) | |
{ | |
printf("外层循环。。。---------- %d\n", i); | |
int j = 1; | |
while (j <= 2) | |
{ | |
printf("内层循环。。。%d\n", j); | |
j++; | |
} | |
i++; | |
} | |
return 0; | |
} | |
//------------------------- 运行结果 ------------------------- | |
外层循环。。。---------- 1 | |
内层循环。。。1 | |
内层循环。。。2 | |
外层循环。。。---------- 2 | |
内层循环。。。1 | |
内层循环。。。2 | |
外层循环。。。---------- 3 | |
内层循环。。。1 | |
内层循环。。。2 | |
外层循环。。。---------- 4 | |
内层循环。。。1 | |
内层循环。。。2 | |
外层循环。。。---------- 5 | |
内层循环。。。1 | |
内层循环。。。2 | |
外层循环。。。---------- 6 | |
内层循环。。。1 | |
内层循环。。。2 | |
外层循环。。。---------- 7 | |
内层循环。。。1 | |
内层循环。。。2 | |
外层循环。。。---------- 8 | |
内层循环。。。1 | |
内层循环。。。2 | |
外层循环。。。---------- 9 | |
内层循环。。。1 | |
内层循环。。。2 | |
外层循环。。。---------- 10 | |
内层循环。。。1 | |
内层循环。。。2 | |
E:\C\Demo\Project3\Project4\x64\Debug\Project4.exe (进程 12796)已退出,代码为 0。 | |
按任意键关闭此窗口. . . |
# 4.3.4.2 do while🌴
#include <stdio.h> | |
int main(void) | |
{ | |
int i = 1; | |
do | |
{ | |
printf("外层循环。。。 --------- %d\n", i); | |
int j = 1; | |
do | |
{ | |
printf("内层循环。。。%d\n", j); | |
j++; | |
} while (j <= 2); | |
i++; | |
} while (i <= 10); | |
return 0; | |
} | |
//-------------------------- 运行结果 -------------------------- | |
外层循环。。。 --------- 1 | |
内层循环。。。1 | |
内层循环。。。2 | |
外层循环。。。 --------- 2 | |
内层循环。。。1 | |
内层循环。。。2 | |
外层循环。。。 --------- 3 | |
内层循环。。。1 | |
内层循环。。。2 | |
外层循环。。。 --------- 4 | |
内层循环。。。1 | |
内层循环。。。2 | |
外层循环。。。 --------- 5 | |
内层循环。。。1 | |
内层循环。。。2 | |
外层循环。。。 --------- 6 | |
内层循环。。。1 | |
内层循环。。。2 | |
外层循环。。。 --------- 7 | |
内层循环。。。1 | |
内层循环。。。2 | |
外层循环。。。 --------- 8 | |
内层循环。。。1 | |
内层循环。。。2 | |
外层循环。。。 --------- 9 | |
内层循环。。。1 | |
内层循环。。。2 | |
外层循环。。。 --------- 10 | |
内层循环。。。1 | |
内层循环。。。2 | |
E:\C\Demo\Project3\Project4\x64\Debug\Project4.exe (进程 17928)已退出,代码为 0。 | |
按任意键关闭此窗口. . . |
# 4.3.4.3 for🌴
#include <stdio.h> | |
int main(void) | |
{ | |
int i = 1; | |
for (;i <= 10;i++) | |
{ | |
printf("外层循环。。。 -------- %d\n", i); | |
int j = 1; | |
for (;j <= 2;j ++) | |
{ | |
printf("内层循环。。。%d\n", j); | |
} | |
} | |
return 0; | |
} | |
//-------------------------- 运行结果 -------------------------- | |
外层循环。。。 -------- 1 | |
内层循环。。。1 | |
内层循环。。。2 | |
外层循环。。。 -------- 2 | |
内层循环。。。1 | |
内层循环。。。2 | |
外层循环。。。 -------- 3 | |
内层循环。。。1 | |
内层循环。。。2 | |
外层循环。。。 -------- 4 | |
内层循环。。。1 | |
内层循环。。。2 | |
外层循环。。。 -------- 5 | |
内层循环。。。1 | |
内层循环。。。2 | |
外层循环。。。 -------- 6 | |
内层循环。。。1 | |
内层循环。。。2 | |
外层循环。。。 -------- 7 | |
内层循环。。。1 | |
内层循环。。。2 | |
外层循环。。。 -------- 8 | |
内层循环。。。1 | |
内层循环。。。2 | |
外层循环。。。 -------- 9 | |
内层循环。。。1 | |
内层循环。。。2 | |
外层循环。。。 -------- 10 | |
内层循环。。。1 | |
内层循环。。。2 | |
E:\C\Demo\Project3\Project4\x64\Debug\Project4.exe (进程 12980)已退出,代码为 0。 | |
按任意键关闭此窗口. . . |
# 4.3.4.1 九九乘法表🌴
#include <stdio.h> | |
int main(void) | |
{ | |
int i = 1; | |
for (;i <= 9;i ++) | |
{ | |
int j = 0; | |
printf("\t\n"); | |
for (;j <= i;j ++) | |
{ | |
printf("%5d*%d = %d",j , i, (j * i)); | |
} | |
} | |
return 0; | |
} | |
//-------------------------- 运行结果 -------------------------- | |
0*1 = 0 1*1 = 1 | |
0*2 = 0 1*2 = 2 2*2 = 4 | |
0*3 = 0 1*3 = 3 2*3 = 6 3*3 = 9 | |
0*4 = 0 1*4 = 4 2*4 = 8 3*4 = 12 4*4 = 16 | |
0*5 = 0 1*5 = 5 2*5 = 10 3*5 = 15 4*5 = 20 5*5 = 25 | |
0*6 = 0 1*6 = 6 2*6 = 12 3*6 = 18 4*6 = 24 5*6 = 30 6*6 = 36 | |
0*7 = 0 1*7 = 7 2*7 = 14 3*7 = 21 4*7 = 28 5*7 = 35 6*7 = 42 7*7 = 49 | |
0*8 = 0 1*8 = 8 2*8 = 16 3*8 = 24 4*8 = 32 5*8 = 40 6*8 = 48 7*8 = 56 8*8 = 64 | |
0*9 = 0 1*9 = 9 2*9 = 18 3*9 = 27 4*9 = 36 5*9 = 45 6*9 = 54 7*9 = 63 8*9 = 72 9*9 = 81 | |
E:\C\Demo\Project3\Project4\x64\Debug\Project4.exe (进程 15984)已退出,代码为 0。 | |
按任意键关闭此窗口. . . |
# 4.3.4.2 模拟时钟🌴
#include <stdio.h> | |
#include <Windows.h> | |
int main(void) | |
{ | |
int i = 0; | |
for (;i <= 24;i++) | |
{ | |
int j = 0; | |
for (;j <= 60;j++) | |
{ | |
int f = 0; | |
for (;f <= 60;f++) | |
{ | |
printf("%02d:%02d:%02d\n", i, j, f); | |
// 每次执行睡眠 1 秒 | |
Sleep(1000); | |
} | |
} | |
} | |
return 0; | |
} | |
//---------------------- 运行结果 ---------------------- | |
00:00:00 | |
00:00:01 | |
00:00:02 | |
00:00:03 | |
00:00:04 |
# 4.4 跳转语句🌳
# 4.4.1 break 语句🌲
在 switch 条件语句和循环语句中都可以使用 break 语句:
- 当它出现在 switch 条件语句中时,作用是终止某个 case 并跳出 switch 结构。
- 当它出现在循环语句中,作用是跳出当前内循环语句,执行后面的代码。
- 当它出现在嵌套循环语句中,跳出最近的内循环语句,执行后面的代码。
#include <stdio.h> | |
int main(void) | |
{ | |
int i = 0; | |
for (;i <= 10;i++) | |
{ | |
// 如果将这个打印语句写在 if 判断的后面则不会输出数值 5 | |
printf("---- %d\n", i); | |
if (i == 5) | |
{ | |
printf("跳出循环程序\n"); | |
break; | |
} | |
} | |
return 0; | |
} | |
//------------------------------- 运行结果 ------------------------------- | |
---- 0 | |
---- 1 | |
---- 2 | |
---- 3 | |
---- 4 | |
---- 5 | |
跳出循环程序 | |
E:\C\Demo\Project3\Project4\x64\Debug\Project4.exe (进程 14112)已退出,代码为 0。 | |
按任意键关闭此窗口. . . |
# 4.4.2 continue 语句🌲
在循环语句中,如果希望立即终止本次循环,并执行该循环体的下一次循环,此时就需要使用 continue 语句
#include <stdio.h> | |
int main(void) | |
{ | |
int i = 0; | |
for (;i <= 10;i++) | |
{ | |
if (i == 5) | |
{ | |
printf("此路不通\n"); | |
// 终止本次循环,执行下一次循环 | |
continue; | |
} | |
printf("---- %d\n", i); | |
} | |
return 0; | |
} | |
//------------------------ 运行结果 ------------------------ | |
---- 0 | |
---- 1 | |
---- 2 | |
---- 3 | |
---- 4 | |
此路不通 | |
---- 6 | |
---- 7 | |
---- 8 | |
---- 9 | |
---- 10 | |
E:\C\Demo\Project3\Project4\x64\Debug\Project4.exe (进程 19152)已退出,代码为 0。 | |
按任意键关闭此窗口. . . |
# 4.4.3 goto 语句 (无条件跳转,尽量少用)🌲
作用:
类似于传送门,前端的 a 标签的锚点
不建议函数之间跳转!因为两个函数互相跳转执行,那么这两个函数就必然是互相依赖的,否则你跳过去的意义呢,程序就没有模块的性质了,一般程序设计要高密度 低耦合,两个模块之间的耦合度要低,这样单独拿出来一个修改不会影响另外一个函数。如果使用 goto 来回跳就破坏了程序的结构
#include <stdio.h> | |
int main(void) | |
{ | |
int i = 0, j = 0; | |
for (;i < 10;i++) | |
{ | |
if (i == 5) | |
{ | |
printf("进入传送门...\n"); | |
goto a; | |
} | |
printf("i --- %d\n", i); | |
} | |
for (;j < 10;j++) | |
{ | |
a: | |
if (j == 0) | |
{ | |
printf("传送成功 .\n"); | |
} | |
printf("j --- %d\n", j); | |
} | |
printf("hello world !\n"); | |
return 0; | |
} | |
//-------------------------------- 运行结果 -------------------------------- | |
i --- 0 | |
i --- 1 | |
i --- 2 | |
i --- 3 | |
i --- 4 | |
进入传送门... | |
传送成功 . | |
j --- 0 | |
j --- 1 | |
j --- 2 | |
j --- 3 | |
j --- 4 | |
j --- 5 | |
j --- 6 | |
j --- 7 | |
j --- 8 | |
j --- 9 | |
hello world ! | |
E:\C\Demo\Project3\Project4\x64\Debug\Project4.exe (进程 14340)已退出,代码为 0。 | |
按任意键关闭此窗口. . . |
跳过 return 0
#include <stdio.h> | |
int main(void) | |
{ | |
printf("hello1\n"); | |
printf("hello2\n"); | |
return 0; | |
printf("hello3\n"); | |
printf("hello4\n"); | |
printf("hello5\n"); | |
} | |
//---------------------- 运行结果 ---------------------- | |
hello1 | |
hello2 | |
E:\C\Demo\Project3\Project4\x64\Debug\Project4.exe (进程 6960)已退出,代码为 0。 | |
按任意键关闭此窗口. . . |
#include <stdio.h> | |
int main(void) | |
{ | |
printf("hello1\n"); | |
printf("hello2\n"); | |
goto a; | |
return 0; | |
a: | |
printf("hello3\n"); | |
printf("hello4\n"); | |
printf("hello5\n"); | |
return 0; | |
} | |
//---------------------- 运行结果 ---------------------- | |
hello1 | |
hello2 | |
hello3 | |
hello4 | |
hello5 | |
E:\C\Demo\Project3\Project4\x64\Debug\Project4.exe (进程 15788)已退出,代码为 0。 | |
按任意键关闭此窗口. . . |
# 4.4.4 goto 死循环🌲
#include <stdio.h> | |
int main(void) | |
{ | |
short i = 65500; | |
// 程序从传送门出来 A-b 端出来,再次往下执行 | |
a: | |
printf("我又回来了, %d\n", i++); | |
// 程序往下执行遇到 goto 进入传送门 A-a 端 | |
goto a; | |
return 0; | |
// 就像一个无头老鼠,一直走进传送门,一直出不来,这是一个死循环! | |
} |
# 4.4.5 结束 goto 死循环🌴
int main(void) | |
{ | |
short i = 65535; | |
// 程序从传送门出来 A-b 端出来,再次往下执行 | |
a: | |
printf("我又回来了, %d\n", i++); | |
// 判断如果一直从这里经过了 i = 5 次后,就在中途结束执行 | |
if (i == 5) | |
{ | |
return 0; | |
} | |
// 程序往下执行遇到 goto 进入传送门 A-a 端 | |
goto a; | |
return 0; | |
// 就像一个无头老鼠,一直走进传送门,一直出不来,这是一个死循环! | |
} | |
//------------------------ 运行结果 ------------------------ | |
我又回来了, -1 | |
我又回来了, 0 | |
我又回来了, 1 | |
我又回来了, 2 | |
我又回来了, 3 | |
我又回来了, 4 | |
E:\C\Demo\Project3\Project4\x64\Debug\Project4.exe (进程 6488)已退出,代码为 0。 | |
按任意键关闭此窗口. . . |