导图社区 C语言知识点全概括
C语言知识点整理,内容涵盖了C语言学习的多个重要方面,从简单的程序结构开始,逐步深入到数据类型、运算符、流程控制结构等核心概念。详细列出了C语言中的运算符,包括自加自减运算符、优先级和结合性等关键概念,帮助学习者理解和掌握这些运算符的使用方法。涵盖了“循环”、“表达式”、“流程结构”、“语句”、“选择判断”和“goto”等知识点,这些都是编写复杂程序时必须掌握的技能。还涉及了函数、数组、内存分区、多文件编程、内存管理、位运算、类型转换、文件操作、类型修饰符、预处理和其他高级主题,如类型重命名等。
编辑于2024-06-03 21:26:37植物生殖生理,概述了植物生殖生理的复杂过程,从基本概念到具体机制,再到实际应用,做了全面而详细的阐述。描述了花朵在特定生长阶段的状态。探讨了影响花粉生活力的外界条件,如湿度、温度、CO2和O2的相对浓度等。描述了花粉萌发、花粉和柱头的相互识别,以及受精过程中雌蕊的生理变化。
C语言知识点整理,内容涵盖了C语言学习的多个重要方面,从简单的程序结构开始,逐步深入到数据类型、运算符、流程控制结构等核心概念。详细列出了C语言中的运算符,包括自加自减运算符、优先级和结合性等关键概念,帮助学习者理解和掌握这些运算符的使用方法。涵盖了“循环”、“表达式”、“流程结构”、“语句”、“选择判断”和“goto”等知识点,这些都是编写复杂程序时必须掌握的技能。还涉及了函数、数组、内存分区、多文件编程、内存管理、位运算、类型转换、文件操作、类型修饰符、预处理和其他高级主题,如类型重命名等。
计算机操作系统思维导图总结,内容包含操作系统引论、进程的描述与控制、处理机调度与死锁、存储器管理、虚拟存储器、输入输出系统、文件管理。
社区模板帮助中心,点此进入>>
植物生殖生理,概述了植物生殖生理的复杂过程,从基本概念到具体机制,再到实际应用,做了全面而详细的阐述。描述了花朵在特定生长阶段的状态。探讨了影响花粉生活力的外界条件,如湿度、温度、CO2和O2的相对浓度等。描述了花粉萌发、花粉和柱头的相互识别,以及受精过程中雌蕊的生理变化。
C语言知识点整理,内容涵盖了C语言学习的多个重要方面,从简单的程序结构开始,逐步深入到数据类型、运算符、流程控制结构等核心概念。详细列出了C语言中的运算符,包括自加自减运算符、优先级和结合性等关键概念,帮助学习者理解和掌握这些运算符的使用方法。涵盖了“循环”、“表达式”、“流程结构”、“语句”、“选择判断”和“goto”等知识点,这些都是编写复杂程序时必须掌握的技能。还涉及了函数、数组、内存分区、多文件编程、内存管理、位运算、类型转换、文件操作、类型修饰符、预处理和其他高级主题,如类型重命名等。
计算机操作系统思维导图总结,内容包含操作系统引论、进程的描述与控制、处理机调度与死锁、存储器管理、虚拟存储器、输入输出系统、文件管理。
C语言知识点全概括
写在前面
本思维导图参考书籍
C Primer Plus(第六版)
软件:Visual Studio2017
一个简单的程序
#include<stdio.h> #incldue <stdlib.h> int main(void) { //输出一句话 printf("hello C3程序猿~" ); system("pause"); return 0; }
注意点
1、空格随便加,看着大气,美观即可,关键字之间别加,
2、每条语句结束时加分号
3、主函数名字,main 不要写成 mian,其他单词也别写错
4、符号一定要是英文的,中文报错
5、system("pause"); 卡住方便观察结果
6、花括号的风格
注释
功能
让其他人能看懂代码
举例说明
符号
//
单行注释
/* code */
多行注释
C语言特有
问题
注释结尾不确定
解决
个人注意
编译器快速注释按钮
主函数
应用程序的入口,操作系统调用程序的接口
main
别写错
一个项目只能有一个main函数
单个文件只能出现一个
多个文件只能一个文件中有一个
常见的五种
int main(void) { return 0; }
C语言标准主函数形式
void 表示不接受任何参数
无void 表示参数类型和数量不确定
int main(int argc, char* argv[]) { return 0; }
C/C++标准主函数形式,使用命令行参数
int main() { return 0; }
C++标准主函数形式
main() { }
老标准支持的写法,现在的 C语言还支持这种写法
但是尽量不要这么写
C++不支持这种写法
void main() { }
不提倡
C++之父说,这种形式的主函数在C语言和C++中都没有被定义
书上说,这种主函数写法逻辑上符合要求,而且有很多系统支持。但是考虑到代码的可移植性,建议用标准形式,标准形式所有系统都支持
return 0;
0 表示正常结束
逻辑的连贯性
标准规定,程序员忘记写,系统或者编译器会帮我们加上。但是不是所有的编译器都会帮我们加上,所以建议大家都写上
头文件
#include<stdio.h>
标注输入输出头文件
就是让我们的函数可用
输入输出
printf
内置函数
常用函数,已在我们的编译器中定义好
目的是增加程序的执行效率
不是所有的编译器都会这么做,建议大家一定要加上头文件,才能保证代码的移植性
数据类型
几个问题认识数据类型
什么是数据?
记录一天各个时间的温度,那么这个温度就是数据,12摄氏度,15摄氏度
记录一个水库水位的变化值,那么这个值就是数据,12.2米,13.2米
记录百度每天搜索的关键词,那么这个关键词就是数据,“C3程序猿”,“编程”
什么是数据类型?
上面的这些数据,12,15是整数,12.2,13.2是小数,“C3程序猿”,“编程”是两句话
在我们的程序里,不同类型的数据,都对应自己的一种特有的数据类型,整数就是整型数据类型,小数就是浮点型数据类型,一句话叫字符型数据类型,等等
计算机为什么要分类型?
从宏观上看,咱们举的这三个例子,长的就不一样,写法也不一样,计算机也不能用一种方法去做三个事,所以计算机做分类处理,不同的数据类型对应着自己的存储和计算的方法,这样会让计算机的工作更有条理,计算更加清晰,从而增加计算机的计算效率
基本数据类型
数值类型
整型
就是整数,-1,-2,0,1,2
int
整数:12
输出
printf("%d\n", 12);
%d---以10进制形式输出12
%d的位置会被我们的数据替换掉
printf("我们的数:%d\n", 12);
可以加任何提示
printf("%d天%d小时\n", 1, 24);
输出多个值,顺序填充
其他格式化符
%x---以16进制形式输出12---c小写
%X---以16进制形式输出12---C大写
%o---以8进制形式输出12
我们写个整数,默认就是int类型的数据,或者说计算机就会以int类型来处理它
问题来了
我们测试的是一个12,如果位数比较多,比如1873426,这个数不好记。如果这个数使用次数、位置很多,使用它计算的时候,根本记不住,还容易写错
这个数据在以后的应用中不合适了, 要修改,那我们就要所有位置都要修改,这相当不方便,比如有100处,那就要修改100次,少一次,程序结果肯定就会出问题
解决
可以定义一个整型的变量,来装这个数
int a = 1873426;
1、然后使用这个数的时候就用a来代替,既方便又准确
2、哪天想换数据了,直接改一个位置int a = 126;就行了
int类型变量的声明
定义一个int型变量,变量名字是a,并将1873426赋值给a
int a = 1873426;
分析
int
integer的缩写
关键字
C语言里关键字有很多个
a是变量名
尽量不要用中文
名字尽量用英文,英文的个数不限制
=
叫赋值运算符,作用是将1873426赋值给a这个变量
不是等于
1873426
就是数据了
数据的类型要跟变量的类型对应
等于号两端的空格
为了显得大气,加空格好看点
详细int
意义
声明一个int类型变量就是声明一个存储int类型数据的容器
这个容器可以放任何int类型数据
使用
声明
没有给初始值
int a;
系统会给一个默认值
查看系统默认值
定义
有初始值/初始化
int a = 12;
建议:定义变量尽量都给它初始化
赋值
数据赋值给变量
a = 13;
变量赋值给变量
int b = a;
连续赋值
a = b = 14;
从右向左执行
变量与常量
常量
13
只代表自己,值不能改变
变量
a
因为a可以装很多值,装谁就可以代表谁
换句话说,值可以改变
属性
有符号
也叫signed int
signed可省略
表示的数的范围
-2^31~2^31-1
-2147483648~2147483647
内存大小
4字节
字节是计算机常用的存储数据
位是计算机最小的存储单位
1字节等于8个2进制位
一个2进制位只有两个数0或者1
4个字节就是32个2进制位
得到类型的大小
sizeof()
sizeof(类型)
sizeof(变量名)
无符号
unsigned int
写法
unsigned int a;
表示数的范围
0~4294967295
0~2^32-1
内存大小
4字节
字节个数跟有符号的一样
得到类型的大小
sizeof()
sizeof(类型)
sizeof(变量名)
输出
printf("%u", a);
%u表示无符号十进制整型的输出
两种情况表示数的个数一样
4294967296个
有符号从0开始分成两部分,个数一样多
无符号从0开始
其他整型
short
short int
短整型
内存字节数
2字节
sizeof() 取类型的大小
范围
有符号
signed short
-2^15~2^15-1
-32768~32767
无符号
unsigned short
0~2^16-1
0~65535
两种情况表示数的个数一样
输出格式符
%hd
int
内存字节数
4字节
《C和指针》标准只规定int不小于short的长度
int 2;
范围
无符号
unsigned
0~2^32-1
0~4294967295
有符号
signed
-2^31~2^31-1
-2147483648~2147483647
两种情况表示数的个数一样
输出格式符
%d
long
long int
长整型
内存字节数
4字节
《C和指针》标准只规定long不小于int的长度
范围
无符号
unsigned
0~2^32-1
0~4294967295
有符号
signed
-2^31~2^31-1
-2147483648~2147483647
两种表示的数的个数一样
输出格式符
%ld
long long
long long int
C99标准扩展的新类型
VC++6.0不支持
内存字节数
8字节
范围
无符号
unsigned long long int
0~2^64-1
有符号
signed long long int
-2^63~2^63-1
两种情况表示数的个数一样
输出格式符
%lld
选择哪个取决于你的数据的大小
变量的地址
之前讲每个变量都对应一块内存空间,计算机的每一块空间都有一个唯一的标志代表,本质就是一个数,从0开始,到一个很大的数,32位程序是2^32-1, 64位程序是2^64-1。这个数也叫地址,就像我们的家的地址一样
取地址
取地址运算符
&
取变量地址
&a
表达式
没有分号
每个表达式都有一个结果
语句
有分号
每个变量都有自己唯一的地址
输出地址
printf("%p", &a);
0014ff20
printf("%#p", &a);
0x0014ff20
%p,就是以地址形式(16进制)输出
%#p,就是以地址形式输出,加上前缀0x
调试查看地址
双击选中下拉到监视栏
修改数据显示进制
右键监视栏数据,勾选16进制或者取消勾选16进制
变量的输入
scanf("%d", &a);
格式
1、scanf( )
2、%d 这个一定要跟变量a类型对应上
都是int
否则运行可能会崩溃
3、&a
一定要加取地址
不加可能会异常
4、回车结束输入
输入多个
scanf("%d%d", &a, &b);
输入两个数的情况可以用空格隔开
分隔符
转义字符中间用什么隔开,输入的时候就用什么分开
例如:scanf("%dasd%d", &a, &b);
控制台:12asd13
否则输入第二个数据会失败
如果想自己加提示文字
printf()配合scanf()
注意点:
1、scanf("%d%d\n", &a, &b);
不要加换行
2、分隔符,一定要跟程序里一样
比如逗号,程序里用英文,输入时候就要用英文逗号,不然会失败
3、消除警告
Visual Studio 2012及以下版本会报警告
更改方式
scanf_s 替换scanf
#define _CRT_SECURE_NO_DEPRECATE
区别
scanf 是ANSI C定义的
scanf_s 不是标准中的,是微软自己的,所以用微软的编译器都会这样提示大家
这个警告是可以忽略的, 但是不是所有的警告都可以忽略
#pragma warning(disable:4996)
4、消除错误
在Visual Studio 2013中和Visual Studio 2015中,scanf这个会报错
更改方式:scanf_s 替换scanf
文件开头加宏:#define _CRT_SECURE_NO_DEPRECATE
一定要加在前边
5、格式化字符%d 这个一定要跟变量a类型对应上
浮点型
数学里的小数
定义
float a = 12.13;
double b;
long double c;
输出
%f
float
默认是打印6位小数
%lf
double
long double
不初始化不行
%e
科学记数法
属性/区别
float
内存大小
4字节
sizeof(float)
可表示数的范围
表示数的范围
正数部分
1.17549e-038 ~ 3.40282e+038
负数部分
-3.40282e+038 ~ -1.17549e-038
0.0
浮点数赋值要赋值0.0, 不要赋值0
标准规定:指数范围不小于-37~38
看编译器默认的这个值
宏
FLT_MIN
头文件
float.h
范围很大
有效数位(单精度)
ANSI C规定最小值是6
注意不是小数的位数,是以第一个非0数字计算的
实测7位
12345.6789
1.2456789
使用
float、double 常用,long double一般用的不是很多,因为我们不需要那么高的精度。科学计算一般精度要求非常高,可能会涉及到
double
内存大小
8字节
可表示数的范围
表示数的范围
正数部分
+2.2250738585072014e-308 ~ +1.7976931348623158ee+308
负数部分
-1.79769e+308 ~ -2.22507e-308
0.0
标准规定:指数范围不小于-37~38
有效数位(双精度)
ANSI C规定最小值是10位
实测16位
long double
内存大小
不小于double的长度,至少8字节,不同的编译器这个字节数可能不同,有的是8,有的是10,有的是12,有的甚至更大。那么这个字节数就以自己使用的编译器为准,知道有这个区别就可以了
表示范围范围
Visual Studio里long double跟double是一样的
有效数位(精度)
ANSI C规定最小值是10位
Windows是18位
后缀
0.2l/0.2L
long double
无后缀
double
0.2F/0.2f
float
问题
如果不是对应的,会涉及到类型转换,类型转换的结果可能会造成内存截断,导致数据不是我们想要的,所以需要尽量保持一致
原因
只有数值类型一样了,计算机才能进行计算,不一样就转成一样的,转不成就报错
输入输出
printf
%f,%lf这两个都行,输出没有严格要求
默认是6位小数
第六位四舍五入
%5.2f
5代表整个数占据屏幕多少个字符位
多了有效果,少了无效果
.表示小数点
2表示小数的位数,第二位是四舍五入
%e
科学记数法
输出这块格式有很多种,大家不必花太多时间,书上有的试一下就行
scanf
float
%f
double
%lf
long double
%lf
这个输入要求极为严格,如果不对应上就会输入失败
小数的存储
float
1bit(符号位) 8bits(指数位) 23bits(尾数位)
最高位是符号位
1代表负数
0代表正数
指数位
-2^7~2^7-1
-127~128
有效数位(尾数位)
2^23
8388608
7位
double
1bit(符号位) 11bits(指数位) 52bits(尾数位)
最高位是符号位
1代表负数
0代表正数
指数位
-2^10~2^10-1
-1024~1023
有效数位(尾数位)
2^52
4503599627370496
16位
字符型
char
内存大小
表示范围
大小写转换
运算符相关
输出输入
_getch
getchar
putchar
构造类型
数组
意义
前奏
有一个数据13,就需要一块空间存储它,方式是声明一个对应类型的变量,然后存入这个数据。 为什么需要存储?计算机跟人一样,就是数据存进大脑,才能拿出来处理。对于计算机,就是存进内存。 当我们有10个数据需要处理的时候,就需要有对应10块空间分别装着这些数据,或者理解为,就是需要10个变量。定义这10个变量太麻烦,C语言就提供了叫数组的数据类型来满足我们的这种需求。数组可理解为数据集合,或者变量集合,也就是一下子申请指定个数的空间。下面就研究一下数组的各种特性
特点
类型相同的元素组成的集合
空间连续
一维数组
声明与定义
声明
类型 + 数组名 + [元素个数]
int a[10];
int表示元素的类型
用整型做例子,其他类型同理
所有元素都是这个类型,即类型必须相同
a是数组的名字
a是一个数组类型的变量
[ ] 方括号表示声明的是数组变量
10表示元素的个数
目前必须是整型常数
大小暂时不要超过120万字节左右,相关的在内存管理部分再讲
不能是负数
不能是0
新特性
变长数组
个数可以用变量表示,而不是说数组可以变化长度
int a[n];,n必须是固定值
目前支持的编译器版本不多,所以代码移植性差一些
浮点型
定义(初始化)
形式
int a[10] = {1,2,3,4,5,6,7,8,9,10};
声明的时候初始化
初始化全部元素
int a[10] = {1,2,3,4,5,6,7,8,9,10};
元素初始化个数太多
初始化部分元素
int a[10] = {1,2,3,4};
初始化部分元素,其他的默认是0
通常用这个属性初始化整个数组
int a[10] = {0};
初始化指定元素
新标准C99
目前支持的编译器也不多,大家试试自己的即可,支持就支持,不支持也就不支持了
int a[10] = {1,2,3,[7]=12};
定义的时候不加元素个数
int a[] = {1,2,3,4,5,6,7,8,9,10};
注意
一定要初始化
最终数组的元素个数由初始化的数据个数决定
元素访问
通过下标使用
下标从0开始
依次递增
数组名+[下标]
与定义的区别
一个叫数组,一个叫下标运算
区分
有没有类型
一个是元素个数,一个代表具体元素的下标
遍历
注意越界问题
下标与元素个数的关系,最大下标是元素个数-1,别越界
可以用循环
循环控制变量从0开始
元素下标可以是变量
演示越界
元素赋值
a[0] = 1; a[1] = 2......a[9] = 4;
输入
scanf("%d", &a[2]);
注意点
a = {1,3,4,5,6};只能在定义的时候用
可以是整型表达式
数组名字是个常量
首元素的首地址
越界
C语言不检测数组越界,目的是增加程序执行效率,所以在使用时一定要注意别越界
地址
数组的大小
sizeof(数组名字)
取地址运算符:&
&a[0], &a[1].....
单个元素地址
a == &a[0]
首元素的首地址
&a
数组的地址
意义上的区别
小区地址与具体住户地址的区别
实际代码的区别
&a+1 加一个类型(整个数组)的大小
一个小区的下一个,那是下一个小区
&a[1]+1 也加一个类型(一个元素)的大小
住户的下一个,是下一户
二维数组
声明和初始化
引出
int a[3];是3个元素的数组,每个元素都是一个整型。元素的类型可以是任意的C语言数据类型。那么如果数组的每个元素都是一个数组类型呢?这就是二维数组,即元素是一维数组的一维数组,是二维数组
声明
int a[3][2];
1、二维数组就是两个[ ], 同理10维数组就是10个[ ]
2、a是数组的名字
3、前边的3表示大的数组里有3个小的一维数组
4、2表示每个小的一维数组有两个元素
5、int表示每个小数组的元素是整型
6、元素个数==2*3
同理二维数组的大小等于3*2*类型 sizeof(a)
初始化
1、int a[3][2] = {{1,2},{3,4},{5,6}};
二维数组的本质,元素是一维数组的一维数组
由三个一维数组组成的数组
2、int a[3][2] = {{1,2}, {3,4}, {5,6}};
符合三行两列的形式,便于理解
行列的方式进行理解
3行2列的数组
元素个数就是2*3
但是一定要注意,行列只是为了理解方便抽象出来的,数组本身都是线性的,不会分行列
其他初始化的形式
1、int a[3][2] = {{1,2},{3,4},{5,6}};
完整初始化
2、int a[3][2] = {{1,2},{3,4}};
未指定数据的元素默认是0
3、int a[3][2] = {{1},{3,4},{6}};
4、int a[3][2] = {1,2,3};
5、int a[][2] = {1,2,3};
二维数组的访问
下标也是从0开始
第一个数00,第二个01,第三个10,第四个11,第五个20,第六个21
打印二维数组
int i, j; for (i = 0; i < 3; i++) { for (j = 0; j < 2; j++) { a[i][j]; } }
赋值
a[i][j] = 12;
输入元素
&a[0][0]
地址
&a[1], &a[2], &a[0] == a
一维数组元素地址
&a[0][1], &a[1][0] == a[1]....&a[0][0] == a[0] ;
取元素地址
&a
二维数组的地址
数组之间的赋值
利用循环对应元素赋值
memcpy();
参数1:目标数组
参数2:被复制的数组
参数3:要复制的字节数
注意
不能用数组名字直接赋值
不要越界赋值
多维数组
类比二维数组
演示第一个元素
类似进位一样
普通程序员基本二维就顶天了,多维在科学计算中用的多
字符数组/字符串
字符
数字字符与数字
字符与scanf
字符串
定义
以'\0'结尾的字符数组
输出输入
printf("%s",name); scanf("%s",name);
字符串的声明方式(四种形式)
这么多方式用哪个好呢?这些方式的功能或者说结果都是一样的,大家平时使用时想用哪个用哪个,不要被条条框框拘束。但是我们写代码的格式还是要完美点,好看点,向标准看齐
字符与scanf
gets可以得到输入的空格
操作
库函数
strcmp(),strlen(),strcpy,strcat()...等等
结构体
struct
结构体类型声明方法
有名字,无名字
结构体变量的声明方法
初始化
成员赋值
成员调用方法
取成员运算符
.
->
结构体大小
内存对齐
链表
单向链表
双向链表
联合
union
特点
所有成员共享内存
大小
最大的成员的大小
一般初始化最大的成员
延伸
大小端模式
大小端模式的测试方法
枚举
enum
声明以及使用
使用有意义的字符串
大小
4字节
为int类型数的集合
指针
前述
指针是一种数据类型,就像int和float,int装整型数据,float装浮点型数据,指针装地址型数据,仅此而已
大家经常说指针难,难在两处
第一是心理暗示
百度里面太多了
第二是不理解
指针的关键点
类型
偏移
加减
内存操作
*
指针类型
基本数据类型的指针
char, short, int, long, long long, float, double
指针的声明与定义
咱们拿整型做例子,其他类型一样
声明一个整型指针 声明一个指针变量
int *p;
形式:类型+*+变量名;
int 表示p装的地址对应的空间的数据类型
*表示p是一个指针变量
p是指针的名字
指针指向空间
指针就是装地址的变量,变量就要赋值,即一定要装一块空间的地址,或者说指向一块空间,才能被使用。就像int a;,如果没有被初始化,没有赋值,这东西啥也不能干。指针变量也是同理,不装地址的情况下,啥都不能干,也叫野指针
初始化
int a = 12; int *p = &a;
画一下内存模型
打印一下值,对比一下
赋值
int a = 12; int *p; p = &a;
注意点
1、指向是什么意思
装哪块地址,就指向哪一块空间
2、类型一定要对应上
类型决定指针的读写方式
演示一下类型不一样会有什么效果
通过指针操作所指向的空间
1、指向其他空间
int a = 12; int b = 18; int *p; p = &a; p = &b;
2、通过地址间接操作所指向的空间
对内存的操作就三种:读、写、取地址
读
int a = 12; int *p = &a; printf("%d", *p);
与定义时候的* 是不一样的
定义时候属于标记
而这个是一个运算符
写
int a = 12; int *p = &a; *p = 12345; scanf ("%d", a);
取地址
p就是指向空间的地址,&p是p变量自己的地址
*
我们叫它内存操作符
还有人叫解引用运算符
使用方法
*+地址
这个地址一定是合法地址
什么是合法地址呢?即我们申请了的地址,如定义的变量、数组等
一个指针指向一个变量,*这个指针,就是变量本身
间接操作与直接操作
类型决定内存操作
所指向的空间是什么类型,那么*p 就一次操作多大的内存空间
一个小范围的指针可以指向大范围的空间,并且操作不异常,虽然结果不太对
一个大范围的指针不可以指向小范围的空间,否则操作异常,结果更不对
二级指针
意义
指针变量声明的分解理解式
int a = 12; int *p = &a;
1、p是一个指针变量
2、指向的空间类型是int,或者指向的变量类型是int
注意
一定要分清是谁的地址
指针变量有地址
一级指针变量的地址,用二级指针来装,同理二级指针地址用三级指针装
定义
int a = 12; int *p = &a; int* *p1 = &p;
内存模型画一下
注意
二级指针跟数组无关
有的同学,就是把2级指针跟二维数组指针想成等价,这是完全不对的。
演示
操作
一个指针指向一个变量,*这个指针,就是那个变量本身
*p1 == p **p1 == *p == a
指针与数组
一维数组与指针
利用指针遍历数组
int a[5] = {3, 1, 7, 4, 9}; int *p = &a[0];
元素地址
p p+1 p+2 p+3 p+4
p是不变的
p++
这种与上面的区别
修改了指针p本身
元素内容
指针操作
*p *(p+1) *(p+2) *(p+3) *(p+4)
*p++
有一个小问题
*p++ 等价于 *(p++)
*(p++)
(*p)++
为什么普通类型指针可以遍历数组?
1、数组空间是连续的
2、地址加减是加一个类型的大小
注意点
p不是数组指针,也不是指针数组,就是一个普通的整型指针
指针类型必须与元素类型一致
只有这样,才能正确读写数据
深入
下标运算
数组名字
a[0] a[1] a[2] a[3] a[4]
本质
地址+[偏移量]
推理出
p[0] p[1] p[2] p[3] p[4]
指针的下标运算
准确地说,下标运算不是数组专属,只是一个运算符。数组可以用这个运算符是因为它符合了下标运算符的使用条件,地址+[偏移量],指针也是完全可以的
推出
*(p+2) == p[2]
执行过程一样,结果也一样
1、地址+偏移量找到目标地址
2、操作目标地址空间(读、写、取地址)
意义不一样
*(p+2)
混合运算表达式,包含三个运算符
p[2]
只有一个运算符
a[0] a[1] a[2] a[3] a[4] 数组名字也是同理
数组名字也可以 *(a+2) == a[2]
数组名字a与指针p的区别
a是不能被赋值,只能参与运算
p既可以参与运算,也可以被赋值,指向新的地址
*(p+2) == *(2+p) == 2[p]
更深入地看到了下标运算
指针数组
思考一个问题
数组元素可以是任何数据类型,那么假设有 int b, c, d, e, f;五个变量,想要把这五个变量的地址用数组装起来, &b &c &d &e &f,那么这个数组该如何定义呢?
注意
这五个变量必须是相同类型,这样他们的地址类型才相同,都是int *
分解式定义
1、a[5] = {&b, &c, &d, &e, &f};
2、a前边是每个元素的类型 int*
3、所以这种数组这样定义:int* a[5] = {&b, &c, &d, &e, &f};
int* a[5];
这就是指针数组
两个关键词
第一是数组(变量的类型,变量先跟方括号结合)
第二是指针(元素类型)
每个元素都装的是地址,所以叫地址数组也没问题
简单用一下
a[0] == &b
*a[0] == b
这一块一定要理解,而不是记忆
一个小的应用思路
简单数组拉链结构
图解表示
元素访问
a[2][3]
访问格式跟二维数组一样,意义也是一样的
1、但是内存结构是不一样的,或者说本质不一样
2、每个小数组大小可以不同,但是元素类型必须相同
数组指针
从理解角度认识数组指针
int a[5]; &a;
&a+1 经过我们测试,是加了整个数组的大小
根据之前的理论,地址加1是加了这个地址类型的大小,int 地址是+4, short+2,依此类推
那么&a+1,加了整个数组的大小,说明&a这个地址类型是其所表示的数组那么大(字节数)。这种地址类型就叫做数组类型的地址,简称数组地址,用数组指针来装(重复下地址与指针的关系)
数组指针规范意义
顾名思义就是数组类型的指针,也就是一个指针指向一个数组类型
代码
int a[5]; int (*p)[5] = &a;
分解式定义法
(*p)
p是一个指针,一定要加这个小括号,否则p就先跟[ ]结合,就是数组了
int [5]
p所指向的类型,数组类型
int [5]与int [7] ,数组元素个数不同,那就是不同类型的数组。一定要记住,不是所有的数组指针都是一样的
演示
合在一起
int [5](*p) 这应该是通常的组合模型,但是C语言不让这样写,得像int (*p)[5]这样写
使用
p指向a,*p就是a本身, 所以*p等价于a
元素读写
(*p)[1] = 2; &(*p)[3];
用处
函数参数这一块意义比较大
顺便解释一个问题
某个知识点用在哪?
知识是活学活用的
两个概念的对比
int (*p)[5]
数组的指针
数组的地址
int* p[5]
指针的数组
地址的数组
二维数组与指针
二维数组指针
二维数组有三个类型的地址 int a[2][3];
&a[0][0]==a[0] &a[1][1]
元素的地址
a[0],a[1]分别是两个小数组的数组名
住户
&a[0]==a &a[1]
每个小数组的地址,是一维数组类型地址
对一维数组名字取地址,是一维数组类型地址
每栋楼
测试下
注意
类型个数是3,不是2
&a
二维数组类型的地址
小区地址
定义
分解式定义法
(*p)
p是一个指针
int [2][3]
p是指针的类型
组合int [2][3](*p);
C/C++规定写法:int (*p)[2][3] = &a;
注意,元素个数一定要相同
利用p编译这个二维数组
一句话,p指向一个变量的地址,*p就是这个变量a本身
a[1][2] == (*p)[1][2]
数组指针,一般应用在函数参数,等到函数部分再实践一下
指针数组
指针数组的意义跟一维指针数组一样, 即每个元素是个指针,任何维数的指针数组都是一样的
int * a[2][3];
跟一维的一样
利用指针遍历二维数组
二维数组有三个类型的地址 int a[2][3];
&a[0][0]==a[0] &a[1][1]
int a[2][3]; int *p = &a[0][0]; (*p) == a[0][0]; *(p+1) == a[0][1];
&a[0]==a &a[1]
int a[2][3]; int (*p)[3] = &a[0]; (*p) == a[0]; (*p)[0] == a[0][0]; *(p+1) == a[1]; (*(p+1))[1] == a[1][1]; p[1][1] == a[1][1];
所以有两种访问方式
(*(p+1))[1] == a[1][1]; p[1][1] == a[1][1];
&a
int a[2][3]; int (*p)[2][3] = &a; a[1][2] == (*p)[1][2];
函数指针
结构体指针
指针的大小
sizeof(指针变量名字)
64bit编译器
8字节
设置编译器环境
32bit编译器
4字节
64和32有什么区别
寻址空间
32
最大寻址0~4G,所以对于32位操作系统,内存再大也没有用
64
理论最大16TB,但是实际中取决于整体硬件,比如主板。目前我们看到的个人电脑,支持到64G内存了,所以从这个角度也可以看出64位系统的提升空间是巨大的
内存申请与释放
C: 内存申请用 malloc() 内存释放用 free()
基本数据类型申请
数组的指针
*的三种作用
声明的时候有*,表示指针变量
*+地址,表示地址操作符
数字*数字,表示乘法
运算符
几个简单的运算符讲解
赋值运算符
=
形式
a = 12;
左侧必须是变量,右侧变量和常量都行
读法
将12赋值给变量a
读成a等于12是不对的
用法
一步赋值
a = 12;
z = g;
连续赋值
a = b = 12;
从右向左,12先赋值给b,b的值再赋值给a
a = 12 = 23;
这种赋值方法是错误的
左侧是目标类型,计算机会转成左侧一样的类型
加法
+
形式
常数+常数
变量+常数
变量+变量
连续加法
变量+常数+变量+变量
从左向右
得到结果的方式
可以用一个同类型的变量装着,然后输出这个变量
a = b + c;
也可以直接输出这个表达式
printf ("%d", a + 12);
空格的话,看大家习惯了,加也行,不加也没问题
减法
-
同上
乘法
*
(a+b)*(c+d)
必须要有*
除法
取整(除)
/
分母不能为0
取余(求模)
%
必须是整数,浮点数不行
拆数
小括号
()
优先级
先算谁,后算谁
混合运算
尽量用同类型的数据进行运算
计算机只能对同种类型数据进行运算,对于不同类型数据的混合运算,系统要先进行转换,才能计算。类型转换就可能造成内存截断,这个结果可能就是错误的,或者是我们不想要的
结合性
连续运算的顺序
一览
运算符优先级表
自加运算符++,--的理解
自加自减运算符
自加1
++
前置++
用自加后的值参与运算
变量本身自加
常量不能自加,自加的本意就是变量的值改变
后置++
用自加前的值参与运算
变量本身自加
相同点
执行完++,都自加1了
区别
所在语句中,参与运算的值是不一样的。后置++是自加前的,前置++是自加后的
优先级
后缀++ 高于 前缀++
自减1
--
前置--
用自减后的值参与运算
变量本身自减
后置--
用自减前的值参与运算
变量本身自减
注意:
同一个变量的自加和自减不能出现在同一条语句中两次以上,否则的话不同的编译器得到的结果是不一样的
a,a++;
若想测试
分行来
有人说先计算后自加
这句话显然是不对的,因为++运算符优先级比=高
这句话可以得到正确的结果,大家可以依据这句话进行计算,但是不要说出来
其他与自己变化相关的运算符,包括复合赋值运算符
+=
a += 2;
a = a + 2;
-=
a -= 2;
a = a - 2;
*=
a *= 2;
a = a * 2;
/=
a /= 2;
a = a / 2;
流程结构
顺序
由上向下一步一步执行
循环
意义
重复去做一件事儿
三种结构
入口条件循环
while(条件)
格式
while ( 条件 ) { code;//重复做的事 }
while
循环的关键字
条件
满足条件就会进入代码块执行
代码块
只有一条语句的时候可以不加花括号
但是建议都加上
注意点
1、while() ;
总是加这个分号
2、总是漏写 { }
缩进不会改变代码的执行结构
书写规范
1、花括号位置
朴实无华的美
2、花括号成对写
3、注意缩进
条件的意义
条件成立(真),则执行,不成立(假),则跳过不执行
真
编程里,非0即真(通常用1代表真), 1 2 3 4 5 6 7 -1 -2 -3 浮点数
假
0就是假
只有一个条件
关系运算符
结果
0
1
演示计算结果
> ,< ,== ,!= ,>= ,<=
不要加空格
多个条件并存
逻辑运算符
&&
真&&真 == 真
真&&假==假
假&&假==假
||
真||真 == 真
真||假==真
假||假==假
!
!真==假
!假==真
&& ,||
&& 的优先级 高于||
短路原则
&&
遇到0就不判断了
||
遇到1就不判断了
目的
减少不必要的运算,提高程序运行效率
可控循环的三要素
循环控制变量要有初始值
演示没有初始值的情况
循环执行条件
真 == 1;
条件一定要有循环控制变量的参与
假 == 0;
循环控制变量有规律的值变化
没规律也是不可控的
死循环
原因
条件一直为真
不可控的了
死循环
循环次数不确定
for(初始值;条件;变化)
格式
for (语句1; 语句2; 语句3) { code; // 代码块 }
打印1 2 3 4 5
1、循环控制变量赋初始值
2、循环执行条件
3、循环控制变量变化
对比while循环
理解
while循环的一个集中写法,即循环三要素都写在一行
1、for循环更容易看清循环结构,用while循环,如果代码块内容多的话不容易看清
2、选择上,大家随便用,哪个熟悉用哪个
for循环结构执行次序
语句1
语句2
语句3
代码块
语句1 只执行一次
结构注意点
0、语句1的位置,Visual Studio2013和Visual Studio2015可以写成int i = 0; ,即可以在里面定义循环控制变量
1、三条语句中间是分号,不是逗号
2、for (语句1; 语句2; 语句3);
后面的这个分号会造成执行结果不对,即循环代码块只有一个空语句
3、一定要加上花括号
for循环的灵活性
语句1
1、通常是做循环控制变量的初始值
2、可以写多条赋值语句,之间用逗号分隔
3、可以什么都不写
分号一定不能丢
4、可以写其他表达式,甚至写个函数调用都行
综合:只要是合法语句都能写,通常用来循环控制变量的初始值
语句2
1、必须写条件
2、可以什么都不写,表示真,死循环
3、甚至可以加个函数
综合:只要是合法语句都能写,通常作为条件
语句3
1、一般写循环控制变量的变化
2、可以写多个变化,逗号隔开
3、可以啥都不写
变化可以放在代码块内
综合:只要是合法语句都能写,通常用来循环控制变量的变化
for(;;) == while(1)
循环嵌套
循环可以嵌套
嵌套的形式
for(;;) { for(;;) { for(;;) ....... } }
嵌套的执行次数
注意点
嵌套的话,for循环的结构比while会更清晰
内层循环第一个语句不设置
语法通过,但是逻辑上可能不通过
嵌套循环,两层循环用一个循环控制变量可能会出问题
入口条件循环,意思就是条件位置在循环入口处
退出条件循环
do{} while(条件)
do { code;// }while(条件);
最后要有分号
条件的意义跟之前我们说的条件都是一样的
执行的顺序
与while和for循环的区别
do while至少执行一次,即使我们的条件为假,也执行一次代码块
执行多次的话效果是一样的
退出条件循环
break和continue
break跳出所在循环
配合if使用
if (条件) { 代码快; }
循环之外不能写break
嵌套只跳出所在循环,不是跳出所有循环
continue执行下一次循环
所在循环
表达式
定义
表达式是一种有值的语法结构,它由运算符(变量、常量、函数调用返回值)结合而成
每个表达式一定有一个值
变量常量表达式
例子
a, 12, 12.4
值
就是变量或者常量本身的值
作为条件的时候
非0即真,0即假
算数表达式
例子
a+b c*d+a 12/3+d i++ --a
值
就是计算的结果
作为条件的时候
非0即真,0即假
赋值表达式
例子
a=12 a+=12 a = c= d
值
赋值完a的值,即为结果
作为条件的时候
非0即真,0即假
关系表达式
例子
a > b 2 ==3
值
1 0
作为条件的时候
非0即真,0即假
逻辑表达式
例子
a && b c||d !a
值
1 0
作为条件的时候
非0即真,0即假
复合表达式
例子
x = ( y = (a + b + a > 4), z=10)
值
依据运算符优先级和结合性得到结果
作为条件
非0即真,0即假
逗号表达式
例子
(1,2,3,4,a)
值
最右侧的值是逗号表达式的结果
作为条件的时候
非0即真,0即假
其他的
有返回值的函数也是一个表达式
其他运算符
语句
表达式+; 就是语句了
选择判断
if (条件){} else if(条件){} else{}
格式
只有一种情况
if (条件) { 代码块 }
加花括号
当if代码块只有一条语句的时候,可以不加花括号,但是建议加上
不要随意加分号
条件的写法
if (a == 1, 2,3,4,5)
区间条件写法
12<a<14
比如 a == 15
a > 12 && a < 14
if (a = 2)
if (2 == a)
嵌套
程序从上向下进行执行
代码缩进
可读性
有两种情况(二选1)
if(条件) { 代码块 } else { 代码块 }
2选1
逻辑上的区别
执行上的区别
注意点
加{ }
不要随意加分号
else 后面没有条件
else 必须跟if 挨着
嵌套
注意else与谁匹配
else跟同层的,上边最近的if匹配
条件运算符
?:
条件运算符是C语言为if else提供的简写形式
意义功能相同
形式
条件?真:假
三个部分,每个部分都是一个表达式
三个操作数,所以是唯一 一个三元运算符
意义
if (条件) 真 else 假
异同
写法不同
眼睛就能看出来
条件运算符语法更简洁
依赖编译器,条件运算符可能会产生更简洁的代码
选择
大家习惯哪个用哪个
优先级
低
结合性
从右向左
有多种情况(多选1)
if(条件) { 代码块 } else if(条件) { 代码块 } else { 代码块 }
多选1
不能随便加分号,不能将结构分离
else跟同层的,上边最近的else if匹配
嵌套
对比多个if并列的写法
写个例子
1、重复录入学生的成绩, 2、打出该成绩的评级,不及格(<60),及格(60~70),中(70~80) ,良好(80~90),优秀(90~100) 3、输入-1退出系统 4、输入非法数据提示重新输入,并重新输入 5、提示欢迎使用本系统与感谢使用本系统
switch(ID) { case ID: break;}
引出
菜单例子
添加一个学生---1
删除一个学生---2
浏览学生信息---3
退 出 系 统---4
格式
switch(匹配标签) { case ID1: 语句1;break; case ID2: 语句2;break; ..... default: 缺省语句; }
结构执行顺序
1、计算switch中表达式的值
2、按顺序扫描标签
发现有匹配的值,就进入标签中执行
执行到break,跳出switch结构
如果没有发现一样的,就最后进入default
执行到最后的花括号结束
注意点
switch后面没分号,case后面是冒号,case和标签之间要有空格,break要有
break跳出switch
循环内部有switch,switch内部有break,那么跳出switch,不跳出循环
结构代码风格
条件 整型表达式
浮点型不行
范围的语法通过,但是意义改变
标签 整型常量表达式
要有空格,必须是冒号
标签内部定义变量需要加上花括号
标签重复的时候
default也可以不写,那就没有默认处理
对比if else if else
if 适合复杂条件,比如范围,浮点型比较,复合表达式(与、或、非), switch就不方便
书上讲到:完成同样的功能,switch稍微快一点,这点效率不值一提。所以,使用的时候根据实际情况,哪个方便用哪个
灵活性
有无break
比else if 更灵活多变
表示范围
goto
说明
原则上,我们C语言根本不需要goto语句结构。C语言中continue和break 是goto的特殊形式,换句话说,goto的功能完全可以用break和continue代替。 大量的使用goto会使我们的程序结构非常乱,难懂。所以建议不要滥用,能不用就不用,甚至在C语言中,这种使用goto语句是不被接受的
可以根据逻辑,写成循环结构或者跳转结构
goto可以根据不同的写法,实现循环或者跳转
跳转
goto step1; 其他代码 step1: 其他代码
循环
step2: 其他代码; if( ) goto step3; goto step2; step3: 其他代码
有一种情况,是可以被接受的
跳出多层嵌套循环
函数
声明,定义
函数类型
无参数,无返回值
C:标准C语言,函数没参数需要写个void
不写void与写void的区别
无参数,有返回值
return ;
有参数,有/无返回值
传值与传址
形参与实参
参数缺省值
数组参数
函数指针
[ ]的三种作用
声明变量的时候有[ ],表示声明的是数组变量
函数参数有[ ],此时表示指针
地址+[ ],表示下标运算
递归函数
展开再理解
内存管理
内存分区
变量的作用域
堆区,栈区,全局区,字符常量区,代码区
多文件
位运算
进制转换
二进制与十六进制,十进制与十六进制, 十进制与二进制
位运算符
&,|,~,^
区别于逻辑运算符
类型转换
隐式类型转换
显式类型转换
强制类型转换
文件操作
文件操作流程
打开文件
fopen
文件的打开方式
文本模式
二进制模式
操作文件
fread(),fwrite(),fgets()...
关闭文件
fclose()
文件写入磁盘的钥匙
其他
类型修饰符
auto
自动变量
通常的局部变量
static
静态变量
const
注意修饰的位置
extern
预处理
宏
#define
替换
参数宏
链接/,#,##
包含头文件
#include
“” 与 <>的区别
防止头文件重复包含
#ifndef AAA #define AAA #endif
类型重命名
typedef
函数类型
结构体类型
指针类型
基本数据类型
多文件