C语言初识函数

函数:1库函数—C语言本身提供的函数
          2自定义函数
函数是一个大型程序软件的部分代码,由一个或多个语句块组成,负责完成某项特定任务,且相较于其他代码有一定独立性
一般有输出参数和返回值
函数不可以嵌套定义,但可以嵌套调用
函数返回类型为void时,也可以有return语句,但不能有任何返回值,只能用return;
函数声明时,可以省略变量名(在函数定义在主函数后时,在主函数前属于声明,定义同时进行), 所以会出现这种情况:void ss(char [],int []);表示形参为一个char类型数组,一个int类型数组
常见库函数
io函数——<stdio.h>
字符串操作函数——<string.h>
内存操作函数——
时间/日期函数——<time.h>
数学函数——<math.h>
其他库函数
strcpy:char* strcpy(char* destination,const  char* sorce);
后字符串复制到前数组中,
//const不加不影响结果
//destination要比source长,否则溢出
memset:void* memset(void* ptr,int value,size_t num);
memset——memory  set——内存设置
将ptr指向的内存空间的前num个字节设置成指定的value值
示例:
int  main()
{
    char arr[]="hello world";
    memset(arr,'*',5);
    printf("%s\n",arr);
    return 0;
}
打印结果:***** world
函数的组成:
ret_type  fun_name(para1,*)//下面部分为函数体,交代函数的实现
{
    statememt;//语句项
}
ret_type——返回类型
fun_name——函数名
para1——函数参数
错误编程:
#include <stdio.h>
void swap( int x, int y)
{
         int tmp = 0;
        tmp = x;
         x = y;
         y = tmp;
}
int main()
{
         int a = 10;
         int b = 20;
        printf( "a=%d,b=%d\n", a, b);
        swap(a, b);
        printf( "a=%d,b=%d\n",a,b);
         return 0;
}
运行结果:a,b不会交换
原因:函数具有独立性,x,y的地址与a,b的地址不一样,需使用指针
当实参传给形参时,形参是实参的一份临时拷贝,对形参的修改结果不会影想实参
无论实参是何种类型的量,在函数调用时,都必须有确定的值
函数的实参可以是:常量,变量,表达式,函数
修改:
void swap( int x, int y) —> void swap( int* x, int* y)
tmp = x;——>tmp=*x;
x = y;——>*x=*y;
y = tmp;——>*y=tmp;
swap(a, b);——>swap(&a, &b);
求元素个数:sizeof(arr)/sizeof(arr[0])
传值调用:函数的形参和实参分别占用不同的内存块,对形参的修改不会影响实参
传址调用:将外部创建变量的内存地址传给函数参数的一种调用函数的方式。这种传参方式可以让函数和函数外面的变量
                 建立真正的联系,函数内部也可以操作函数外部的变量
函数的传参:int add (int a[],int b) //本质上a是指针
                    ……
                    int a[10];
                    c=add(a,k); //传递的是数组a的首地址
函数的传递的参数可以是:整型,字符型,指针,函数
所有成员函数默认为内联函数。
数组是一组连续的空间,如果数组元素过多,形参拷贝实参会导致大量空间浪费,所以数组传参,只传首元素地址
在二分查找算法中,如果想使用函数方式解决,那么计算数组元素个数的部分要在主函数进行。
若要实现函数执行一次,则实参自增1,需使用传址调用*p++不能完成,++优先级高于*,应为(*p)++
函数的嵌套调用和链式访问
嵌套调用:
void  new_line()
{
    printf("hehe\n");
}
void  three_line()
{
    int i=0;
    for(i=0;i<3;i++)
    {
        new_line;
    }
}
int main()
{
    three_line;
    return 0;
}
链式访问:
int main()
{
    int len=0;
    printf("%d\n",strlen("abc"));
    return 0;
}
int main()
{
    printf("%d",printf("%d",printf("%d",43)));
    return 0;
}
运行过程:先执行打印43,printf返回值为打印字符个数
如果打印遇到错误,则返回一个负值
结果:打印4321
函数的声明和定义
函数在主函数后,则函数需要声明,在主函数前,需要说明函数类型(返回值,函数名,形参),如:int add(int x,int y)
写出的函数模块称为函数的定义如:
int add(int x,int y)
{
    int z=x+y;
    return 0;
}
实际应用:创造一个add.h的头文件,创造一个add.c的源文件
实际应用升声明前会写上:
#ifndef  _ _ ADD_H_ _         (_ _ ADD_H_ _为一个符号,根据头文件设计)(ifndef——if  no  define)
#define _ _ ADD_H_ _
声明后会写上:#endif
处理#include<stdio.h>编译器会将stdio.h内的所有内容拷贝过来,在一个工程中,#include<stdio.h>会被应用多次,
多人引用时会拷贝多份,为避免文件被引用多次,会使用这种形式
在add.h头文件中声明:int Add(int x,int y);
在add.c源文件中定义:
int Add(int x,int y)
{
    int z=x+y;
    return 0;
}
add.h和add.c构成加法模块
若想使用函数,需声明:#include"add.h"
(引用自己写的使用双引号,引用库函数使用尖括号)
函数的声明一般放在头文件中,函数需要先声明,后使用
函数递归
main函数也可以被调用
程序调用自身的编程技巧称为递归,递归是一种算法,在程序设计语言中广泛运用,一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法
递归的主要思考方式为:大事化小
两个必要条件
1:存在限制条件,满足这个条件时,递归不在继续
2:每次递归调用后越来越接近这个限制条件
一个简单递归:
int  main()
{
    printf("hehe\n");
    main();
    return 0;
}
运行结果:hehe死循环,然后停止工作,随后报错:stack  overflow
此为递归常见错误:栈溢出
main,printf调用时都会向栈区申请空间,栈空间耗尽后报错
形式参数也在栈区
任何一个函数调用都会向内存申请空间。
内存分为几个简单区域:栈区,堆区,静态区
栈区存放内容:局部变量,函数形参
堆区存放内容:动态开辟的内存malloc,calloc
静态区存放的内容:全局变量,static修饰的变量
例题:接受一个无符号整形,按照顺序打印它的每一位,例:输入1234,输出1    2    3    4
void  print (int n)
{
    if(n>9)
        print(n/10)
    printf("%d",n%10);
}
//过程:n的值没有改变,传递的值改变了而已
int  main()
{
    unsigned  int num=0;
    scanf("%d",&num);
    print(num);
    return 0;
}
通常将一个大型复杂问题,转化为一个规模较小,性质相似的问题
例题:计算一个字符串的长度,不允许创建临时变量
#include<stdio.h>
int  my_strlen(char* str)
{
    if(*str!='\0')
        return 1+my_strlen(str+1);
    else
        return 0;
}
//过程:
int  main()
{
    char  arr[]="bit";
    int  len=my_strlen(arr);
    printf("len=%d\n",len);
    return  0;
}
递归阶乘 阶乘公式:
Fac(n)=1,(n<=1)
Fac(n)=n*Fac(n-1),n>1
例题:求第n个斐波那契数列
Fib(n)=1,n<=2
Fib(n)=Fib(n-1)+Fib(n-2),n>2
程序设计思路之一——TDD——测试驱动开发——先写出怎么用的部分,然后写如何实现
int  Fib(int  n,int* p)
{
    if(n==3)
        (*p)++;
//计算第三个数被计算了多少次
    if(n<=2)
        return  1;
    else
        return  Fib(n-1,p)+Fib(n-2,p);
}
图解说明:
int  main()
{
    int  n=0;
    int  ret=0;
    int count=0;
    scanf("%d",&n);
    ret=Fib(n,&count);
    printf("ret=%d\n",ret);
    printf("count=%d\n",count);
    return 0;
}
缺点:重复计算次数非常多
解决方法:循环和叠加
int  Fid(int  n)
{
    int  a=1;
    int  b=1;
    int  c=1;
//c为第三个数,因为若n<=2,则返回1,所以讲1赋给c
进阶算法:
        1    1    2    3    5    8    13    21    ……
1      a    b    c
2            a    b    c
3                  a    b    c
    while(n>2)
    {
        c=a+b;
        a=b;
        b=c;
        n--;
    }
    return c;
}
int main()
{
    int  n=0;
    int  ret=0;
    int count=0;
    scanf("%d",&n);
    ret=Fib(n,&count);
    printf("ret=%d\n",ret);
    return 0;
}
另一个思路:使用static对象替代nonstatic局部变量
递归常遇见栈溢出,示例:
void  test(int  n)
{
    if(n<10000)
        test(n+1);
}
int  main()
{
    test(1);
    return  0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值