一、Linux安装与基本操作
1、软件–开发环境
在学习C语言时Linux C环境作为开发环境。在Linux系统,进行软件开发编写运行C语言代码程序;Linux系统在嵌入式中使用广泛 ;Linux系统就是一个C语言编写的系统。
虚拟机:就是在当前已经存在的系统上,额外再安装一个系统运行, 虚拟机中选择的系统为Linux版(Ubuntu)
- Ubuntu Linux:是一款Linux系统的发行版本,比较接近于 Windows
- 在Ubuntu进行开发时,绝大部分操作都是在终端完成,通过在终端输入控制命令(字符组成),来完成操作我们的操作系统
终端的使用:
(1)终端字体大小调节
-
调大:
Ctrl + Shift + " + "
-
调小:
Ctrl + " - "
(2)在终端上默认有一个提示结构
ubuntu @ linuxmachine : ~ $
用户名 分隔符 主机名 分隔符 工作路径 权限
用户主目录(家目录):当前使用的用户,它的对应的目录
~:代表当前用户的家目录
2、Linux终端交互命令
(1)reboot :重启
(2) shutdown:关机
(3)cd:切换工作路径 ,切换到指定的工作目录下
cd 目录 //进入到对应目录 Linux目录结构
(4)两种工作路径
在 Windows中:C:\Program Files (x86)\VMware
-
相对路径:从当前的目录位置出发,经过的位置的一个路径
cd Desktop/1
-
绝对路径:从根目录 ’ / ’ 开始的一个路径
-
根目录: 从磁盘的开始位置,经过的一个路径,整个Linux系统的磁盘起始位置,叫做根目录 ’ / ',Linux系统的最底层目录,有且仅有一个
cd /home/ubuntu/Desktop/1
-
cd ~ :进入到家目录
-
.. :当前工作目录的上一层目录
-
. :当前工作目录
-
(5) pwd:打印当前工作路径 ,显示当前工作路径的绝对路径
(6)ls : 查看目录下的文件
(7)touch: 创建文本文件
touch 文件名路径 //在指定目录下创建某个文件
(8)rm : 删除文件、目录
rm 文件路径名1 文件路径名2 ..... //删除文件
rm -rf 目录路径名1 目录路径名2 ..... //删除目录
(9) mkdir:创建目录(文件夹)
mkdir 目录路径 //在指定目录下创建一个目录
(10)rmdir:删除目录
rmdir 目录路径名1 目录路径名2 ..... //删除目录
(11)cp:拷贝文件、目录
cp 源拷贝文件名路径 目标拷贝文件名路径 //拷贝文件
cp -rf 源拷贝目录路径 目标拷贝文目录路径 //拷贝目录
(12)mv:移动文件
mv 源移动文件名路径 目标移动文件名路径 //可用于文件重命名
3、文本编辑
(1)图形界面的文本编辑 : gedit
gedit //打开图形界面文本编辑器,
(2)字符界面的文本编辑:vi/vim
vi编辑器是系统自带的,vim是升级版(终端记事本,文本编辑工具)。vim只有命令,没有菜单选项,所有的控制(保存、复制、粘贴、剪切、退出)都只能依靠命令来完成
vim 路径文件名 //打开文件
vim 路径文件名1 路径文件名2 -o //小写o,上下分屏同时编辑两个文件,ctrl + w + w切换光标
vim 路径文件名1 路径文件名2 -O //大写O,左右分屏同时编辑两个文件,ctrl + w + w切换光标
(3)Vim有三种工作模式
a、命令模式
主要的工作模式,执行命令完成文件的功能(查找、替换、复制、进入编辑),在命令模式下,所有的输入 都是命令
-
插入命令(此时进入编辑模式)
a:在光标位置之后插入,进行编辑 A:在光标所在行的末尾插入,进行编辑 i:在光标位置之前插入,进行编辑 I:在光标位置所在行的行首插入,进行编辑 o:在光标所在行之后新建一行插入,进行编辑 O:在光标所在行之前新建一行插入,进行编辑
-
定位命令
gg( [[ ):定位到第一行 GG( ]] ):定位到最后一行 nG( ngg ):定位到指定的第n行 $:定位到当前行的行尾 0:定位到当前行的行首 h、j、k、l:上移、下移、左移、右移 L:移动到本界面的最后一行 b:移动到上一个单词 w:移动到下一个单词
-
删除命令
x:删除光标所在字符,每次只会删除一个字符 nx:删除光标所在位置后的n个字符 nX:删除光标所在位置前的n个字符 dd:删除当前行 D:删除当前行的内容 ndd:删除n行 dG:删除光标所在行到整个文件的结束 u:如果误删除,可以使用命令恢复
-
复制粘贴命令
yy:复制当前行到缓冲区 nyy:复制n行到缓冲区 p:粘贴,把缓冲区的内容粘贴到光标的下一行 dd:剪切当前行到缓冲区 ndd:剪切n行到缓冲区
b、插入模式
编辑文本内容,输入、删除文本
- Esc:按下后,从插入模式进入命令模式
c、底行模式
vim工具的使用模式(打开新文件、保存文件、 关闭文件)
: //冒号后面开始输入命令
w //保存
q //退出
! //强制执行
//注意:可以多个命令一起使用!
wq //保存退出
//搜索内容
/ 内容 //回车确认后,从当前光标位置开始进行搜索,如果查找到对应内容,光标移动到对应的位置,确认之后, 命令n搜索下一个,N搜索上一个
//查找替换
%s/old/new //%s:全文替换 old:要查找被替换的内容 new:用新的内容进行替换
n1,n2s/old/new //n1:要替换的开始行号 n2:要替换的结束行号
//显示行号
set nu //显示
set nonu //不显示
d、三种模式转换关系图
二、计算机程序与语言
计算机程序就是为了告诉计算机做某个事情或解决某个问题而设计编写的一个功能集合,只要在计算机中执行程序,计算机就会自动进行工作,根据程序的内容(指令功能)执行操作。计算机的一切控制都是依靠程序进行控制的。
计算机是一种电器设备,只有两种状态:一种是通电,一种是断电。正因为如此,所以计算机运行时(工作时),需要通过很多的开关和连接线路来完成。计算机只认识 1 和 0 两种,程序最终也只能是 1 和 0 的组合。程序的 1 和 0 的组合就叫做一条指令,一条或多条指令的集合就是计算机程序。由于计算机只认识0和1,需要把我们想实现的功能转换为计算机能够识别的语言(0和1)。只要我们使用的任何一门语言计算机能够听得懂(转换为0 和 1)
计算机语言:按照一定的格式,计算机能够识别看得懂转换为0 和1的计算机指令的语言,就叫做计算机语言
常用的计算机语言:
1、机器语言
所有的内容就只有0和1,0表示断电,1表示通电
2、汇编语言
符号化机器语言,用一个符号(单词、数字)来表示一个机器指令
3、高级语言
非常接近于自然语言,语法和结构类似普通英语 c、c++、java、python
三、C语言基础
1、C语言的基本概念
C语言是用于和计算机交流的高级语言,非常接近于自然语言,按照人书写的方式习惯进行编写,方便人进行查看编写,编译(翻译)后给机器进行执行。
C语言版本:
(1)K & R C 1978年,原始版本
(2)ANSI C 1989年,美国标准协会,对C语言制定了标准,1990年通过标准C89、C90
(3)C95:对C89版本进行了一个补充
(4)C99:1999年,对C89标准做出了大量的更新,增加了非常多的语言特性,也是目前流行 的版本
(5)C11:2011年,对C语言进行了更新,增加了多线程的支持
(6)C17
(7)C2X
2、C语言程序结构
对于计算机程序,就是功能指令的集合,如果使用C语言编写程序(功能),怎么执行,按照什么顺序执行,从哪里执行,C语言都做了规定,要遵循C语言的语言规则。
(1)C语言的文件结构
C语言程序内容,需要有一个编写位置,是以 .c 文件后缀,作为C语言程序内容(功能)的标志
(2)C语言程序规定
程序内容执行只有一个入口。
int main() //入口
{
//开始执行内容,且只执行{}中的内容,顺序执行
}
(3)编译(翻译程序)
C语言有一个针对自己功能代码的翻译工具(编译器),把内容翻译为机器识别的0和1内容。
编译器:gcc
gcc 文件名.c //生产一个名为a.out文件,翻译之后的文件内容,计算机可以直接识别
路径/a.out //执行程序
//如果是多个c代码的源码文件,编译方法如下:
$ gcc test1.c test2.c -o main.out //o<文件> :将输出输入到指定文件,不写则默认输出至a.out
$ ./main.out
3、C语言基础语法
C语言是类似自然语言,要满足一定的语言规则。
(1)语句
C语言的程序代码就是由一行行的语句构成。语句就是程序翻译执行的一条操作命令。C语言规定语句(一条操作命令)必须以分号作为结束,除非C语言明确规定了可以不写分号。
(2)表达式
C语言中各种运算参与就是表达式。表达式就是一条计算式,用来获取值(运算式的结果)。只要是表达式就有一个运算结果,运算结果出现在运算的位置。
(3)语句块
C语言允许多条语句形成一个整体,组成一个块,也叫做复合语句,通过加上一组 { } 表示。
(4)空格
C语言中空格没有任何的语法意义,只是为了区别各个不同的单位,增强可读性。
(5)注释
注释就是对代码(C程序指令)的说明,只起到提示作用,编译器不会对注释内容进行编译,对代码无任何实质影响。
a、单行注释
" // " //表示单行注释,从双斜线开始到这一行结束都是注释内容
b、多行注释
/* 注释内容 */ //从" /* "开始进行注释,一直注释到" */ "结束
4、数据类型
对于计算机而言,所有的信息都是以 0 和 1表示在计算机中。
- 移位型数据:
- 十进制:逢十进一(非0开头,所以其他进制的写法,要前补0用于区分),基本数字:0、1、2、3、4、 5、6、7、8、9 ,数字所在的位置,作为权值。
- 二进制:(在计算机中数据存储的格式)逢二进一(0b或0B开头) ,基本数字:0、1,数字所在的位置,作为权值。例如:0b11001 = 1*2^0 + 0*2^1 + 0*2^2 + 1*2^3 + 1*2^4 = 1 + 0 + 0 + 8 + 16 = 25
- 八进制:逢八进一(数字0开头),基本数字:0、1、2、3、4、5、6、7 ,例如:0321
- 十六进制:逢十六进一(0x或0X开头),基本数字:0、1、2、3、4、5、6、7、 8、9、A(10)、B(11)、C(12)、D(13)、E(14)、F(15),例如:0x10101
5、C语言支持的数据类型
在C语言中,每个数据类型都会给一个固定空间大小来存储,方便操作(存取都很方便)。
-
数据单位
bit //二进制位 Byte //字节 KB //千字节 MB //兆字节 GB //吉字节 TB //太字节 PB //拍字节 1 PB = 2 ^ 10 TB = 1024 TB 1 TB = 2 ^ 10 GB = 1024 GB 1 GB = 2 ^ 10 MB = 1024 MB 1 MB = 2 ^ 10 KB = 1024 KB 1 KB = 2 ^ 10 B = 1024 B 1 Byte = 8 bit
(1)布尔类型
bool值:真true、假false
(2)整数类型
C语言支持整数,是使用固定大小存储整数,但是有些数据比较大,有些数据又比较小,固定大小空间,存比较大的数据可能存不下,小的数据存储又浪费,因此整型类型中又设计了多个整型,表示不同的固定大小。
类型 | 描述 |
---|---|
short(int) | 短整型,数据宽度2B (16bit) |
int | 整型,数据宽度4B (32bit) |
long(int) | 长整型,(64位计算机)数据宽度8B (64bit),(32位计算机)数据宽度4B (32bit) |
long long (int) | 长整型,数据宽度8B (64位) |
(3)字符类型
在C语言中,只支持ASCII字符。在计算机中不认识字符,用一个数字对应一个字符,在计算机中只需要存储这个数字就相当于存储了字符,取出数字根据对应关系就相当于取出对应的字符。存储字符就是按照整数存储,这个整数就是字符的编码。
char : 字符类型,数据宽度1B (8bit)
(4)浮点类型
浮点类型中设计了多个浮点数,表示不同的固定大小,存储小数的范围不同。
类型 | 描述 |
---|---|
float | 单精度浮点数,数据宽度4B (32bit),精度只有7位(有效位数6-7位) |
double | 双精度浮点数,数据宽度8B (64bit),精度有16位(有效位数15-16位) |
- 数据类型的符号
对于数据而言既有正数又有负数,在存储数据时既要存储数据部分也要存储符号部分,把数据空间的最高位作为符号位:0表示正数,1表示负数。但是,在计算机中为了方便计算,不采用数据的实际表示方式来存储。
(5)数据类型强制转换
(类型名)值/变量 //表示把值或变量值强制转换为某种类型
例如:
(int)1.2 //把1.2强制转换为整型int
int *p = &a;
(float *)p //把p的值(地址)强制转换为float数据的地址
6、原码、反码、补码
(1)原码
数值使用二进制表示,然后最高位为符号位用 0 和 1 表示符号
(2)反码
- 正数:反码 == 原码
- 负数:在原码的基础上,除了符号位不变,数据位每一位都按位取反(0变为1,1变为0)
(3)补码
- 正数:补码 == 原码
- 负数:在反码的基础上+1
注意:
在计算机中数据都是以补码形式存储,在数据中有时不需要负数,所以表示数据时,需要告知是否有符号,在数据类型前加关键字予以区分。
signed //表示有符号的数据
unsigned //表示无符号的数据
(signed) int //有符号的整数类型,int类型默认带符号
unsigned int //无符号的整数类型
7、变量定义
- 数据:静态数据、动态数据
- 静态数据:是指一些永久性的数据,不会改变
- 动态数据:是指程序在运行过程中,可以动态变化的数据(改变)
(1)常量
静态数据,在设计程序时就确定的,在程序运行过程中不会改变的数值
整型常量:10、1、5
字符常量:'a'、'$'、'1'
浮点常量: 1.2、3.1415926 、0.032、3.2e-2(3.2 * 10 ^-2)
字符串: "ok"、"hello"
(2)变量
表示一些不固定的值,数据可以变化(更改),需要使用一个符号来表示这个数据,不管数据如何变化,这个符号都可以表示这个数据。一个容器,可以存放数据,一段空间的名字,通过名字(变量名),就可以访问到空间的内容。每个变量必须有自己的类型,才能知道存储空间的大小,变量名表示存储空间的名字
定义变量格式:
数据类型 变量名字; //格式1
数据类型 变量名字 = 值; //格式2
注意:
在表示变量名时,有一定的要求,要满足标识符规则:
- 不能与C语言中定义好的关键字冲突
- 只能有字母、数字和下划线
- 只能以字母和下划线_ 开始作为名字,不能以数字开头
(3)变量的使用
变量进行了定义,就是申请了一个对应大小的空间用来存储数据,变量名就代表了存储的数据,使用变量名就是使用存储的空间。
a、变量存储数据
可以利用运算符( “ = ”赋值运算符)存储数据值到变量中。赋值完成后,整个赋值表达式结果为所赋的值!例如:表达式(a=10)的值为10
b = 2; //把右边的值存储到左边变量b中
//注意:
printf("%d\n", a=10); //输出10,表达式a=10的值为10
//可以同时赋值:
int a,b,c,d;
a = b = c = d = 1; // a = 1, b = 1, c = 1, d = 1
b、变量数据传递
把一个变量的数据,存储到另一个变量中。
b = a; //直接把变量a的值,赋值给另一个变量b
c、定义多个同种类型的变量
数据类型 变量名1, 变量名2, ...... ;
8、标准输入输出
scanf、printf都不是关键字,它们只是函数名,故可以用来定义变量:int scanf; int printf;。
(1)标准输出
把程序中的需要显示的内容,显示(打印)到终端上,使用C语言标准库所提供的**printf()**功能输出打印。如果要使用printf功能,需要进行声明(说明要用什么功能),声明在一个特定的文件中:stdio.h
#include <stdio.h>
int main()
{
printf("字符串常量(要输出的内容,是原样输出,如果要输出其他类型,需要使用格式化字符%来表示要输出的一个某种类型的值)",数据1, 数据2, 数据3, ...... );
}
//例如
int a;
float b;
char c;
printf("%d %f %c",a,b,c);
格式控制符 | 说明 |
---|---|
%c | 输出一个单一的字符 |
%hd、%d、%ld | 以十进制、有符号的形式输出 short、int、long 类型的整数 |
%hu、%u、%lu | 以十进制、无符号的形式输出 short、int、long 类型的整数 |
%ho、%o、%lo | 以八进制、不带前缀、无符号的形式输出 short、int、long 类型的整数 |
%#ho、%#o、%#lo | 以八进制、带前缀、无符号的形式输出 short、int、long 类型的整数 |
%hx、%x、%lx、%hX、%X、%lX | 以十六进制、不带前缀、无符号的形式输出 short、int、long 类型的整数。如果 x 小写,那么输出的十六进制数字也小写;如果 X 大写,那么输出的十六进制数字也大写。 |
%#hx、%#x、%#lx %#hX、%#X、%#lX | 以十六进制、带前缀、无符号的形式输出 short、int、long 类型的整数。如果 x 小写,那么输出的十六进制数字和前缀都小写;如果 X 大写,那么输出的十六进制数字和前缀都大写。 |
%f、%lf | 以十进制的形式输出 float、double 类型的小数 |
%e、%le、%E、%lE | 以指数的形式输出 float、double 类型的小数。如果 e 小写,那么输出结果中的 e 也小写;如果 E 大写,那么输出结果中的 E 也大写。 |
%g、%lg、%G、%lG | 以十进制和指数中较短的形式输出 float、double 类型的小数,并且小数部分的最后不会添加多余的 0。如果 g 小写,那么当以指数形式输出时 e 也小写;如果 G 大写,那么当以指数形式输出时 E 也大写。 |
%s | 输出一个字符串 |
(2)标准输入
从终端上输入数据到程序的变量中,使用c语言标准所提供的**scanf()**功能输入数据。如果要使用scanf功能,需要进行声明(说明要用什么功能),声明在一个特定的文件中:stdio.h
#include <stdio.h>
int main()
{
scanf("输入的匹配字符串(从匹配字符串中提取对应类型的数据(格式化字符串%)", &变量名1, &变量名2, &变量名3, ...... );
}
//注意
int a,b;
float c;
scanf("%d%d%c",&a,&b,&c); //输入a,b的值时,虽然"%d%d%c"中无空格,但a,b输入时需加空格予以区分,小数同理!!!
注意:
scanf输入后按下回车键,此时回车键会被保存为字符‘ \n ’,此时可以这样写scanf避免误输入
scanf("%d,%d\n",&a,&b); //方法一:末尾写" \n "
scanf("%d,%d%*c",&a,&b); //方法二:末尾写" %*c ",星号表示忽略"%c"这个输入项,用于接收回车字符'\n',此时不用定义变量接收
9、运算符
只要使用数据,通常都会使用数据进行运算,对运算符按照功能划分:算数运算符、关系运算符、逻辑运算符、位运算符、逗号运算符、赋值运算符、三元表达式(三元运算符)、sizeof运
算符。只要是使用运算符进行运算,就会得到一个运算结果在对应的运算位置,不会改变运算的操作数的值。
(1)算数运算符
对数字型数据的运算:
-
+ (加法)
-
- (减法)
-
* (乘法)
-
/ (除法)
-
% (取余):在进行除法时,得到的结果不是商而是余数,只能整数求余数
注意: 算数运算,类型相同,结果一定为同种类型,不同类型就是精度高的类型
(2)比较运算符
比较运算符也叫做关系运算符,求左右两边表达式的关系,通过关系运算符来进行比较判
断关系是否成立(成立、不成立)。
- 成立:真(非0表示)
- 不成立:假(0表示)
比较运算符 | 作用 |
---|---|
> | 大于运算符,判断比较左边的结果是否是大于右边的结果 |
< | 小于运算符,判断比较左边的结果是否是小于右边的结果 |
== | 等于运算符,判断比较左边的结果是否等于右边的结果 |
!= | 不等于运算符,判断比较左边的结果是否不等于右边的结果 |
<= | 小于等于运算符,判断比较左边的结果是否是小于等于右边的结果 |
>= | 大于等于运算符,判断比较左边的结果是否是大于等于右边的结果 |
注意:
不可将浮点变量用“ == ”或“ != ”与任何数字比较。千万要留意,无论是float 还是double 类型的变量,都有精度限制。所以一定要避免将浮点变量用“ == ”或“ != ”与数字比较,应该设法转化成“ >= ”或“ <= ”形式。
浮点数的比较:
//方法一
#define EPS 0.00000001 //精度
float x,y;
if( (x-y) > -EPS && (x-y) < EPS)
{
// x == y
}
//方法二
if( fabs(x-y) < EPS) //fabs():求绝对值
{
// x == y
}
//方法三
#include <float.h>
if( fabs(x-y) < DBL_EPSILON ) //DBL_EPSILON:系统提供的最小精度,包含在float.h文件中
{
// x == y
}
(3)逻辑运算符
逻辑运算符,提供逻辑判断功能,用于构造更加复杂的表达式
逻辑运算符 | 作用 |
---|---|
&& | 逻辑与,当两侧的表达式都为真时,整个逻辑判断为真 |
|| | 逻辑或,当两侧表达式有一个为真,整个逻辑判断为真 |
! | 逻辑非,当表达式为真时,结果为假,当表达式为假时,结果为真 |
(4)三元运算符
格式:
条件真假 ? 满足条件执行 : 不满足条件执行; //同时把执行内容结果作为整个表达式的结果
注意:
满足条件的语句、不满足条件的语句只执行一个,剩下的另一个不执行!有变量自增时应特别注意!
int x = 1,y = 2;
printf("%d\n", (x > y ? y++ : x++)); //输出1,执行x++, x=2,y=2
printf("%d\n",(x <= y ? ++y : ++x) ); //输出3,执行++y, x=2,y=3
printf("%d, %d",x,y); //输出2,3,x=2,y=3
例如:
c = a > b ? a-b : a+b;
printf("c = %d\n",c);
c = a > b ? a : b;
printf("c = %d\n",c);
(5)逗号运算符号
逗号运算符,也叫做逗号表达式。将多个表达式写在一起,从左至右依次运算每个表达式,同时把最后一个表达式的运算结果作为整个逗号运算符的结果。
int main()
{
int a=20,b=10,c;
c = (a+b,a-b);
printf("c = %d",c); // c = a-b = 10
}
(6)位运算符
C语言中,由于所有的数据存储都是二进制存储,在C语言中就可以对数据中的某一位或某几位进行操作,可以把数据按照二进制位bit的方式进行运算,包括符号位。注意:负数按补码形式参与运算!
运算符 | 作用 |
---|---|
~ | 按位取反,把一个数据的二进制位每一位都进行取反(0变为1,1变为0),包含符号位 |
& | 按位与,把两个数据值中的每一位二进制进行比较,当两个数值的相同位置的二进制位都为1,结 果中的这一位为1,否则为0(都为1,则为1;有0, 为0) |
| | 按位或,把两个数据值中的每一位二进制进行比较,当两个数值的相同位置的二进制位都为0,结 果中的这一位为0,否则为1(都为0,则为0;有 1,为1) |
^ | 按位异或,把两个数据值中的每一位二进制进行比较,当两个数值的相同位置的二进制位不同时(其中一个为1,另一个为0),结果中的这一位为1,否则为0(相同为0,不同为1) |
<< | 左移,把一个数据值中的每一位都左移指定的位数,低位补0(左边的二进制位丢弃,右边补0) |
>> | 右移,把一个数据值中的每一位都右移指定的位数,高位补最高位的值(正数左补0,负数左补1,右边丢弃) |
例如:
-3 & 2 :
-3:
原码: 1 0 0 0 0 0 1 1
补码: 1 1 1 1 1 1 0 1 //负数补码 = 符号位不变,原码取反后加1,计算机中存储的都是补码!!!
2:
原码: 0 0 0 0 0 0 1 0
补码: 0 0 0 0 0 0 1 0 //正数补码 = 原码,计算机中存储的都是补码!!!
最后两个数字的补码相与:
-3(补码): 1 1 1 1 1 1 0 1
2(补码): 0 0 0 0 0 0 1 0
相与的结果:0 0 0 0 0 0 0 0 = 0
(7)sizeof运算符
sizeof运算计算出变量对应的类型的大小
sizeof( 数据类型 )
sizeof( 变量 )
sizeof( 数据值 )
(8)运算符的优先级
如果表达式中出现多个运算符,则按照运算符的优先级运算。
优先级 | 运算符 | 结合律 |
---|---|---|
1 | 后缀运算符:[ ] ( ) · -> ++ – (类型名称){列表} | 从左到右 |
2 | 一元运算符:++ – ! ~ + - * & sizeof_Alignof | 从右到左 |
3 | 类型转换运算符:(类型名称) | 从右到左 |
4 | 乘除法运算符:* / % | 从左到右 |
5 | 加减法运算符:+ - | 从左到右 |
6 | 移位运算符:<< >> | 从左到右 |
7 | 关系运算符:<<= >>= | 从左到右 |
8 | 相等运算符:== != | 从左到右 |
9 | 位运算符 AND:& | 从左到右 |
10 | 位运算符 XOR:^ | 从左到右 |
11 | 位运算符 OR:| | 从左到右 |
12 | 逻辑运算符 AND:&& | 从左到右 |
13 | 逻辑运算符 OR:|| | 从左到右 |
14 | 条件运算符:?: | 从右到左 |
15 | 赋值运算符: = += -= *= /= %= &= ^= |= <<= >>= | 从右到左 |
16 | 逗号运算符:, | 从左到右 |
(9)复合运算符
在运算符中,可以把运算符结合起来一起使用,把“ = ”运算符和其他运算符结合起来一起使用:
+= 、-= 、*= 、/= 、%=、&= 、|= 、^= 、<<= 、>>=
变量 += 值; //变量 = 变量 + 值
变量 -= 值; //变量 = 变量 - 值
变量 *= 值; //变量 = 变量 * 值
变量 /= 值; //变量 = 变量 / 值
变量 %= 值; //变量 = 变量 % 值
变量 &= 值; //变量 = 变量 & 值
变量 |= 值; //变量 = 变量 | 值
变量 ^= 值; //变量 = 变量 ^ 值
变量 <<= 值; //变量 = 变量 << 值
变量 >>= 值; //变量 = 变量 >> 值
-
++、––运算符(自增、自减运算符)
- ++:对变量加1
- ––:对变量减1
注意:
-
变量++:先使用当前变量的值进行其他运算,然后再把变量的值+1
-
++变量:先把变量的值+1,然后再使用当前变量的值进行其他运算
10、流程控制
C语言中的程序默认是顺序执行,即在执行的内容中先执行前面的语句,再执行后面的语句。会按照书写的顺序从上往下依次执行程序中的每一行代码。但是这种情况不能满足程序执行的所有执行要求。
C语言提供了三种控制程序执行的流程:
(1)顺序结构
程序按照从上到下的顺序执行。
(2)选择结构
根据条件选择是否执行语句。根据对应的条件是否成立,进行判断来决定是否要执行。
a、二分支结构:if … else 结构
-
if 单分支选择
先进行条件判断,满足条件就执行语句,且继续往下执行;不满足就跳过对应语句,继续往下执行。
语法结构:if( 条件表达式(只判断真假:非0为真,0为假) ) { 满足条件执行的语句块1 //如果语句块只有一条语句,可以不写{} }
运行逻辑:
当条件表达式为真时,需要执行语句块1,否则,跳过。
-
if 双分支选择
先进行条件判断,如果满足条件(为真),执行满足条件的语句块;如果不满足条件( 假),则跳过满足条件的语句块,去执行不满足条件的语句块。语法结构:
if ( 条件表达式 ) { 满足条件的语句块1 } else { 不满足条件的语句块2 }
运行逻辑:
当条件表达式为真时,需要执行语句块1,否则, 执行语句块2。 -
if 多分支选择
先进行条件判断,如果满足条件1,则执行满足条件语句块1,结束整个if;不满足条件1,则进行条件2判断,满足条件2,则执行满足条件2的语句块2;不满足条件2,则进行条件3判断,满足条件3,则执行满足条件3的语句块;不满足则继续判断,直到最后一个条件判断为止,满足则执行,不满足则执行不满足条件语句块。语法结构:
if( 条件1 ) { 满足条件1语句块 } else if( 条件2 ) { 满足条件2语句块 } else if( 条件3 ) { 满足条件3语句块 } else if( 条件4 ) { 满足条件4语句块 } ..... else if( 条件n ) { 满足条件n语句块 } else { 不满足条件语句块 //该语句块可以省略不写! }
b、多分支结构:switch … case 结构
switch…case是一种特殊的if…else结构,对if…else进行了补充,提供了多路选择,把if…else结构的多路分支改成更加易用,可读性更好。执行switch时,使用switch整型表达式与case的常量表达式进行比较,判断是否相等,相等就执行对应case的语句块。
语法格式:
switch( 整型表达式(或可以转换为整数,其结果为一个整数) )
{
case 常量表达式1:
语句块1;
break;
case 常量表达式2:
语句块2;
break;
case 常量表达式3:
语句块3;
break;
......
case 常量表达式n:
语句块n;
break;
default: //如果所有的判断都不成立,则执行默认语句块,default分支可省略不写!
默认语句块;
}
运行逻辑:
先计算swicth整型表达式的值,对该值匹配 "==” case常量表达式的值,若没有一个匹配就执行default语句,当不需要的时候可以省略default语句;有匹配则执行匹配的case后面的内容,如果只想执行一个case的内容,需要在结束的位置加上break。
注意事项:
- switch语句中的变量是一个常量表达式,必须是一个整型或枚举类型。
- 在一个 switch 中可以有任意数量的 case 语句,每个 case 后跟一个要比较的值和一个冒号。
- case 的常量表达式必须与 switch 中的变量具有相同的数据类型,且必须是一个常量或字面量。
- 当被测试的变量等于 case 中的常量时,case 后跟的语句将被执行,直到遇到 break 语句为止。
- 当遇到 break 语句时,switch 终止,控制流将跳转到 switch 语句后的下一行。
- 不是每一个 case 都需要包含 break。如果 case 语句不包含 break,控制流将会继续后续的 case,直到遇到 break 为止。
- 一个 switch 语句可以有一个可选的 default case 出现在 switch 的结尾。default case 可用于在上面所有 case 都不为真时执行一个任务。default case 中的 break 语句不是必需的。
(3)循环结构
循环语句允许我们多次执行一个语句或语句块。当给定的条件满足时,重复执行(判断一次条件,执行一次)对应的语句块,直到条件不满足为止。给定的条件:循环条件;重复执行的内容(代码块):循环体;
-
while 循环
语法结构:while( 循环条件表达式 ) { 循环体(满足循环条件执行) //如果循环体只有一条语句,可以不写{} 条件改变语句 //能够让循环结束的语句 }
运行逻辑:
先判断循环条件是否为真(非0,负数同样为真),为真就执行循环体语句;继续判断循环条件是否为真,为真则继续执行循环体,直到判断循环条件为假(为0),此时结束while循环。
先判断条件表达式:
真:执行循环体语句块,回到条件判断
假:结束while循环条件:满足条件就会执行,循环执行和退出的控制方式,来控制循环的执行次数,控制 循环什么时候退出。
循环体:循环过程中需要重复执行的内容 -
do … while 循环
语法结构:
do { 循环体 }while( 条件表达式 ); //注意别忘了while后面的分号!
运行逻辑:
先执行一次循环体,再进行条件判断。注意:
while循环与do…while循环没有任何区别,只是do…while会确保至少执行一次循环,循环从第二次开始判断条件。 -
for 循环
for循环作用和while循环作用一致,判断条件是否满足,满足就执行,不满足就退出。
for循环由3个部分组成:- 循环的准备动作,初始化
- 条件与循环体(多次循环执行)
- 条件改变,指向退出条件
语法结构:
for( 初始化表达式; 循环条件表达式; 执行循环操作后的条件改变表达式) { 循环体; }
说明:
- 初始化表达式:用户初始化循环变量,执行for循环到结束为止,只在开始执行一次
- 循环条件表达式:判断表达式是否成立,为真就执行一次循环体(下次继续判断条件),为假就跳出循环
- 执行循环操作后的条件改变表达式:当执行一次循环体后就执行一次
运行逻辑:
先执行初始化表达式,完成初始化;判断循环条件表达式的真假,为真则执行循环体,然后回到条件改变表达式,继续判断循环条件表达式的真假;为假则退出for循环。
注意事项:- for循环的三个表达式都可以不写, 但是 ; ; 必须要添加
- 如果初始化表达式没有添加,说明执行for循环时不需要初始化(不用执行初始化), 直接进行条件判断
- 如果循环条件表达式没有添加,默认为真
- 如果条件改变表达式没有添加,每次执行完循环体后不额外执行条件改变表达式
11、四大跳转语句
(1)continue
用于循环中,跳过本次循环尚未执行的语句,直接进行下一次循环。
(2)break
立即跳出当前的循环或立即跳出当前的switch语句。
(3)goto
跳转到程序指定位置进行执行。
结构:
//先在需要跳转的地方,设置标记
标记名 : //标记格式
//在需要执行跳转的位置,执行跳转
goto 标记名; //执行跳转
(4)return
用于函数中,结束当前函数,把结果返回到调用位置。在函数中使用return,用来表达返回值,同时也表示函数功能执行结束。如果函数中有多条return,只要执行到第一个return语句,就结束函数。
return 表达式; //函数有返回类型
return; //函数无返回类型
12、嵌套循环
(1)选择结构中嵌套循环结构
//格式1
if() { for()/while(){} }
//格式2
switch() { case: for()/while(){} case: }
(2)循环结构中嵌套选择结构
for()/while() { if(){} }
(3)选择结构中嵌套选择结构
if() { if(){} else{} }
(2)循环结构中嵌套循环结构
在循环当中,包含了其他循环。
for()/while(){ for()/while(){} }
运行逻辑:
外层循环执行一次,内层循环执行完一次
13、数组
数组就是一组同种类型的数据集合,存储多个数据。数组就是用来存储一组数据,具有一定关系的若干个变量的集合,用来存储多个数据值。所有的数组都是由连续的内存位置组成,最低的地址对应第一个元素,最高的地址对应最后一个元素。
(1)数组的定义
数组的几个名词:
- 数组:一组“相同数据类型”数据的有序集合
- 数组元素:在数组这个数据集合中的每一个数据(变量),构成数组的每一个数据
- 数组下标:数据原始位置的索引,数组中的特定元素可以通过索引访问,第一个索引值为0
- 数组名:整个数据集合的名字
注意:
-
数组是有序的一组相同数据类型的数据集合
-
数组中的元素的数据类型一致
-
所有的数组都是由连续的内存位置组成
数组的声明:
元素数据类型 数组名[ 元素个数 ];
数组的大小 = 元素数据类型大小 x 元素的个数
(2)数组的使用
数组的使用,就是使用数组的元素,把数组元素进行操作(数组表示一次定义多个变量,当然使用的是定义的元素)
数组元素访问:通过索引(下标)进行访问
数组名[ i ]
(3)数组的初始化
在数组定义时就对数组元素进行赋值,就叫做数组的初始化。
初始化:
元素类型 数组名[元素个数] = { 值1,值2,值3, ...... }
-
完全初始化:对数组的每一个元素都进行初始化
-
有元素个数:
元素类型 数组名[元素个数] = { 值1,值2,值3, ...... } //每个值都写完
-
无元素个数:根据元素的个数来确定数组中的元素个数
元素类型 数组名[] = { 值1,值2,值3, ...... } //每个值都写完
-
-
部分初始化:对数组的部分元素进行初始化,如果出现没有元素个数,就一定是完全初始化
-
有元素个数
-
按照下标顺序,进行部分初始化
元素类型 数组名[元素个数] = { 值1,值2,值3, ...... } //其他没有初始化的元素值默认设置为0
-
按照指定下标,进行部分初始化
元素类型 数组名[元素个数] = { [下标1] = 值1, [下标2] = 值2, [下标3] = 值3, ...... } //其他没有初始化的元素值默认设置为0
-
-
无元素个数
-
按照指定下标,进行部分初始化
元素类型 数组名[] = { [下标1] = 值1, [下标2] = 值2, [下标3] = 值3, ...... } //其他没有初始化的元素值默认设置为0
注意:此时把指定下标的最大值作为元素个数!
-
-
(4) 字符数组
数组的元素中存储的数据类型是字符类型。
char 数组名[元素个数]
-
字符串
把多个字符按照先后顺序有序排列,形成一个字符序列。如果是一个字符串,字符串在存储时会默认在末尾添加一个‘ \0 ’ 字符,‘ \0 ’:空字符,又称结束符,缩写:NUL,ASCII码值:0//字符数组在初始化时存储字符串 char 数组名[元素个数] = "字符串";
字符串的输入、输出:
scanf("%s",数组名);
printf("%s",数组名);
注意:
- 在C语言中没有专门的用于存储字符串的变量,通常使用字符数组来进行存储字符串 ,类似于字符串变量的作用
- 字符数组在存储字符时按照先后顺序排列
- 字符串一定会有结束符 ’ \0 ’ ,字符数组存储字符串时,一定要存储 ’ \0 ’
- 使用sizeof()计算字符串长度时,会在原有字符数量的基础上加上一个‘\0’字符,总数量+1
- char buff[5]={0};是一种定义空字符数组的严谨的写法!数字0表示ASCII码值为0的空字符‘ \0 ’
例如:
char a[] = "hello"; //等价于 char a[6] = {'h','e','l','l','o','\0'};
printf("%ld\n",sizeof(a)); //字符长度为6
(5)二维数组
数组的元素也是一个数组,称为二维数组。一个二维数组,在本质上是由多个一维数组构成(每一个一维数的大小必须相同)。
a、二维数组
-
二维数组的定义
一维数组元素类型 数组名[元素个数][一维数组的元素个数]
-
变量的类型
int a; // 类型 int char b; // 类型 char int x[10]; // 类型 int [10] char y[10]; // 类型 char [10] //二维数组说明 float a[2][5]; //定义二维数组,二维数组数组名: a,二维数组有2个一维数组,每个元素是一维数组(元素类型:float,有5个元素)
-
二维数组的初始化
一维数组元素类型 数组名[二维数组大小][一维数组大小] = { 值1, 值2, 值3, ...}; 一维数组元素类型 数组名[二维数组大小][一维数组大小] = {{值1, ... },{ 值2, ... },{值3, ... },... };
-
完全初始化
//1、二维数组在初始化的时候可以分行进行初始化 int b[3][4] = { {0, 1, 2, 3} , /* 初始化索引号为 0 的行 */ {4, 5, 6, 7} , /* 初始化索引号为 1 的行 */ {8, 9, 10, 11} /* 初始化索引号为 2 的行 */ }; //2、二维数组也可以进行放在一起进行初始化,内部嵌套的括号是可选的,下面的初始化与上面是等同的 int b[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};
-
部分初始化
//1、分行部分初始化 int arr[2][3] = { { 1 }, { 2 } }; //等价于 int arr[2][3] = {{ 1,0,0 },{ 2,0,0 }}; //未写的默认为0 //2、放在一起部分初始化 int arr[2][3] = { 1, 2, 3 }; //等价于 int arr[2][3] = { 1, 2, 3, 0, 0, 0 };
-
-
二维数组的使用
二维数组名[二维数组的下标][一维数组的下标];
-
注意:
二维数组的初始化可以省略第一维(行)的数字,但是不能省略第二维(列)的数字!//1、分行部分初始化 int arr[][3] = { { 1 }, { 2 }}; //等价于 int arr[2][3] = {{ 1,0,0 },{ 2,0,0 }}; //未写的默认为0 //2、放在一起部分初始化 //注意:用这种方式进行初始化的时候,一维的数字大小是由这个初始化数字的数量多少来取的,例如这个例子是4个数字那么就是4/3+1(如果有余数再加1) int arr[][3] = { 1 ,2 ,3, 4 }; //等价于 int arr[2][3] = { 1 ,2 ,3, 4, 0, 0};
b、二维字符数组
本质还是二维数组,就是二维数组的每个元素都是一个一维字符数组。二维字符数组一般用于存储和处理多个字符串,二维字符数组中的每一行均可存储表示一个字符串。
-
二维字符数组定义
char c[3][10]; //定义了一个3行10列的二维字符数组c
-
二维字符数组初始化
通常情况下,二维数组的每一行分别使用一个字符串进行初始化。二维字符数组的第一维(行)大小可省略!char c[3][8]={{"apple"},{"orange"},{"banana"}}; //上面语句等价于 char c[3][8]={"apple","orange","banana"}; //注意:二维字符数组的第一维(行)大小可省略! char c[][8]={"apple","orange","banana"}; //3行8列 //以下均是对二维字符数组元素的引用 printf ("%c",c[1][4]); //输出1行4列元素'g'字符 scanf ("%c",&c[2][3]); //输入一个字符到2行3列元素中 c[2][0]='B'; //把字符赋值给2行0列元素 printf ("%s",c[1]); //c[1]为第2行的数组名(首元素地址),输出 orange scanf ("%s",c[2]); //输入字符串到c[2]行,从c[2]行的首地址开始存放
-
注意:
二维数组在逻辑上是分行分列的,但其存储结构却是连续的!char c[3][5] = {"Apple","Orange","Pear"}; for(int i=0;i<3;i++) { printf ("%s\n",c[i]); } //1、程序输出 /* AppleOrangPear //printf ("%s\n",c[0]); //输出AppleOrangPear OrangPear //printf ("%s\n",c[1]); //输出OrangPear Pear //printf ("%s\n",c[2]); //输出Pear */ //2、程序说明 /* (1) 字符串 "Apple" 的长度为5,加<img src="./截屏/image-20230301115638129-1677643001027-1.png" />上结束符'\0'共6个字符,前5个字符分别从c[0]行的首元素 c[0][0]开始存放,到c[0][4],第6个字符'\0'只能保存到c[1]行的首元素c[1][0]。 (2)字符串 "Orange" 的长度为6,该字符串的前5个字符分别从c[1]行的首元素c[1][0]开始存放,到c[1][4],第6个字符及结束符'\0'顺序存到c[2][0]和c[2][1]。 (3)字符串"Pear"的长度为4,该字符串的5个字符(包含'\0')分别从c[2]行的首元素c[2][0]开始存放,到c[2][4]。 故该数组个元素中的值如下所示: 0 1 2 3 4 c[0] A p p l e c[1] O r a n g c[2] P e a r \0 */
14、函数
(1)函数概念
需要重复执行的功能代码,把这个功能单独的写出来,然后只需要在使用的位置直接说明要使用这个功能。C语言有两类函数:库函数、自定义函数。函数就是一个特定的功能代码,将需要完成的特定功能代码的有序集合,将一个功能代码封装起来(单独书写成一个整体)。就是将一段功能代码封装起来,需要使用该功能时,只需要调用这个函数。
(2)函数定义
返回值类型 函数名( 功能列表 )
{
函数体;
返回值;
}
- 函数体:这个函数的功能是什么(对功能的封装),具体的功能代码语句,包含了什么代码
- 函数名:函数的名字,标识这个功能(函数)
- 返回值类型:表示函数的功能的结果的数据类型
- 返回值:表示函数在调用(使用)后,存在一个执行结果,作为函数的结果,这个值就叫做返回值。函数返回值类型的定义可以缺省,此时函数值的隐含类型是int
- 参数列表:执行函数功能时,函数的执行需要额外的参数内容才能执行
a、无返回值,无参数列表的函数定义
函数的执行后没有结果(不需要有结果),无返回值,函数执行不需要有额外的参数成员。C语言定义了void,表示空类型,没有数据类型。
-
函数定义
void 函数名() { 函数体; }
-
函数调用
函数名();
b、有返回值,无参数列表的函数定义
函数执行后需要有一个结果,有返回值。return表示当前函数的结束,函数体内return后面的语句不会再执行。
- 函数定义
返回值类型 函数名()
{
函数体;
return 表达式; //表示函数执行后的结果是:表达式的值
}
-
函数调用
函数名();
-
注意
有返回值的函数,在函数调用完成(函数结束),在函数的调用位置就是函数的结果值,所以叫做返回值。返回值是什么类型是根据函数名前面的所定义的返回值类型决定!
c、无返回值,有参数列表的函数定义
没有参数列表的函数,只能用于一些固定的功能操作。如果函数的功能需要用到一些值,这些值是不确定的,此时把这些值设计为变量,作为函数的参数进行使用。在调用的时候就把实际的具体值,传入给调用函数中的参数变量,就可以完成具体的操作。参数列表:描述有哪些变量作为参数,等待调用传入;参数列表格式:类型1 变量名1, 类型2 变量名2, 类型3 变量名3,……
-
函数定义
void 函数名( 类型1 变量1, 类型2 变量2, 类型3 变量3, ······ ) { 函数体; }
-
函数调用
函数名( 实际值1, 实际值2, 实际值3, ...... ); //实际值会一次性传递给参数变量
d、有返回值,有参数列表的函数定义
-
函数定义
返回值类型 函数名( 类型1 变量1, 类型2 变量2, 类型3 变量3, ······ ) { 函数体; return 表达式; //表示函数执行后的结果是:表达式的值 }
-
函数调用
函数名( 实际值1, 实际值2, 实际值3, ...... ); //实际值会一次性传递给参数变量
(3)函数使用的注意事项
a、函数定义注意:
- 函数名满足标识符命名规则
- 多个函数,函数名不能相同
b、函数的参数和返回值:
- 形式参数:在定义函数时,()中定义的参数变量,简称:形参。形参变量属于定义函数时添加的变量(在传递时只能赋值为实际值)。
- 实际参数:在函数调用时,传入的值(实际值),简称:实参。实参可以是变量、常量、表达式,甚至可以是函数调用(函数的返回值)。
- 参数个数:传递的实参个数需要和形参匹配
- 参数类型:当实参和形参的类型不匹配时,会自动把实参值转为形参的类型
- 实参和形参只是值传递,即把实参的值传递给形参,而不是把整个实参变量传递给形参
- 返回值:如果函数没有写返回类型,默认返回值类型为int。如果返回值(return 值;)和函数值类型不匹配,则以函数值类型为准(函数调用时自动转换为函数值类型!)
(4)函数声明
在C语言中,函数的定义是有讲究的,只有在后面定义的函数可以直接调用在前面定义的函数。因为如果相反,编译器搞不清楚有没有这个函数,所以就存在函数声明。在之前就说明这个函数存在是什么样子(有没有返回值,参数是什么,函数名是什么) 。如果函数的定义是在函数调用之后的位置,就需要声明。
函数声明格式:
返回值类型 函数名( 参数列表 )
15、变量的生命周期与作用域
(1)变量的生命周期
变量从创建开始到变量消亡的存在时间
- 局部变量:在函数中定义的变量叫做局部变量
- 局部变量的生命周期:从定义开始,到对应的语句块 { } 结束
- 全局变量:在函数外定义的变量叫做全局变量
- 全局变量的生命周期:从程序开始执行,到程序结束
(2)变量的作用域
- 作用域:能够使用变量的范围
- 局部变量的作用域:从定义开始,到对应的语句块{ }结束
- 全局变量的作用域:任意位置都可以使用
注意:
在C语言中,可以在不同作用域中定义相同名字的变量。在都能使用时,使用作用域最小的同名变量。