函数和递归(1)
Suzhou 38 0

加法函数模块化写法

//test_1_add.h
#ifndef __TEST1_ADD_H__
#define __TEST1_ADD_H__

int ADD(int x, int y);

#endif
------------------------------------
//test1_1.c
int ADD(int x, int y)
{
    return x + y;
}
------------------------------------
//test1.c
#include <stdio.h>
#include "test1_add.h"

int main()
{
    int a = 10;
    int b = 20;
    int num = ADD(a,b);
    printf("num=%d", num);
    return 0;
}
num=30

::: tip

#include <stdio.h>

#include表示包含一个头文件,如上代码表示包含stdio.h头文件,编译器会把stdio.h中的内容全部拷贝过来。 ::: 在一个工程内,可能出现很多个引用“stdio.h”的情况,为了避免同一个头文件被重复引用,在第一次引用头文件中使用如下操作:

#ifndef __ADD_H__  //infdef:如果没有定义...
#define __ADD_H__  //定义...
//下面的内容在主函数中被include包含引用
#endif  //包含结束条件编译的结束。

上述代码第一行是判断句。 工程中每次使用include包含该头文件时,就会执行一次该判断语句。判断为真执行下面代码,为假不执行。 ::: warning C语言语法中要求对于变量和函数是不能重复定义的,重复包含会导致编译出错。 ::: 第一次编译该头文件时,判断没有定义_XXX_H_(通常根据头文件名设定),就执行下面的程序:首先定义_XXX_H_,然后执行头文件的内容。即第一次包含该头文件时,该头文件被编译器编译。 而当该头文件被第二次包含时,由于_XXX_H_已经被定义,后面的的代码编译器就不会编译了,直接忽略,防止了头文件的重复包含。


函数递归

程序调用自身的编程技巧称为递归(函数自己调用自己)。 是一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。 递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。递归的主要思考方式在于:把大事化小。

简单的递归程序:

#include <stdio.h>

int main()
{
    printf("Hello World!\n");
    main();
    return 0;
}

上述程序中,main()函数反复调用自己,反复打印出“Hello World!”。 递归在执行过程中会出现一些问题,如上述的栈溢出问题: 函数和递归(1) 函数的任何一次调用,都要向内存申请空间。内存空间划分为栈区、堆区、静态区。 栈区中包含局部变量、函数形参等,堆区存放动态开辟的内存,静态区存放全局变量和static修饰的变量(静态变量)。 ::: tip 栈溢出: 由于函数调用都在栈区申请空间。 上述代码中main函数不断调用自己,不断在内存栈区申请空间,逐渐把栈区空间耗尽。栈区空间被耗尽时,编译器会抛出错误:Stack overflow,即栈溢出。 ::: 程序调用自身的编程技巧称为递归( recursion )。递归作为一种算法在程序设计语言中广泛应用。是一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法。通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。 递归的主要思考方式在于:大事化小

::: warning 递归的两个必要条件:

  • 存在限制条件,当满足限制条件时,递归不再继续。
  • 每次递归调用之后都会越来越接近限制条件。 :::

递归练习1

接受一个整型值,按照顺序打印每一位。例如:输入“1234”,打印出“1 2 3 4”。

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

void print(int m)  //1.(1234)4.(123)7.(12)10.(1)
{
    if (m > 9)  //2.(1234>9)5.(123>9)8.(12>9)11.(1<9)
    {
        print(m / 10);  //3.(1234/10)6.(123/10)13.[12%10]9.(12/10)
    }
    printf("%d ", m % 10);  //12.[(9)%10=1]13.[(6)%10=2]14.[(3)%10=3]15.[(1)%10=4]
}

int main()
{
    unsigned int sum = 0;
    scanf("%d", &sum);
    print(sum);
    return 0;
}
1234
1 2 3 4

函数和递归(1) ::: tip 代码思路:使用“m%10”将m从后往前每次取出最后一位,但题目要求是从前往后按顺序输出。可以先使用“m/10”,逐渐找到数字m最大的一位,然后“%10”。循环调用函数,将m的每一位依次打印。 printf(1234)----printf(123)4----printf(12)3 4----printf(1)2 3 4 :::


递归练习2

编写函数不允许创建临时变量,求字符串长度。 模拟实现strlen函数:

#include <stdio.h>

int my_strlen(char* str)
{
    int count = 0;  //count为计数器
    while (*str != '\0')
    {
        count++;
        str++;
    }
    return count;
}

int main()
{
    char arr[] = "Hello";
    int len = my_strlen(arr);  //数组传参时,函数接收到的是数组内部首元素的地址
    printf("len=%d", len);
    return 0;
}
len=5

::: tip 代码思路: 数组传参时函数接收到数组内首元素的地址。数组arr中共有“H”、“e”、“l”、“l”、“o”、“\0”5个元素。(char* str接收到“H”,str解引用。)str就是“H”,通过和“\0”的对比确认不是数组中最后一个元素,进入循环count++,str++,str表示数组内第二个元素,再和数组结束标志“\0”对比,直到str值为“\0”,不进入循环,直接返回count的值给主函数len变量。(数组的长度不包含结尾“\0”)。 ::: 上述代码中使用了计数器count,不符合题目中“不允许创建变量的”要求,需要使用递归方法解决:

#include <stdio.h>

int my_strlen(char* str)
{
    if (*str != '\0')
    {
        return 1 + my_strlen(str + 1);
    }
    else
    {
        return 0;
    }

}

int main()
{
    char arr[] = "Hello";
    int len = my_strlen(arr);
    printf("len=%d",len);
    return 0;
}
len=5

::: tip 代码思路: 在函数my_strlen中,当数组中第一个元素不是“\0”时,可以认为数组长度至少为1,剥离出来后为1+my_strlen(ello);当数组中第二个元素不是“\0”时,可以认为数组长度至少为2,剥离出来后为1+1+my_strlen(llo);...当数组中第6个元素为“\0”时,不计入数组长度,此时长度为1+1+1+1+1+0=5。 :::

评论区

索引目录