C入门(八)

1.Review

2.库函数标例rands、in、localtime

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

3.其它库函数示例

4.自定义函数的声明_定义_调用

c语言又称为是函数语言,将具体功能函数化,然后组织整个架构逻辑,这样亦是软件的设计原则,自顶向下,逐步细化

比如下面的代码结构,就可以看的出来,整个程序实现的功能从main开始,分了三个大的模块。每个模块下,又有子模块。

在这里插入图片描述

自定义函数必须满足如下格式,样式跟我们前面使用的库函数基本一致,只不过多了一个函数体,需要去实现。

在这里插入图片描述

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

//定义和声明的关系
//定义在前,调用在后
//定义在后,调用在前,此时需要前向声明

int myMax(int i,int j)
{
  //  if(i>j){
  //       return i;
  //   }
  //  else {
  //    return j;
  //  }

    return i>j?i:j;
}

int main(int argc, char *argv[])
{


    int a = 3;
    int b = 5;

    int iMax = myMax(a,b);
    printf("max value %d\n",iMax);
    return 0;
}

在这里插入图片描述

#include <stdio.h> //将printf的声明放到调用之前
#include <stdlib.h>
#include <time.h>

//定义和声明的关系
//定义在前,调用在后
//定义在后,调用在前,此时需要前向声明,函数一个特点,先声明后使用
//入参中如果没有参数,可以使用void表示无入参。通常省略
//如果没有返回值,即返回类型是void,void不可以省



int myMax(int i,int j);




int main(int argc, char *argv[])
{


    int a = 3;
    int b = 5;

    int iMax = myMax(a,b);
    printf("max value %d\n",iMax);
    return 0;
}


int myMax(int i,int j)
{
  //  if(i>j){
  //       return i;
  //   }
  //  else {
  //    return j;
  //  }

    return i>j?i:j;
}

在这里插入图片描述

实参与形参

1.形参

在定义或声明函数中指定的形参,在未出现函数调用时,它们并不占内存中的存储单元。只有在发生函数调用时,形参才被分配内存单元。在调用结束后,形参所占的内存单元也被释放。

2.实参

实参可以是常量、变量或表达式,但要求它们有确定的值。在调用时将实参的值赋给形参。


5.传值与传址和传递一维数组

传值与传址

传值与传址,本质是都是传递一个数值而己。

1.传值

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

//进程空间
//函数在被调用之前,其内所有的变量尚未开辟空间。
//空间的开辟起始于函数调用,空间消失结束于函数调用完毕
//传值



void func(int a);

int main(int argc, char *argv[])
{
    int a=10;
    func(10);
    printf("main : a = %d\n",a);
    return 0;
}

void func(int a)
{
    a++;
    printf("func : a = %d\n",a);
}

在这里插入图片描述

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

//能过传值的方式,达不到修改变量a的内容的目的。
//地址对于不同的作用域来说,是开放的。



void mySwap(int* pa,int* pb);

int main(int argc, char *argv[])
{
    int a=10,b=20;
    printf("a = %d b = %d\n",a,b);
    mySwap(&a,&b);
    printf("a = %d b = %d\n",a,b);
    return 0;
}

void mySwap(int* pa,int* pb)
{

    int t = * pa;
    *pa = *pb;
    *pb =t;
}

在这里插入图片描述

如何来传递一个一维数组

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

//数组的传递不可能通过拷贝的方式来传递。c语言基于效率的原因,只传递首地址
//数组有三要素,起始地址,步长(刻度),范围
//数组名,是一个指针包含了起始地址,步长(刻度),但是没有范围
//所以在传递一维数组的时候,要传数组名和范围




void disArray(int* p,int n);

int main(int argc, char *argv[])
{
    int arr[] ={1,2,3,4,5,6,7,8,9,10,12,12,12,22};
    printf("main sizeof(arr)  = %d\n",sizeof(arr));

    disArray(arr,sizeof (arr)/sizeof (arr[0]));  // == disArray(&arr[0]);
    return 0;
}

void disArray(int* p,int n)
{
    printf("main sizeof(arr)  = %d\n",sizeof(p));

    for (int i=0;i<n;i++) {
        printf("%d\n",*p++);
    }
}



在这里插入图片描述

6.函数在结构设计和逻辑设计中的应用

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

//数组的传递不可能通过拷贝的方式来传递。c语言基于效率的原因,只传递首地址
//数组有三要素,起始地址,步长(刻度),范围
//数组名,是一个指针包含了起始地址,步长(刻度),但是没有范围
//所以在传递一维数组的时候,要传数组名和范围


void initRandArr(int *arr,int n)
{
    srand(time(NULL));
    for (int i=0;i<n;i++) {
        *arr++ = rand()%100;
    }
}

void display(int *arr,int n)
{
    for (int i=0;i<n;i++) {
        printf("%2d\n",*arr++);
    }
}

int smallextIdx(int startIdx,int *arr,int n)
{
    int idx = startIdx;
    for(int i = startIdx+1;i<n; i++){
        if(arr[i]<arr[idx])
        idx = i;
    }
    return idx;

}

void mySwap(int *pa,int *pb)
{
  int t = *pa;
  *pa = *pb;
  *pb = t;

}

void selectSort(int *arr,int n)
{
    int idx;
    for (int i=0;i<n-1;i++) {
        idx = smallextIdx(i,arr,n);
        if(idx!=i)
        {
            mySwap(&arr[i],&arr[idx]);
        }
    }
}

int main(int argc, char *argv[])
{

    int arr[10];
    initRandArr(arr,10);
    display(arr,10);
    selectSort(arr,10);
    printf("after sort \n");
    display(arr,10);
    return 0;
}





在这里插入图片描述

如何来传递一个二维数组

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


void func(int (*p)[4],int n)
{
    for (int i=0;i<n;i++) {
        for (int j=0;j<4;j++)
        {
            printf("%3d",p[i][j]);
        }
        putchar(10);
    }
}

int main(int argc, char *argv[])
{


    int arr[3][4] = {{1},{2,3},{4,5,6}};
    func(arr,3);
    return 0;
}





在这里插入图片描述

7.递归程序设计与提高

普通调用

所有函数都是平行的即在定义函数时是分别进行的,是互相独立的。函数间可以互相调用。常见有,平行调用,嵌套调用。
在这里插入图片描述

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

//嵌套调用,多了会挂掉,栈溢出


void foo();

void func()
{
    printf("func()\n");
    foo();
    //main();
}

void foo()
{
    printf("foo()\n");
    func();
}

int main(int argc, char *argv[])
{
    func();
    foo();
    return 0;
}

在这里插入图片描述

递归调用

既然函数是平行的,可以相互调用,如果自己调用自己会如何呢?

函数,自身直接调用自身,或是间接调用自身的的现象,称为递归。比如生活中,经常有这样的推理过程。

直接或是或是间接调用自己的情形,就递归调用。recursive

递归,是比较接近自然语言特性的一种调用方式。递归必须要用合理的出口,不然会挂掉

练习:

有5个人坐在一起,问第5个人多少岁?

他说比第4个人大5岁。问第4个人岁数,他说比第3个人天5岁。问第5个人,又说比第2个人大5岁。问第2个人,说比第1个人大5岁。最后问第1个人,他说是10岁

请问第5个人多大?

在这里插入图片描述

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

// 直接或是或是间接调用自己的情形,就递归调用。recursive
// 递归,是比较接近自然语言特性的一种调用方式。递归必须要用合理的出口,不然会挂掉


int getAge(int n)
{
    if(n == 1)
        return 10;
    else
        return getAge(--n)+5;
}


int main()
{
    int age = getAge(5);
    printf("age =%d \n",age) ;
    return 0;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

每次开辟一个和自己一样的空间,一旦返回,空间逐个消失


能用循环做的,就能用递归做,在算法比赛中,一般只写递归,不写循环

循环 for (起始条件;循环终止条件;循环条件变化 ) === 》
递归:递归起始条件:getAge(n) ;有使递归趋于结束的语句:getAge(–n) ; 递归终止条件 n == 1

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

// 直接或是或是间接调用自己的情形,就递归调用。recursive
// 递归,是比较接近自然语言特性的一种调用方式。递归必须要用合理的出口,不然会挂掉


//循环 === 》  递归:递归起始条件:getAge(n)    有使递归趋于结束的语句:getAge(--n)   递归终止条件  n == 1


int func_recursive(int n)
{
    if(n == 9)
    {
        printf("%d \n",n) ;
        return 0;
    }
    else
    {
        printf("%d \n",n) ;
        return func_recursive(++n);
    }
}


int main()
{

    // for循环
    for (int i =0;i<10;i++) {
        printf("%d \n",i) ;
    }

    printf("**********一样一样的*****************\n");

    // 迭代
    int n = 0;
    int ret = func_recursive(n);

    return 0;
}




在这里插入图片描述

猴子吃桃子的问题

猴子第一天摘下若干个桃子当即吃了一半,还不过瘾,又多吃了一个。

第二天早上又将剩下的桃子吃掉一半,又多吃了一个。

以后每天早上都吃了前一天剩下的一半零一个。

到第10天早上想再吃时,见只剩下一个桃子了

求第一天共摘了多少?

在这里插入图片描述


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

// 直接或是或是间接调用自己的情形,就递归调用。recursive
// 递归,是比较接近自然语言特性的一种调用方式。递归必须要用合理的出口,不然会挂掉


//循环 === 》  递归:递归起始条件:getAge(n)    有使递归趋于结束的语句:getAge(--n)   递归终止条件  n == 1


int peachCount(int day)
{
    if(day == 10)
        return 1;
    else {
        return (peachCount(++day)+1)*2;
    }
}

int main()
{

    int ret = peachCount(1);
    printf("%d",ret);
    return 0;
}

在这里插入图片描述

在这里插入图片描述


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

// 直接或是或是间接调用自己的情形,就递归调用。recursive
// 递归,是比较接近自然语言特性的一种调用方式。递归必须要用合理的出口,不然会挂掉


//循环 === 》  递归:递归起始条件:getAge(n)    有使递归趋于结束的语句:getAge(--n)   递归终止条件  n == 1

//注意,递归条件参不参与运算。

int factorial(int n)
{
    if(n == 0)
    {
        return 1;
    }
    else {
        return n*factorial(n-1);
    }

}

int main()
{

    int ret = factorial(3);
    printf("%d",ret);
    return 0;
}




在这里插入图片描述


递归的书写结构

递归返回func(递归条件)
{
if(递归终止条件)
	终止处理;
else
	func(趋于递归终结的条件);
}


递归和循环,有共同的特点,有起点,有终点,重复作同样的事情。

所以很多情况,两者是可以相互转换的。

如果上升一下理论高度,作一个重复面有明确起点和终点的事,有递归和迭代两种选择。

循环其实就是一种迭代。

递归的方式,写法比较简洁,符合正常逻辑,但代码理解难度大,内存消耗大(易导致栈溢出)

所以能用迭代(lterative)解决的问题,不要用递归来完成。


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

// 直接或是或是间接调用自己的情形,就递归调用。recursive
// 递归,是比较接近自然语言特性的一种调用方式。递归必须要用合理的出口,不然会挂掉


//循环 === 》  递归:递归起始条件:getAge(n)    有使递归趋于结束的语句:getAge(--n)   递归终止条件  n == 1

//注意,递归条件参不参与运算。

int fun(int i)
{

    if(i>0)
    {
        fun(i/2);
    }
    printf("%d\n",i);

}

int main()
{

    fun(10);

    return 0;
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值