C语言提高学习及总结

1 导读

该提高部分主要从基本知识的强化入手,对C语言的基础知识难点进行进一步加强。主要包括:内存管理(内存四区)、指针的强化(指针是C语言的半壁江山)、多级指针、多维数组(数组指针、指针数组)、结构体(结构体内嵌套指针的深浅拷贝)、数据结构的入门(链表)及函数指针(回调函数:C语言的另外半壁江山)和函数的递归。

2 C语言提高

提高部分需要对基础知识掌握后再去学习,学习的判断标准可以自我检测(基础知识需要铭记于心),也可以通过基本算法进行判读(基本排序:冒泡和选择法排序的思想及代码需要非常熟练)才可以学习这一部分。

2.1 内存管理(内存四区)

2.1.1、标准
需要自己熟练掌握冒泡和选择排序的思想及代码、会封装函数及函数参数中的数组退化问题。
void fun(int a[]);// 4字节
int a[] = {1, 2, 3}; // 12个字节
2.1.2、数据类型
需要掌握类型的本质和作用,自定义类型和sizeof关键字,void类型的使用。
a)如果函数没有返回值,必须用void修饰: void fun(int a);
b)如果函数没有参数,参数可以用void修饰: int fun(void);
c)不能定义void类型的普通变量, void a; //error,不能确定分配多大空间
d)void p; //ok, 万能指针, 指针类型都是 4 个指针,函数参数,函数返回值
int b[10];
//b, &b的数组类型不一样
//b, 数组首元素地址, 一个元素4字节,+1, +4
//&b, 整个数组的首地址,一个数组4
10 = 40字节,+1, +40

2.1.3、变量
需要掌握变量的本质、定义和赋值。
4)变量的赋值:1)直接 2)通过指针间接
4)重点:没有变量,哪来内存,没有内存,哪里内存首地址
5)变量三要素(名称、大小、作用域),变量的生命周期

2.1.4、内存四区(栈区、堆区、全局区、代码区)
1)栈区:系统分配空间,系统自动回收,函数内部定义的变量,函数参数,函数结束,其内部变量生命周期结束
2)堆区:程序员动态分配空间,由程序员手动释放,没有手动释放,分配的空间一直可用
3)全局区(全局变量和静态变量,里面又分为初始化区和未初始化区,文字常量区:字符常量):整个程序运行完毕,系统自动回收
4)内存四区模型图(C语言学好指针的关键)
5)a) 栈区地址生长方向:地址由上往下递减
b) 堆区地址生成方向:地址由下往上递增
c) 数组buf, buf+1 地址永远递增

2.1.5、函数调用模型
1)程序各个函数运行流程(压栈弹栈,入栈出栈,先进后出)

2.2 指针的强化

2.2.1、指针强化
需要掌握指针的本质、定义和使用。还有就是指针变量和指针指向的内存、使用指针操作写内存时需要注意该内存是否可写(定义在文字常量区)。
通过指针进行间接赋值的三大条件。
重要:如果想通过函数形参改变实参的值,必须传地址
1、值传递,形参的任何修改不会影响到实参
2、地址传递,形参(通过*操作符号)的任何修改会影响到实参
3、不允许向NULL和未知非法地址拷贝内存
4、void *指针的使用:可以指向任何指针和地址,但使用时尽量进行转换。
5、栈区返回变量的值和变量的地址区别
6、.c -> 可执行程序过程
预处理:宏定义展开、头文件展开、条件编译,这里并不会检查语法
编译:检查语法,将预处理后文件编译生成汇编文件
汇编:将汇编文件生成目标文件(二进制文件)
链接:将目标文件链接为可执行程序
程序只有在运行才加载到内存(由系统完成),但是某个变量具体分配多大,是在编译阶段就已经确定了,换句话说,在编译阶段做完处理后,程序运行时系统才知道分配多大的空间,所以,很多时候说,这个变量的空间在编译时就分配(确定)了。

7、 指针做函数参数的输入输出特性
输入:主调函数分配内存
输出:被调用函数分配内存

8、变量内存的值和变量的地址
int a = 0;
a变量内存的值为0
a变量的地址(&a)绝对不为0,只要定义了变量,系统会自动为其分配空间(一个合法不为0的地址)

2.2.2、字符串操作
需要掌握字符串的初始化以及字符串与字符数组的区别、sizeof和strlen的区别、数组法和指针法操作字符串、字符串拷贝函数、一些常用字符串应用模型:使用标准库函数找字符串中另一个字符(串)的个数,两头堵模型。

2.3 多级指针

2.3.1、const的使用:常量指针和指针常量的使用与注意事项。

2.3.2、多级指针
1)如何定义合适类型的指针变量
//某个变量的地址需要定义一个怎么样类型的变量保存
//在这个类型的基础上加一个*

2)二级指针做输出
输入:主调函数分配内存
输出:被调用函数分配内存

3)二级指针做输入的三种内存模型
指针数组做输入、二维数组做输入、在堆上打造二维数组做输入。

2.4 多维数组

2.4.1 需要掌握一维数组的定义、初始化和使用。数组类型和数组名及数组名取地址的含义、数组类型的定义。
2.4.2 数组指针变量的定义(三种):后两种常用。
1、先定义数组类型,再定义数组指针变量。
2、先定义数组指针类型,再定义变量。
3、直接定义数组指针变量。
2.4.3 多维数组的本质、定义、初始化及使用。
int a[3][5] = { 0 };
a:
二维数组首元素地址
代表首行地址,相当于一维数组整个数组的地址,相当于上面的 &b,本来就是一个二级指针
//(重要)首行地址 --> 首行首元素地址(加*)
a:首行首元素地址,相当于一维数组首元素地址,相当于上面的 b
a + i -> &a[i]: 第i行地址
//(重要)某行地址 --> 某行首元素地址(加

*(a+i) -> *&a[i] -> a[i]: 第i行首元素地址
//第i行j列元素的地址,某行首元素地址 + 偏移量
*(a+i)+j -> a[i]+j -> &a[i][j]: 第i行j列元素的地址
//第i行j列元素的值,第i行j列元素的地址的基础上(加 *)
((a+i)+j) -> a[i][j]: 第i行j列元素的值
int a[3][5] = { 0 };
sizeof(a): 二维数组整个数组长度,4 * 3 * 5 = 60
sizeof(a[0]):a[0]为第0行首元素地址,相当于测第0行一维数组的长度:4 * 5 = 20
sizeof(a[0][0]):a[0][0]为第0第0列元素(是元素,不是地址),测某个元素长度:4字节
多维数组名,实际上是一个数组指针,指向数组的指针,步长为一行字节长度。

2.4.4 二维数组做形参的三种形式:
//一维数组做函数参数退化为一级指针
//二维数组(多维数组)做函数参数,退化为数组指针
int a[3][5] = { 0 };
void print_array1(int a[3][5]);
//第一维的数组,可以不写
//第二维必须写,代表步长,确定指针+1的步长 5*4
void print_array2(int a[][5])

	//形参为数组指针变量,[]的数字代表步长
	void print_array3(int (*a)[5]);

	//a+1和二维数组的步长不一样
	//这里的步长为4
	//上面二维数组的步长为 5 * 4 = 20
	void print_array3(int **a); //err

2.4.5 指针数组(它是数组,每个元素都是指针)的定义、初始化和使用。
1)指针数组的定义
//指针数组变量
//[]优先级比*高,它是数组,每个元素都是指针(char *)
char *str[] = { “111”, “2222222” };
char **str = { “111”, “2222222” }; //err

2)指针数组做形参
void fun(char *str[]);
void fun(char **str); //str[] -> *str

3)main函数的指针数组
//argc: 传参数的个数(包含可执行程序)
//argv:指针数组,指向输入的参数
int main(int argc, char *argv[]);

2.5 结构体

2.5.1、结构体类型基本操作:定义(struct关键字、使用typedef定义别名);结构体变量的定义:先定义类型再定义变量、定义类型的同时定义变量;结构体变量初始化(定义时初始化);结构体的使用:变量法和指针法操作结构体成员;结构体数组的定义及初始化。

2.5.2 结构体嵌套指针、结构体赋值、结构体的浅拷贝和深拷贝。

2.5.3、结构体字节对齐(以空间换时间)。

2.6 链表入门及函数指针

2.6.1、链表
1、数组和链表的区别
数组:一次性分配一块连续的存储区域
优点:
随机访问元素效率高
缺点:
需要分配一块连续的存储区域(很大区域,有可能分配失败)
删除和插入某个元素效率低

链表:现实生活中的灯笼
优点:
	不需要一块连续的存储区域
	删除和插入某个元素效率高
缺点:
	随机访问元素效率低

2、相关概念
节点:链表的每个节点实际上是一个结构体变量,节点,既有 数据域 也有 指针域
typedef struct Node
{
int id; //数据域
struct Node *next; //指针域
}SLIST;

尾结点:next指针指向NULL

3、结构体套结构体
typedef struct A
{
int b;
}A;
/*
1、结构体可以嵌套另外一个结构体的任何类型变量
2、结构体嵌套本结构体普通变量(不可以)
本结构体的类型大小无法确定,类型本质:固定大小内存块别名
3、结构体嵌套本结构体指针变量(可以)
指针变量的空间能确定,32位, 4字节, 64位, 8字节
*/
typedef struct B
{
int a;
A tmp1; //ok
A *p1; //ok

	//struct B tmp2; 
	struct B *next; //32位, 4字节, 64位, 8字节	
}B;

2.6.2、函数指针
1、指针函数,它是函数,返回指针类型的函数
//指针函数
//()优先级比*高,它是函数,返回值是指针类型的函数
//返回指针类型的函数

2、函数指针,它是指针,指向函数的指针,(对比数组指针的用法)

一个函数在编译时被分配一个入口地址,这个地址就称为函数的指针,函数名代表函数的入口地址。函数指针变量,它也是变量,和int a变量的本质是一样的。
//定义函数指针变量有3种方式:
(1)先定义函数类型,根据类型定义指针变量(不常用)
(2)先定义函数指针类型,根据类型定义指针变量(常用)
(3)直接定义函数指针变量(常用)

3、函数指针数组,它是数组,每个元素都是函数指针类型
4、回调函数,函数的形参为:函数指针变量

2.6.3、函数的递归
递归:函数可以调用函数本身(不要用main()调用main(),不是不行,是没有这么做,往往得不到你想要的结果)
(1)普通函数调用(栈结构,先进后出,先调用,后结束)
(2)函数递归调用(调用流程和上面是一样,换种模式,都是函数的调用而已)

3 总结

其它总结见:
https://download.csdn.net/download/zhaoyinlo3/12912070

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值