目录
前言
上周完成了C基础的课程,接下来就进入了【Linux系统和C高级阶段】,这周主要是接着接着C基础部分的,讲了一些更深一点的东西:接着函数部分,讲了函数指针、指针函数、函数指针数组和指向函数指针数组的指针,其中前三个是比较重要的,后面两个就属于套娃了,了解能看懂即可;再就是讲了typedef关键字、分文件编程。期间还针对比较难懂的指针,做了针对性的复习;然后讲了存储类型、枚举(enum)、结构体和共用体。
因为周五,上午时间复习,下午时间是阶段性考试,所以这周四天的时间里,讲的东西还是挺多的,而且也都是重点,因此这次复习总结,主要会针对函数和指针和数组的结合、结构体等几个方向,进行重点复习。
同样,写此文章,是想以这种碎碎念的方式回顾重点、重复盲点、加深印象,复习、总结和反思本周的学习,仅供后期自己回顾使用。知识体系不完善,内容也不详细,仅供笔者复习使用。如果是有需要笔记,或者对这方面感兴趣,可以私信我,发你完整的知识体系和详细内容的笔记。如有任何错误请多指正,也欢迎友好交流,定会虚心听取大佬的宝贵意见!
周一
一、函数
1.1 指针函数
本质是一个函数,返回值是一个指针类型。(后面是什么,就是什么)
格式:
返回值类型 *函数名(形参1类型 形参1名, 形参2类型 形参2名 ){}
注意:
指针函数不能返回局部变量的地址。
可以返回:
1.全局变量的地址
2.可以返回 static 修饰的局部变量的地址
3.也可返回由参数传递过来的地址 (例如 :strcpy strcat 等)
1.2 函数指针
本质是一个指针,指向一个函数。
格式:
返回值类型 (*函数指针名)(函数的形参列表);
典型使用场景:
定义一个函数指针,指向一个函数,函数名就是函数的首地址,函数指针指向函数以后,就可以通过函数指针来调用函数了。
回调函数:
#include <stdio.h>
int my_add(int x, int y){
return x+y;
}
int my_sub(int x, int y){
return x-y;
}
//所谓的回调函数,是将函数指针最为某个函数的形参
//在函数中通过函数指针调用函数时,具体调用的是哪个函数
//取决与 jisuan 这个函数的调用者给他传递的第三个参数
//传的是什么,就回过头去调用什么 所以叫做 回调函数
int jisuan(int x, int y, int (*p)(int, int)){
int temp = p(x,y);
return temp;
}
int main(int argc, const char *argv[])
{
int a = 20;
int b = 10;
printf("jisuan(a, b, my_add) = %d\n", jisuan(a, b, my_add));//30
printf("jisuan(a, b, my_sub) = %d\n", jisuan(a, b, my_sub));//10
return 0;
}
1.3 函数指针数组(了解)
本质是一个数组,数组中的每个元素都是一个函数指针。
格式:
返回值类型 (*函数指针数组名[数组长度])(函数的形参表);
1.4 指向函数指针数组的指针(了解)
本质是一个指针,指向一个函数指针数组。
格式:
返回值类型 (*(*指针名))(函数的形参表);
二、typedef
2.1 typedef的使用
本质上是用来给类型起别名的。
可以这么理解:原本的名字很长很复杂,可以简化的起一个简称或者绰号,这个简称或者绰号和和它的大名代表的意思是一模一样的。
例如:
typedef unsigned int m;//m本来是变量名,加上typedef之后就变成了新的类型名
//使用m定义的变量 和 使用unsigned int定义的变量是一样的
unsigned int v1 = 0;
m v2 = 0;
2.2 typedef和define的区别
1.宏定义是在预处理阶段完成替换的,typedef是类型的重定义,会在编译阶段检查;
2.宏定义只是简单的替换(无脑替换),typedef是类型的重定义;
3.typedef 指定后面必须要加分号,而define 不强制要求;
三、分文件编程
实际开发的过程中,会根据函数的功能不同,按照模块,分成多个文件处理。
.c 叫做源文件 .h 叫做头文件
.c 放的是函数的定义 .h 中放的是函数的声明
编译时 需要 gcc 后面加上所有的 .c 文件
简单归纳理解:程序篇幅过大,为了方便数理逻辑,main.c里放的都是函数调用;其他.c文件中放的是功能实现,.h文件里放的是函数声明。
注意事项:
1、.c文件中调用自己的头文件要用引号,不在同一目录下,要指明头文件所在的目录;
2、防止头文件重复包含,一般要在.h文件中加如下代码:
#ifndef __FILI_H__ //防止头文件重复包含的 __FILI_H__
#define __FILI_H__ //注意下划线和大小写;
#endif
周二
今天老师根据我们的学习情况,做了针对指针的复习。
一、指针复习
使用指针的三步骤:
1.定义指针变量
2.明确指针的指向(这一步必不可少)
3.操作指针指向的空间
1.1 一级指针
char *p;
//*p = 'H'; //错误的写法!! 指针的指向还不明确呢
int *q;
char value = 'N';
p = &value;
p++; //正确的 指针的指向向后偏移 1个char
*p++; //相当于 取 *p 的值 然后 p++
(*p)++; //相当于 value++, p的指向没有变
++(*p); //相当于 ++value, p的指向没有变
1.2 二级指针
int a = 10;
int *p = &a;
int **q = &p;
q++; //让 q 保存 p 后面一个 int * 大小的空间的首地址
*q++; //相当于 取出 *q 的值(也就是p的值) 然后 q++
(*q)++; //相当于 p++
p++; //让 p 指向 a 后面一个int 大小的空间的首地址
*p++; //相当于 取 *p 的值(也就是a的值) 然后 p++
(*p)++; //相当于 a++
1.3 指针和一维数组
格式:
char arr[] = "hello world";
char *p = arr;
等价关系:
arr[i] <==> *(arr+i) <==> p[i] <==> *(p+i)
区别:
arr是常量 p是变量
练习:
char *p = "helloworld";
char a[] = "helloworld";
char *str;
char b[32];
p++; //正确的 p的指向后移一个char
*p++; //正确的 相当于 先取*p 然后p++
(*p)++; //错误的 常量区的内容不允许修改
*p = 'H'; //错误的 常量区的内容不允许修改
p = "hqyj"; //正确的 p的指向可以改变
a++; //错误的 a是数组名 是常量 不能++
*a++; //错误的 a不能++
(*a)++; //正确的 相当于 a[0]++
*a = 'H'; //正确的 相当于 a[0] = 'H';
a = "hqyj"; //错误的 a是数组名 是常量 不能被赋值
str++; //正确的 指针str的指向 向后偏移一个 char
*str = 'H'; //错误的 str是野指针 不能对不确定的空间赋值
str = p; //正确的 指针变量的相互赋值
*str = 'H'; //错误的 常量区的内容不允许修改
b = a; //错误的 b也是数组名 是常量 不能被赋值
//数组一旦定义好了就不能整体赋值了
1.4 指针数组
本质是一个数组,数组中每个元素都是一个指针。
char *s[4] = {"./a.out", "hello", "world", "beijing"};
//s是一个指针数组 每个元素都是一个 char * 类型的指针
//s[0]指向 "./a.out"
//s[1]指向 "hello"
//s[2]指向 "world"
//s[3]指向 "beijing"
printf("%s\n", s[1]);//hello
printf("%s\n", *(s+2));//world
printf("%c\n", *(*(s+3)+3));//'j'
printf("%c\n", s[3][3]); //'j'
1.5 数组指针
本质是一个指针,指向一个二维数组,也叫行指针。
int s[2][3] = {1,2,3,4,5,6};
int (*p)[3] = s;//定义了一个行指针p指向 二维数组s
等价关系:
s[i][j] <==> *(s[i]+j) <==> *(*(s+i)+j) <==
==> p[i][j] <==> *(p[i]+j) <==> *(*(p+i)+j)
区别:
s 是常量 p 是变量
p++; //p的操作空间是 3个int 所以p++ 相当于p偏移了 12个字节
*p+1; //*p 操作空间是 1个int 所以 *p+1 是 第0行第1个元素的地址
*(*p+1);//第0行第1个元素
二、总结
根据老师课上讲的内容,加上我在csdn上找的一位大神对指针的理解的文章,结合起来还是比较好理解的:
- int p; //这是一个普通的整型变量
- int *p; //首先从P 处开始,先与*结合,所以说明P 是一个指针,然后再与int 结合,说明指针所指向的内容的类型为int 型.所以P是一个返回整型数据的指针
- int p[3]; //首先从P 处开始,先与[]结合,说明P 是一个数组,然后与int 结合,说明数组里的元素是整型的,所以P 是一个由整型数据组成的数组
- int *p[3]; //首先从P 处开始,先与[]结合,因为其优先级比*高,所以P 是一个数组,然后再与*结合,说明数组里的元素是指针类型,然后再与int 结合,说明指针所指向的内容的类型是整型的,所以P 是一个由返回整型数据的指针所组成的数组
- int (*p)[3]; //首先从P 处开始,先与*结合,说明P 是一个指针然后再与[]结合(与"()"这步可以忽略,只是为了改变优先级),说明指针所指向的内容是一个数组,然后再与int 结合,说明数组里的元素是整型的.所以P 是一个指向由整型数据组成的数组的指针
- int **p; //首先从P 开始,先与*结合,说是P 是一个指针,然后再与*结合,说明指针所指向的元素是指针,然后再与int 结合,说明该指针所指向的元素是整型数据.由于二级指针以及更高级的指针极少用在复杂的类型中,所以后面更复杂的类型我们就不考虑多级指针了,最多只考虑一级指针.
- int p(int); //从P 处起,先与()结合,说明P 是一个函数,然后进入()里分析,说明该函数有一个整型变量的参数,然后再与外面的int 结合,说明函数的返回值是一个整型数据
- Int (*p)(int); //从P 处开始,先与指针结合,说明P 是一个指针,然后与()结合,说明指针指向的是一个函数,然后再与()里的int 结合,说明函数有一个int 型的参数,再与最外层的int 结合,说明函数的返回类型是整型,所以P 是一个指向有一个整型参数且返回类型为整型的函数的指针
- int *(*p(int))[3]; //可以先跳过,不看这个类型,过于复杂从P 开始,先与()结合,说明P 是一个函数,然后进入()里面,与int 结合,说明函数有一个整型变量参数,然后再与外面的*结合,说明函数返回的是一个指针,,然后到最外面一层,先与[]结合,说明返回的指针指向的是一个数组,然后再与*结合,说明数组里的元素是指针,然后再与int 结合,说明指针指向的内容是整型数据.所以P 是一个参数为一个整数据且返回一个指向由整型指针变量组成的数组的指针变量的函数.
结合优先级,对指针进行组合,相对来说还是比较好理解的。

本周嵌入式培训主要深入讲解了C语言的高级特性,包括函数指针、typedef、分文件编程、存储类型、枚举、结构体和共用体。特别强调了函数指针的应用,如回调函数,以及结构体在内存对齐和位域上的细节。同时,复习了指针与数组的结合,复习过程中强调了理解指针的优先级和组合。此外,还讨论了内存分配方式和存储类型,如const、static、extern、register和volatile的关键区别。通过周末的练习题,巩固了冒泡排序、字符串转换、查找最大值和结构体数组的创建与操作等知识点。
最低0.47元/天 解锁文章


被折叠的 条评论
为什么被折叠?



