嵌入式养成计划-7-C语言函数&多文件编译&内存空间划分&递归

十七、函数

函数		:	C语句称为函数语言,实现特定功能的代码块。
函数优点	:	使用函数可以使程序更加简洁,提高运行速度,
			当重复执行同一块代码的时候,选择封装函数。
			
函数有很多,举几个例子:
	1.IO函数:scanf\printfgetcahr\putchar\gets\puts
	2.字符串函数:strlen\strcpy\strcat\strcmp
	3.数学函数:pow   sqrt
	4.主函数:main 
			一个程序只有一个主函数,主函数可以调用任何函数
			但是任何函数不可以调用主函数。

17.1 函数如何定义

格式:
		    存储类型  数据类型   函数名(参数列表)  //函数头
		    {
		        函数体;    
		    }
    
解析:
1.存储类型	:	auto\static\extern\register\const\volatile
2.数据类型	:	基本类型、构造类型、指针类型、空类型
3.函数名		:	满足命名规范
    	如	:	Max    max_fun
4.()		:	不可以省略,函数的标志
5.参数列表	:	可有可无,如果没有void ,有参数且多个中间使用逗号隔开
6.{}		:	函数不可以省略

17.2 函数的分类

  • 库函数:系统自带的函数【strlen…】

  • 自定义函数:程序员手动封装的函数
    1. 有/无参数
    在这里插入图片描述

    2. 有/无返回值
    在这里插入图片描述

void Sum(void);			无参无返回值函数
void Sum(int a,int b);	有参无返回值函数
int Sum(void);			无参有返回值函数
int Sum(int a,int b);	有参有返回值函数

17.3 无参函数

17.3.1 无返回值

定义格式:					eg:
    void 函数名(void)    	void Sum(void)
    {                   	{
        函数体;                int a=1,b=2;printf("%d",a+b);
    }                    	}
调用格式:
    函数名();            	Sum();

17.3.2 有返回值

定义格式:					eg:
    int 函数名(void)    		int Sum(void)
    {                   	{
       函数体;              	int a=1,b=2;printf("%d",a+b);
       return num;				return a+b;
    }                    	}
调用格式:
    函数名();            	Sum();
    或	:	int sum = Sum();

17.4 有参函数

17.4.1 无返回值

定义格式:					eg:
    void 函数名(参数列表)    	void Sum(int a, int b)
    {                   	{
        函数体;                printf("%d",a+b);
    }                    	}
调用格式:
    函数名();            	Sum(num1, num2);

17.4.2 有返回值

定义格式:					eg:
    int 函数名(参数列表)    	int Sum(int a, int b)
    {                   	{
       函数体;              		printf("%d",a+b);
       return num;				return a+b;
    }                    	}
调用格式:
    函数名();            	Sum(num1, num2);
    或	:	int sum = Sum(num1, num2);
普通变量做参数
实参		:	变量 常量 表达式
形参		:	变量
实参和形参的特点:
	1.个数一致
	2.类型建议一致,如果不一致以形参为主,发生自动或强制转换
	3.变量可以不一致
	4.形参实参需要一一对应
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void Sum(int m,int n);
//有参函数:被调函数需要使用主调函数的局部变量
#if 0
	实参:变量 常量 表达式
	形参:变量
	实参和形参的特点:
	1.个数一致
	2.类型建议一致,如果不一致以形参为主,发生自动或强制转换
	3.变量可以不一致
#endif
int main(int argc, const char *argv[])//主调函数
{
    float a=1.8999,b=2.2222;//局部变量,只能main函数使用
    Sum(a,b);//实际参数/实参:只写变量名
    return 0;
}

void Sum(int m,int n)//被调函数  形式参数/形参:需要添加数据类型
{
    printf("m+n=%d\n",m+n);
}
数组做参数

数组做参数值传递数组名,数组名表示数组的首地址

1.一维数组做参数
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

//形参数组的数组长度需要>=实际参数数组的个数
//省略形参数组长度,默认是实际实参数组的个数
void Output(int len,int arr[])//形参arr看似数组,实则是指针
{
    printf("arr=%ld\n",sizeof(arr));
    for(int i=0;i<len;i++)
    {
    printf("%d\t",arr[i]);
    }
}
int main(int argc, const char *argv[])
{
    int arr[]={11,22,33,44};//局部
    //在自定义函数实现输出:有参无返函数
    //参数:数组\长度
    int len=sizeof(arr)/sizeof(arr[0]);//局部
    printf("arr=%ld\n",sizeof(arr));
    Output(len,arr);//参数arr,数组名表示数组的首地址

    return 0;
}
2.二维数组做参数
#include <stdlib.h>
void Output(int row,int arr[][row],int line);
int main(int argc, const char *argv[])
{
    int arr[][4]={11,22,33,44,55,66};//局部
//在自定义函数中实现输出
    //参数:数组,行、列
    int line=sizeof(arr)/sizeof(arr[0]);
    int row=sizeof(arr[0])/sizeof(arr[0][0]);
    Output(row,arr,line);    
    return 0;
}
void Output(int row,int arr[][row],int line)
{
    for(int i=0;i<line;i++)
    {
        for(int j=0;j<row;j++)
        {
            printf("%4d",arr[i][j]);
        }
        printf("\n");
    }
}


3.字符数组做参数
void Ouput(char str[])
{
    printf("%s\n",str);
    printf("sizeof(str)=%ld\n",sizeof(str));//8在自定义函数计算指针的大小
    printf("strlen(str)=%ld\n",strlen(str));//5 在自定义函数中可以计算字符串长度
}
int main(int argc, const char *argv[])
{
    char str[]="hello";
    printf("sizeof(str)=%ld\n",sizeof(str));//6
    printf("strlen(str)=%ld\n",strlen(str));//5
//参数:str,
    Ouput(str);
    return 0;
}


4.二维字符数组做参数
void Output(char str[][5],int line)
{
    for(int i=0;i<line;i++)
    {
    printf("%s\n",str[i]);
    }
}
int main(int argc, const char *argv[])
{
    char str[][5]={"asdf","!@","1234"};
    //自定义函数输出
    //参数:数组,行
    int line=sizeof(str)/sizeof(str[0]);
//    printf("line=%d\n",line);
    Output(str,line);
    return 0;
}

17.4.3 return

return的作用:
	返回值,结束函数

定义格式:
	格式1:  return(表达式);
	格式2:return 表达式;
	
	1.表达式:变量,常量,表达式
	return 1;  return b;   return a+b;
	2.return 一个函数可以有多个return,但是只执行一个,原因结束
	3.return的位置可以任意,一般在最后
	4.当函数的返回值类型和return的数据类型不一致时和函数类型保持一致,发生自动或强制转换。
	5,return只能返回一个值或者地址

17.5 自定义函数的位置

17.5.1 定义在主函数的上面

在这里插入图片描述
注意:

	自定义函数在主函数上面时,
	本着 先声明后使用的原则,
	如果顺序在前的函数使用了顺序在后的函数,
	会报未声明的错误,因此需要理清楚调用逻辑

17.5.2 定义在主函数的下面

在这里插入图片描述
注意:

	自定义函数在主函数下面时,
	本着 先声明后使用的原则,
	需要先在上面写出声明,
	再在主函数下面写出完整的函数功能。

tips:
	声明时需要在这一句声明之后写个 ; 表示这局声明结束了
	可以在声明中的参数列表 略写 变量名,但是变量类型必须写
	函数功能实现时,参数列表必须完整:变量类型 变量名,
	要注意实现时 别在 参数列表后的括号之后写 ; 

17.6 多文件编译

目前写的代码,代码量最多只是到了一百多行,等以后开发时,代码量会特别多,因此要学会多文件编译,将代码分到不同的文件中

17.6.1 函数声明在自定义头文件

进入底行模式 :vsp  文件名
	如果文件存在,则在同一目录下打开该文件
	如果文件不存在,则在同一路径创建该文件,并打开

在这里插入图片描述

可以将自定义的函数声明都放在自己定义的头文件中,然后把头文件引入到主函数所在的文件,如上图所示:#include "head.h"
可以将自定义的函数放在另一个 .c 文件中,要注意这个 .c 文件中别写 main 函数
将存放自定义函数的文件引入到 头文件 中,即在头文件中引入这个 .c 文件,如:#include" my_fun.c"
还需要将存放自定义函数的文件引入到 主函数所在的文件中,方式如上。
如果存放自定义函数的文件没有引入到主函数所在的文件,那么编译时需要在 要编译的文件前 加上存放自定义函数的文件全名
如:gcc myfun.c main.c

17.7 内存空间的分区、变量的作用域及生命周期

  • 系统性学过组成原理的同学都应该知道,存储器的级别分为很多层次
最顶级的是    寄存器  	 断电数据易失		读写最快
往下是    高速缓冲存储器    有多级(一般三级或两级)也被称为缓存、cache,断电数据易失	速度次之
然后是        主存 		 也就是我们常说的内存条,我们的所有程序都要取到这里面进行运行,断电数据易失		速度再次
最后是        辅存		 这个是各种的磁盘、硬盘之类的存储器,断电数据不易失

17.7.1 内存(主存)空间的分区

在这里插入图片描述
一般内核区与用户区按1:3分配

17.7.2 全局变量

全局变量:变量定义在函数外面
	1.全局变量没有写存储类型,默认是外部变量extern
	2.全局变量内存空间在静态区
	3.作用域:从定义开始到整个文件结束
	4.生命周期:从地址申请到释放  即【整个文件】

17.7.3 局部变量

局部变量:定义在函数内部的变量,参数列表
	1.局部变量省略存储类型默认是auto
	2.局部变量的内存空间在栈区,由计算机自动分配自动回收
	3.作用域:从定义开始,到本函数有效
	3.生命周期:从地址申请开始,到本函数结束后释放

17.7.4 全局变量和局部变量结合使用

1.全局变量和局部变量重名,优先选用局部变量,局部屏蔽全局---》就近原则
int a=100,b=2;

void Sum(void)
{
    int a=1,b=4;
    printf("%d+%d=%d\n",a,b,a+b);
}
int main(int argc, const char *argv[])
{
    Sum();
    return 0;
}
2.局部变量和局部变量重名
int a=100,b=2;

void Mul(void)
{
    printf("%d-%d=%d\n",a,b,a-b);//3
}
void Sum(void)
{
    int a=1,b=4;
    printf("%d+%d=%d\n",a,b,a+b);//5
}
int main(int argc, const char *argv[])
{
    Sum();
    Mul();
    return 0;
}
3.局部和局部重名
    15    int main(int argc, const char *argv[])
    16    {
    17    //    Sum();
    18    //    Mul();
    19        int i=0;//19--28
    20        for(int i=1;i<=4;i++) //20--24   5
    21        {
    22            int i=10;//22--24
    23            printf("i=%d\n",i);//10 10 10 10 
    24        }
    25    
    26        printf("i=%d\n",i);//
    27        return 0;
    28    }

在哪都是就近原则,因为在编译器里面就是这么设置的,数据需要压进栈里,先声明的会扔到下面,后面声明的就在上面(先进后出、后进先出),取用时先从上面取,所以就是就近原则

17.8 递归

递归:(recursion)是指一种通过重复将问题分解为同类的子问题而解决问题的方法。本质是循环。
1)递归运行速度慢,但是多用于问题规模较大的情况
2) 循环运行速度快

  • 直接递归:函数自身调用自身
  • 间接递归:多个函数之间相互递归调用
  • 死递归: 等价于死循环
    在这里插入图片描述

17.8.1递归练习

1.递归计算1-n的和
在这里插入图片描述
2.递归计算阶乘n!
5!=12345=54321
边界:n==1
递归公式: n*rec(n-1)
for(int i=5;i>=1;i–)
在这里插入图片描述

3.递归实现斐波那契第n项
1 2 3 4 5 6… 项数
1 1 2 3 5 8… 值
n-2 n-1 n
递归出口:n1 || n2 返回1
递归公式:return rec(n-1)+rec(n-2)

在这里插入图片描述

小作业 1

1,
在主函数定义一维数组并输入
自定义有参函数实现冒泡升序排序
自定义有参函数实现简单选择降序排序
自定义函数实现输出
2,
在主函数定义二维数组并循环输入
自定义函数实现计算最大值和最小值
3,
在主函数定义两个字符串并输入
自定义函数实现字符串拷贝
4,
在主函数定义两个字符串并输入
自定义函数实现字符串比较
下面是我写的:代码在图后面
1.
在这里插入图片描述
2.
在这里插入图片描述
3.
在这里插入图片描述
4.
在这里插入图片描述
代码:
1.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void bubble_up(int *, int);
void jdxz_down(int *, int);
void print_arr(int *, int);
void swap_num(int *, int *);

int main(int argc, const char *argv[])
{
    int a[8] = {0};
    for (int i = 0; i < 8; i++)
        scanf("%d", a + i);
    int len = sizeof(a) / sizeof(a[0]);
    bubble_up(a, len);
    print_arr(a, len);
    jdxz_down(a, len);
    print_arr(a, len);
    return 0;
}

void bubble_up(int *a, int n)
{
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n - i - 1; j++)
            if (a[j] > a[j + 1])
                swap_num(a + j, a + j + 1);
}

void jdxz_down(int *a, int n)
{
    int max;
    for (int i = 0; i < n - 1; i++)
    {
        max = i;
        for (int j = i + 1; j < n; j++)
            if (a[max] < a[j])
                max = j;
        if (i != max)
            swap_num(a + i, a + max);
    }
}

void print_arr(int *a, int n)
{
    for (int i = 0; i < n; i++)
        printf("%d  ", *(a + i));
    putchar(10);
}

void swap_num(int *a, int *b)
{
    *a ^= *b;
    *b ^= *a;
    *a ^= *b;
}

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int get_max(int line, int row, int (*a)[row]);
int get_min(int line, int row, int (*a)[row]);
void Outout(int line, int row, int (*a)[row]);

int main(int argc, const char *argv[])
{
    int a[3][4];
    int line = sizeof(a) / sizeof(a[0]);
    int row = sizeof(a[0]) / sizeof(a[0][0]);
    for (int i = 0; i < line; i++)
    {
        for (int j = 0; j < row; j++)
        {
            scanf("%d", &a[i][j]);
        }
    }
    Outout(line,row,a);
    get_max(line,row,a);
    get_min(line,row,a);

    return 0;
}

int get_max(int line, int row, int (*a)[row])
{
    int max, x = 0, y = 0;
    for (int i = 0; i < line; i++)
    {
        for (int j = 0; j < row; j++)
        {
            if (i == 0 && j == 0)
                max = a[i][j];
            else if (max < a[i][j])
            {
                max = a[i][j];
                x = i;
                y = j;
            }
        }
    }
    printf("最大值为:%d\n", max);
    printf("在第%d行,第%d列.\n",x+1, y+1);
}

int get_min(int line, int row, int (*a)[row])
{
    int min, x = 0, y = 0;
    for (int i = 0; i < line; i++)
    {
        for (int j = 0; j < row; j++)
        {
            if (i == 0 && j == 0)
                min = a[i][j];
            else if (min > a[i][j])
            {
                min = a[i][j];
                x = i;
                y = j;
            }
        }
    }
    printf("最小值为:%d\n", min);
    printf("在第%d行,第%d列.\n", x+1, y+1);
}
void Outout(int line, int row, int (*a)[row])
{
    for (int i = 0; i < line; i++)
    {
        for (int j = 0; j < row; j++)
        {
            printf("a[%d][%d]=%d\t", i, j, a[i][j]);
        }
        putchar(10);
    }
}

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char *mystrcpy(char *dest, const char *src);

int main(int argc, const char *argv[])
{
    char s1[128] = {0};
    char s2[128] = {0};
    printf("请输入字符串s1:\n");
    fgets(s1, 128, stdin);
    printf("请输入字符串s2:\n");
    fgets(s2, 128, stdin);

    mystrcpy(s1, s2);
    puts(s1);
    return 0;
}

char *mystrcpy(char *dest, const char *src)
{
    char *p = dest;
    int i = 0;
    while (*(src + i))
    {
        *dest = *(src + i);
        dest++;
        i++;
    }
    *dest = *(src + i);
    return p;
}

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int mystrcmp(const char *src1, const char *src2);
void print_result(int a);

int main(int argc, const char *argv[])
{
    char s1[128] = {0};
    char s2[128] = {0};
    printf("请输入字符串s1:\n");
    fgets(s1, 128, stdin);
    printf("请输入字符串s2:\n");
    fgets(s2, 128, stdin);

    print_result(mystrcmp(s1, s2));
    return 0;
}

int mystrcmp(const char *src1, const char *src2)
{
    int i = 0;
    while (*(src1 + i) && *(src2 + i) && *(src1 + i) == *(src2 + i))
    {
        i++;
    }
    return *(src1 + i) - *(src2 + i);
}

void print_result(int a)
{
    if (a > 0)
        printf("第一个字符串大\n");
    else if (a < 0)
        printf("第二个字符串大\n");
    else
        printf("俩字符串一样大\n");
}

小作业 2

递归计算各个位数字之和
下面是我写的:(图在前,代码在后)
在这里插入图片描述
代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
    递归求各个位数之和
*/

int rec(int);

int main(int argc, const char *argv[])
{
    int a;
    scanf("%d",&a);
    printf("%d\n", rec(a));

    return 0;
}
int rec(int n)
{
    if (n / 10 <= 0)
    {
        return n;
    }
    else
    {
        return rec(n / 10) + (n % 10);
    }
}

思维导图

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhk___

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值