一、函数
函数:实现特定功能的代码块,C语言称为函数语言。
作用:解决重复代码所带来的劳动量,使程序开起来更加简洁,运行速度快。
1> 主函数main: 一个程序里面只能有一个主函数,主函数可以调用任何函数,但是主函数不可以被任何一个函数调用
2> IO函数:scanf printf getchar putchar gets puts
3> str函数:strlen strcat strcpy strcmp
1.1 函数定义
定义格式:
数据类型 函数名(参数列表) //函数头
{
函数体 ; //{}里面的所有称为函数体
}
int main(int argc, const char *argv[])
{
return 0;
}
注意: 数据类型:基本类型、构造类型、指针类型、空类型
函数名: 需要满足名规范【但是在定义函数时,函数名不可以重复】
():函数的标志,不可以省略 参数列表:可以是多个,或者0个
{}:不可以省略
1.2 函数的分类
1> 库函数:系统自带的函数
2> 自定义函数:由程序员自己封装的函数
3> 有无参数函数:取决于参数列表
1.如果参数列表存在:则称为有参函数
2.如果参数列表不存在:则称为无参函数
4> 有无返回值函数:取决于函数的数据类型,
1.如果数据类型是void: 称为无返函数
2.如果数字局类型不是void:称为有返函数
void sum() //无参无返函数
int sum() //无参有返函数
void sum(int a,int b)//有参无返函数
int sum(int a,int b)//有参有返函数
1.3 无参无返函数+调用
定义格式
void 函数名(void)//void可写可不写 void sum()
{ {
int a=1,b=2;printf("a+b=%d",a+b);
} }
调用格式:
函数名(); sum();
1.3.1自定义函数写在主函数的上面
1.3.2自定义函数写在主函数的下面
1.3.3自定义函数声明写在预处理阶段
注意:自定义函数写在主函数下面运行速度,相对较快,对成员相对友好。
1.4 全局变量、局部变量
1.4.1 全局变量
全局变量:在函数外定义的变量。
作用域:从定义开始,到整个工程结束
1.4.2 局部变量
局部变量:在函数内定义,参数列表
作用域:在本函数内有效
空间: 当函数调用局部变量分配空间,函数调用结束,空间释放
1.4.3 全局变量和局部变量重名
1.5 有参函数
定义格式
void 函数名(参数列表) void sum(int m,int n) //多个参数之间使用逗号隔开
{ {
int a=1,b=2;printf("m+n=%d",m+n);
} }
调用格式:
函数名(参数变量名); sum(a,b);
1> 传参:被调函数需要使用主调函数的局部变量,传递参数
2> 实参和形参的类型
实际参数:变量 常量 表达式 函数
形式参数:变量 函数指针
3> 传参的注意事项
1.个数必须一致
2.类型可以不一致【以形参为主,发生自动或强制转换】
3.一 一对应
4.形式参数必须加数据类型
5. 形参和实参的变量名可以不一致
1.5.1 数组做参数
注意:数组做参数,只传递数组组名
1> 一维数组做参数,数组或指针接收
void fun(int arr[],int len);
void fun1(int *p,int len);
int main(int argc, const char *argv[])
{
int arr[]={11,22,33,44,55};//局部变量:只能在主函数
printf("sizeof(arr)=%ld\n",sizeof(arr));
int len=sizeof(arr)/sizeof(arr[0]);
fun(arr,len);//数组做参数传递数组名
fun1(arr,len);
return 0;
}
//在自定义函数fun输出
//形参的数组长度必须要大于等于实参的数据长度
//一般省略不写,默认是实际参数的数据长度
void fun(int arr[],int len) //arr看似是数组,实际是指针
{
for(int i=0;i<len;i++)
{
printf("%d\t",arr[i]);
} }
void fun1(int *p,int len)
{
for(int i=0;i<len;i++)
{
printf("%d\t",*(p+i));
} }
2> 字符串做参数,数组或指针接收
#include <stdio.h> #include <string.h>
#include <stdlib.h> void fun(char *p);
int main(int argc, const char *argv[])
{
char str[]="hello"; //局部变量,只能主函数使用
fun(str);
printf("str=%s\n",str);
return 0;
}
//在自定义函数中实现大转小,小转大写
void fun(char *p)
{
int len=strlen(p);
while(*p!='\0')
{
if(*p>='A' && *p<='Z')
{ *p+=32; }
else if(*p>='a' && *p<='z')
{
*p-=32;
}
p++; } }
3> 二维数组做参数,数组和指针接收
void output(int (*p)[3],int line,int row);
int main(int argc, const char *argv[])
{
int arr[][3]={11,22,33,44,55,66};//局部变量
int line=sizeof(arr)/sizeof(arr[0]);
int row=sizeof(arr[0])/sizeof(arr[0][0]);
output(arr,line,row);
return 0;
}
//自定函数实现输出
void output(int (*p)[3],int line,int row)
{
for(int i=0;i<line;i++)
{
for(int j=0;j<row;j++)
{
// printf("%d\t",arr[i][j]);
printf("%d\t",*(*(p+i)+j));
}
printf("\n"); } }
1.5.2 数组做参数练习
练习1:实现简单选择排序【一维数组做参数】
要求:使用指针接收
定义一个函数input实现输入
定义一个函数select_sort实现排序
定义一个函数output实现输出
练习2:实现字符串拷贝函数
1.6 有返回值函数
有返回值函数:主调函数需要使用被调函数的局部变量
注意:
1> 如果数据类型是void默认是无返回值函数
2> 如果返回的类型是int类型,可以默认不写
3> return: 返回 结束函数
格式1:return 表达式
格式2:return(表达式)
1.表达式:变量、常量、表达式
2.return位置可以任意,一般放在最后
3.return一次只可以返回一个变量
4.return可以写多个,但是只有第一个有效
5.当函数的返回类型和函数的数据类型不一致时,选用函数的数据类型
练习:计算字符串的长度
练习2:使用数组指针接收二维数组
1.定义有参有返函数,返回最大差值
2.定义有参有返函数, 返回最大和
1.7 指针函数
本质上一个是函数,主要返回的是一个指针
定义格式:
数据类型 *函数名(参数列表)
{
函数体;
return 地址;//注意地址不可以是局部变量的地址
}
题目:返回两个变量,需要借助于数组
练习:实现字符串拷贝函数
返回拷贝后字符串的首地址
1.8 函数指针
函数指针本质上是一个指针,用来指向函数的地址
定义格式:
数据类型 (*函数指针名)(参数列表);
void sum() //无参无返函数 void (*p)(void)
int sum() //无参有返函数 int (*p)(void)
void sum(int a,int b)//有参无返函数 void (*p)(int a,int b) -->void (*p)(int,int)
int sum(int a,int b,float c)//有参有返函数 int (*p)(int ,int,float)
注意:
1> 函数名表示函数的首地址
2> 没有办法计算函数所占空间的大小
3> 指针指向函数时,通过p调用,表示通过函数首地址直接调用函数,*p调用,表示通过函数首地址取值,根据值调用函数,注意:***p这里面的*个数可以是任意的。
1.8.1 函数指针练习
1.9 函数指针数组
函数指针数组本质上是一个数组,存储多个函数地址的
定义格式: 数据类型 (*函数指针数组名[常量表达式])(参数列表)
1.10 值传递和地址传递
1.10.1 值传递
1> 值传递的是值
2>实参和形参分别占有不同的存储空间
3>形参的改变不影响实参
4>单向传递:实参传递给形参
1.10.2 地址传递
1> 地址传递传递的地址:数组名,指针
2>地址传递传递的地址
3>形参和实参共享存储空间
4>形参的改变影响实参
5>单项传递:实参传递给形参
1.10.3两个数的交换练习
1.11 递归
递归:函数自己调用自己
1> 直接递归:函数自己调用自己 【死循环】
2> 间接递归:多个函数之间相互调用
3> 递归组成:
递归出口:结束递归的
递归前进段/递归公式: 递归调用
4> 循环可以用来解决问题规模较小的情况,递归常用来解决问题规模较大的时候,递归的运行速度慢
1.11.1 递归练习
1.12 存储类型
1,const[重点]
1> const修饰的变量不可以改变值
2> const修改的全局变量在.ro段
3> const修改的局部变量在栈区
4> const和指针
const int *p: *在const的右边,const修饰的是值,值不可以改变,地址可以改变
int const *p:*在const的右边,const修饰的是值,值不可以改变,地址可以改变
int * const p:*在const的左边,const修改的是地址,值可以改变,地址不可以改变
const int * const p:const修改的是地址和值,值不可以改变,地址不可以改变
2.static【重点】
2.1>static局部变量
static修饰的局部变量:延长生命周期,作用类似全局变量
注意:static修饰的局部变量,作用域依旧是fun函数中能使用 ,但是生命周期,也就是地址没有释放,延长至文件结束
2.2>static修饰全局变量
1.static修饰未初始化的全局变量,默认值为0
2.static修饰的全局变量,不可以跨文件使用
注意:在同一目录下创建或打开文件 vsp 文件名,
多个文件编译:
gcc 文件名1文件名2......
2.3>static修饰函数
static修饰的函数,不可以跨文件使用
3.extern
作用:引入外部变量,但是不可以引入静态变量的值
4.register【了解】
作用:寄存器变量,变量的空间在寄存器
1> 寄存器变量运行速度最快【寄存器> cache高速缓存 > 内存】
2> 寄存器变量不可以取地址操作
5.volatile【了解】
作用:防止内存优化,保持内存的可见性
6.auto
自动类型,一般默认不写就是自动类型的变量
自动类型修饰的局部变量在栈区,全局变量在静态区
1.13 宏
宏:只做替换,不做计算,不做正确性的检查,常量[不变]
1.13.1 宏定义
定义格式: #define 宏名 字符串
1.13.2 宏函数
1.13.3 多文件编译
头文件:预处理命令,全局变量,函数声明
自定义文件:自定义函数
主函数文件:主函数
作业:使用多文件编译【复习】
实现字符串技能
三个文件:main.c head.h test.c
1> 通过指针指向字符串,实现字符串逆置 void MyStrRev(char *p) //p要逆序的字符串
3> 字符串连接 char *MyStrcat(char *dest,const char *src)//dest:连接到dest字符串中,src字符串不变
4> 字符串比较函数int MyStrcmp(const char *s1,const char *s2) s1存储第一个字符串 s2存储第二个字符串
5> 字符串拷贝char *MyStrcpy(char *dest,const char *sec) dest:拷贝后的字符串 src源字符串
6> 字符串长度long Mystrlen(const char *s1) s1存储字符串
7> 使用递归实现斐波那契 FibonaciRec(n-1)+FibonaciRec(n-2)
8> 实现单词的逆置例如:char str[]="hello my student",输出结果是“student my hello”
思路:先整体对字符串逆置,在对每一个单词逆置
9> 指针指向一维数组,实现冒泡排序
10> 指针指向一维数组,实现简单选择排序