Linux和C语言(Day10)
一、学习内容
-
指针数组
-
本质是一个数组,数组的类型是指针,也就是存储的是地址
-
整型数组:int a[5]={1,2,3,4,5}//类型是整数,存的都是整数
-
指针数组:int *a[5]={&,&,&,&,&}
-
-
指针和字符串
-
指针指向字符数组
-
指针指向字符串常量
-
-
函数
-
函数的定义
-
实现特定功能的代码块
-
-
函数的作用
-
分工合作、提高效率
-
提高代码的重用率
-
功能独立。便于维护与修改
-
-
函数的分类
-
用户角度
-
库函数——前辈写好的,放在include目录中,以.h做后缀的头文件
-
eg:strlen\strcmp\strcpy\strcat\getchar\putchar\gets\puts\printf\memset\bzero
-
-
自定义函数——用户自己写的函数
-
eg:int my_strlen(const char *s){ int count=0; while(*s !='\0'){ count++; s++; } }
-
-
-
参数角度
-
有参函数
-
无参函数
-
-
返回值角度
-
无返回值函数
-
有返回值函数
-
-
-
函数的定义语法格式
-
定义函数: 存储类型 数据类型 函数名(参数列表){ 实现特定功能的代码块 }
-
分析: 存储类型——自动存储(auto)、寄存器存储(register)、静态存储(static)、外部存储(extern)和常量存储(const)等【函数若省略存储类型,默认是extern】 数据类型——基本、构造、指针、空,控制的是函数的返回值,一个函数可以返回一个值,若没有返回值设为void 函数名——符合命名规范 ①驼峰命名法SumFunction()②连字符法 sum_fun() ()——函数的标志,不可以省略 (参数列表)——参数列表可以为空void,也可以不为空,可以写多个参数,中间以逗号隔开。定义函数时的参数——形参,只起到一个占位符的作用,没有实际意义。当你去使用该函数的时候,往里面按传你想传的参数——实参,有实际意义。 {}——中间写代码块
-
函数三要素:功能,返回值,参数
-
-
使用/调用函数
-
第一种: int fun(int a,int b){ return a+b; } //定义有参有返回值函数 并 实现功能 printf("%d\n",fun(1,2));//使用函数fun
-
第二种: void show(char *name){ printf("Welcome %s",name) } //定义有参无返回值函数 并 实现功能 show("张xx"); //使用函数show
-
-
-
函数声明
-
一个程序中有且只有一个主函数main,作为程序的入口部分。C语言的代码执行是从上往下依次执行的,若顺序颠倒的话,读到9 10 11 12行出现不明函数。 这个时候我们可以使用函数声明。
-
定义变量初始化
-
int a=10; int a; a=10;
-
-
定义函数并实现
-
第一种:int my_sum(int a,int b){return a+b;}
-
第二种:①int my_sum(int a,int b); //int my_sum(int,int); ②int my_sum(int a,int b){return a+b;}
-
-
函数声明的意义是什么呢
-
提高效率【main可以放在前面,也可以放在后面,但是建议放在最前面,作为入口】
-
便于分工合作
-
-
-
-
讲解有参函数
-
形参 和 实参
-
形参——定义时的参数,形式上的参数,没有实际意义,语法上必须带有数据类型 void fun(int a,int b); void fun(int a[],int n); void fun(char *s); 可以是:变量、数组、指针
-
实参——调用时的参数,实际上的参数,有实际意义,实参语法上不加数据类型,直接传参名字 fun(a,b) fun(1,2) fun(a+2,3) fun(a,strlen(a)) fun(3,fun(4,5)) 可以:常量、变量名、数组名、指针名、表达式
-
注意:实参必须和形参保持数据类型、顺序、数量一一对应。【名字无所谓】
-
eg: void fun(int a,float b,char c); fun(3 , 4 , 'A'); //正确 fun(3,4); //错误 int x=3; float y=3.5; char z='a'; fun(x,y,z); //正确
-
-
-
值传递 和 地址传递
-
值传递
-
形参和实参分处不同的存储单元形参变实参不变
-
实参【原件】拷贝出一份值给形参【复印件】,复印件修改原件不变
-
-
-
地址传递
-
形参和实参除处在相同的存储单元里面形参变、实参也变
-
实参【原件】直接给了形参
-
-
-
-
数组传参方式
-
整型一维数组传参
-
一维数组传参需要两个参数——数组、数组的长度
-
void FA(int a[10],int n);
-
void FA(int a[],int n);
-
void FA(int *a,int n);
-
-
-
整型二维数组传参
-
二维数组传参需要三个参数——数组、行数、列数
-
void FB(int b[2][3], int hang, int lie);
-
void FB(int b[][3], int hang, int lie);
-
void FB(int (*b)[3], int hang, int lie);
-
-
-
-
-
脑图
二、作业
1.函数之间参数传递的方式有________和________。(富士安全)
解答:
值传递和地址传递
值传递:将实际参数的值复制给形参,函数内部修改形参不会影响实际参数。
地址传递:传递的是参数的引用(或指针),函数内部修改形参会直接影响实际参数。
2.简述:函数之间两种参数传递方式的区别。
解答:
值传递:
概念:将实际参数的值复制给函数的形参,函数内部操作的都是形参的副本。
特点:函数对形参的修改不会影响实际参数。适用于不希望函数修改原始数据的情况。
地址传递:
概念:传递的是实际参数的地址或引用,函数对形参的操作会直接影响实际参数。
特点:函数可以直接修改实际参数的值。适用于需要在函数内部修改外部变量的场景。
3.函数main 有那些参数?各代表什么含义? 假如程序a调用如下所示:a 123写出程序a中main 函数的参数的具体值。 (拓仓科技)
解析:
int argc, const char *argv[]一共是两个参数,argc代表的是调用命令的个数,而agrv[]是调用命令时所存的数组
解答:
当程序调用a 123时,参数的值会发生改变,变为agrc=2 argv[0]=a、argv[1]=123
4.用指针作函数参数,编程序求一维数组中的最大和最小的元素值
思路:①主函数中定义一维数组int a[]={11,8,89,7,5,4,32,15,21} , max , min;
②定义函数 void arr_max(int *a, int n,int *max); //a接收数组 n表示长度 max接收最大值
③定义函数 void arr_min(int *a, int n,int *min); //a接收数组 n表示长度 min接收最小值
④在主函数中输出最大值和最小值
解答:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// 函数声明:用于找到数组中的最大值
void arr_max(int *a, int n, int *max);
// 函数声明:用于找到数组中的最小值
void arr_min(int *a, int n, int *min);
int main(int argc, const char *argv[]) {
// 初始化一个整型数组 a,并赋值
int a[] = {11, 8, 89, 7, 5, 32, 15, 21};
// 定义变量 max 和 min,分别存储数组中的最大值和最小值
int max, min;
// 计算数组的长度
int len = sizeof(a) / sizeof(int);
// 调用 arr_max 函数找到最大值
arr_max(a, len, &max);
// 调用 arr_min 函数找到最小值
arr_min(a, len, &min);
// 输出最大值和最小值
printf("最大值为%d 最小值为%d\n", max, min);
return 0;
}
// 函数定义:找到数组中的最大值并存储到 max 中
void arr_max(int *a, int n, int *max) {
// 假设数组的第一个元素为最大值
*max = a[0];
// 遍历数组的每一个元素
for (int i = 1; i < n; i++) {
// 如果当前元素比已知最大值大,更新最大值
if (*max < *(a + i)) {
*max = *(a + i);
}
}
}
// 函数定义:找到数组中的最小值并存储到 min 中
void arr_min(int *a, int n, int *min) {
// 假设数组的第一个元素为最小值
*min = a[0];
// 遍历数组的每一个元素
for (int i = 1; i < n; i++) {
// 如果当前元素比已知最小值小,更新最小值
if (*min > a[i]) {
*min = *(a + i);
}
}
}
结果展示:
5.用指针接收函数参数,编程序求二维数组的输入、输出、求最大值
思路:1》主函数中定义二维数组 int a[3][4];
2》自定义函数void arr_input(int (*a)[4] , int H , int L);
3》自定义函数void arr_output(int (*a)[4] , int H , int L);
4》自定义函数void arr_max(int (*a)[4] , int H , int L);
5》主函数调用函数实现
解答:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// 函数声明:用于输入二维数组元素
void arr_input(int (*a)[4], int H, int L);
// 函数声明:用于输出二维数组元素
void arr_output(int (*a)[4], int H, int L);
// 函数声明:用于找到二维数组中的最大值
void arr_max(int (*a)[4], int H, int L);
int main(int argc, const char *argv[]) {
int a[3][4]; // 定义一个 3x4 的二维数组
// 调用函数,输入、输出和查找最大值
arr_input(a, 3, 4); // 输入数组元素
arr_output(a, 3, 4); // 输出数组元素
arr_max(a, 3, 4); // 查找并输出数组中的最大值
return 0;
}
// 函数定义:输入二维数组的元素
// 参数:二维数组指针 int (*a)[4],数组的行数 H 和列数 L
void arr_input(int (*a)[4], int H, int L) {
// 遍历二维数组的每一行和每一列,依次输入元素
for (int i = 0; i < H; i++) {
for (int j = 0; j < L; j++) {
// 通过指针输入元素 *(a+i) 指向第 i 行,+j 指向第 j 列
scanf("%d", *(a + i) + j);
}
}
}
// 函数定义:输出二维数组的元素
// 参数:二维数组指针 int (*a)[4],数组的行数 H 和列数 L
void arr_output(int (*a)[4], int H, int L) {
// 遍历二维数组的每一行和每一列,依次输出元素
for (int i = 0; i < H; i++) {
for (int j = 0; j < L; j++) {
// 通过指针输出元素,*(a+i)+j 指向二维数组的具体元素
printf("%5d", *(*(a + i) + j));
}
printf("\n"); // 输出换行符,分隔每一行
}
}
// 函数定义:找到二维数组中的最大值并输出
// 参数:二维数组指针 int (*a)[4],数组的行数 H 和列数 L
void arr_max(int (*a)[4], int H, int L) {
// 初始化 max 为数组的第一个元素
int max = *(*(a));
// 遍历二维数组的每一行和每一列,查找最大值
for (int i = 0; i < H; i++) {
for (int j = 0; j < L; j++) {
// 如果找到比当前 max 更大的元素,更新 max
if (max < *(*(a + i) + j)) {
max = *(*(a + i) + j);
}
}
}
// 输出最大值
printf("最大值为 %d\n", max);
}
结果展示:
三、总结
1. 学习内容概述
指针数组
理解了如何定义和使用指针数组,指针数组是数组的元素为指针的数组,常用于存储字符串数组.
函数指针
学习了如何定义函数指针及其用途,函数指针可以指向一个函数,并通过指针调用该函数,这对于实现回调机制或动态函数调用非常有用。
宏定义
掌握了如何通过`#define`指令进行宏定义,宏定义可以用于定义常量、简单的函数和代码块,提高代码的可读性和可维护性。
自定义命令
学习了如何在终端中创建并使用自定义命令,便于简化重复性操作,提升开发效率。
2. 学习难点
函数指针的使用
函数指针本身的定义和调用机制较为复杂,尤其是将其作为参数传递给其他函数时,理解函数指针与普通指针的区别和使用场景是一个难点。
指针数组与数组指针的区别
在实际使用中,指针数组和数组指针容易混淆。指针数组是数组的元素为指针,而数组指针则是指向数组的指针,需要通过不同的方式访问元素。
条件编译的灵活性
条件编译允许根据不同条件选择性编译代码块,虽然灵活,但也需要对项目的整体结构和预处理流程有清晰的理解,以避免逻辑混乱或错误。
3. 注意事项
函数指针的声明和使用
函数指针的声明语法较为复杂,尤其是在嵌套使用时,容易产生混淆。建议先清晰理解函数指针的基本定义和用法,然后逐步应用到更复杂的场景中。
指针数组与数组指针的区别
在使用过程中,要明确区分这两者的作用及其内存分布情况。例如,指针数组可以直接用下标访问其中的指针,而数组指针则需要先解引用才能访问数组中的元素。
宏定义的边界条件
在定义宏时,确保考虑到所有边界条件,使用括号包裹宏参数和表达式以防止求值优先级问题。另外,宏不要过度复杂化,以免影响代码的可读性和维护性。
自定义命令的命名规范
在创建自定义命令时,确保命名合理,不要与系统内置命令冲突,且命令的功能要与其命名相符,方便日后调用和维护。
4. 未来学习的重点
深入学习函数指针与回调函数
函数指针可以用于更复杂的编程场景,特别是回调函数和动态链接库的实现。在后续学习中可以深入研究其在大型系统中的应用。
宏定义的高级用法
进一步学习宏的条件编译和递归展开的用法,尤其是复杂宏定义中的使用技巧,以及如何避免宏定义带来的副作用。
指针在数据结构中的应用
指针是C语言中构建动态数据结构(如链表、树等)的基础,在深入理解指针操作后,可以进一步学习如何通过指针实现这些数据结构的增删查改操作。
代码优化与重构
通过自定义命令、函数指针、宏定义等手段,可以实现代码的优化与重构,提升程序运行效率和可维护性,建议在今后的学习中将这些技巧应用到实际项目中,积累经验。