零基础C语言入门030——函数

  1. 为什么要自己定义函数?

因为随着程序规模的变大,都免不了会遇到下面这些问题:

main 函数变得相当冗杂,程序可读性差
程序复杂度不断提高,编程变成了头脑风暴
代码前后关联度高,修改代码往往牵一发而动全身
变量的命名都成了问题(因为简单的名字都用完了啊,小明,小红,旺财,阿福,隔壁家老王这些名字都用过了,为了不重复命名,只能小明2号,小红3号这样……)
为了在程序中多次实现某功能,不得不重复多次写相同的代码
……

  1. 函数的定义

C语言要求函数必须“先定义,再调用”,定义函数的格式如下:

类型名  函数名(参数列表)
{
        函数体
}

类型名就是函数的返回值,如果这个函数不准备返回任何数据,那么需要写上 void(void 就是无类型,表示没有返回值)。
函数名就是函数的名字,一般我们根据函数实现的功能来命名,比如 print_C 就是“打印C”的意思,一目了然。
参数列表指定了参数的类型和名字,如果这个函数没有参数,那么这个位置直接写上小括号即可(())。
函数体就是指定函数的具体实现过程,是函数中最重要的部分。

  1. 函数的声明

所谓声明(Declaration),就是告诉编译器我要使用这个函数,你现在没有找到它的定义不要紧,请不要报错,稍后我会把定义补上。

有时候,你可能会发现即使不写函数的声明,程序也是可以正常执行的。但如果你把函数的定义写在调用之后,那么编译器可能就会找不着北了:

#include <stdio.h>

int main(void)
{
        print_C();

        return 0;
}

void print_C(void)
{
        printf(" ###### \n");
        printf("##    ##\n");
        printf("##      \n");
        printf("##      \n");
        printf("##      \n");
        printf("##    ##\n");
        printf(" ###### \n");
}

程序的编译时从上到下执行的,所以从原则上来说,函数必须“先定义,再调用”。向上边例子反其道而行就会出问题。但在实际开发中,经常会在函数定义之前使用它们,这个时候就需要提前声明。

声明函数的格式非常简单,只需要去掉函数定义中的函数体再加上分号(;)即可:

#include <stdio.h>

void print_C(void);

int main(void)
{
        print_C();//这里不要加void

        return 0;
}

void print_C(void)
{
        printf(" ###### \n");
        printf("##    ##\n");
        printf("##      \n");
        printf("##      \n");
        printf("##      \n");
        printf("##    ##\n");
        printf(" ###### \n");
}

作为一个良好的编程习惯,建议大家还是无论如何都把函数的声明写上比较合适。

  1. 函数的参数和返回值

有时候,函数需要接收用户传入的数据,那么就需要使用函数的参数。根据需求,函数的参数数量可以有多个,类型也可以各不相同。而函数的返回值通常是反馈了函数的计算结果,也可以是函数的执行结果(比如成功或失败)。

请参考以下两个例子:

a. 编写一个函数 sum,由用户输入参数 n,计算 1+2+3+…+(n-1)+n 的结果并返回。

#include <stdio.h>

int sum(int n);

int sum(int n)
{
        int result = 0;

        do
        {
                result += n;
        } while(n-- > 0);

        return result;
}

int main(void)
{
        int n;

        printf("请输入n的值:");
        scanf("%d", &n);

        printf("1+2+3+...+(n-1)+n的结果是:%d\n", sum(n));

        return 0;
}

b. 编写一个函数 max,接收两个整型参数,并返回它们中的较大的值。

#include <stdio.h>

int max(int, int); // 声明可以只写参数的类型,不写名字

int max(int x, int y)
{
        if (x > y)
                return x; // 程序一旦执行return语句,
                               //表明函数返回,后边的代码不会继续执行。
        else
                return y;
}

int main(void)
{
        int x, y, z;

        printf("请输入两个整数:");
        scanf("%d%d", &x, &y);

        z = max(x, y);

        printf("它们中较大的值是:%d\n", z);

        return 0;
}

5.补充:如果函数不需要参数,建议定义时在函数名后边的小括号中写上 void,明确表示该函数无参数。
如果定义了多个函数 ,但主函数里只调用某一个函数,那么只有一个函数会运行。只有主函数里调用到的函数才会被运行

6.几个语句执行的顺序为B->F->C->A->D->E

#include <stdio.h>

void a();
void b();
void c();

void a()
{
        ; // A   注:什么都没有只写一个分号(;)
           //表示这是一个空语句,什么也不做。
}

int main()
{
        b(); // B
        a(); // C
        c(); // D

        return 0;
}

void c()
{
        ; // E
}

void b()
{
        ; // F
}
  1. 如果定义一个与标准库函数重名的函数,会怎么样?
    答:重新定义的同名函数会覆盖标准库函数(前提是两者的声明一致,包括返回值和参数类型、个数一致)。
// 打印大个Fishc
#include<stdio.h>
void print_F();
void print_I();
void print_S();
void print_H();
void print_C();
int main()
{
    print_F();
    printf("\n");
    print_I();
    printf("\n");
    print_S();
    printf("\n");
    print_H();
    printf("\n");
    print_C();
    printf("\n");
    return 0;
}

void print_F()
{
    printf(" ####### \n");
    printf(" ##      \n");
    printf(" ##      \n");
    printf(" ##      \n");
    printf(" ######  \n");
    printf(" ##      \n");
    printf(" ##      \n");
}
void print_I()
{
    printf("   ####  \n");
    printf("    ##   \n");
    printf("    ##   \n");
    printf("    ##   \n");
    printf("    ##   \n");
    printf("    ##   \n");
    printf("   ####  \n");
}
void print_S()
{
    printf("   ###### \n");
    printf(" ##       \n");
    printf(" ###      \n");
    printf("   #####  \n");
    printf("       ###\n");
    printf("        ##\n");
    printf("  ######  \n");

}
void print_H()
{
    printf(" ##     ## \n");
    printf(" ##     ## \n");
    printf(" ##     ## \n");
    printf(" ######### \n");
    printf(" ##     ## \n");
    printf(" ##     ## \n");
    printf(" ##     ## \n");
}
void print_C()
{
    printf("    ##### \n");
    printf("  ##    ##\n");
    printf(" ##       \n");
    printf(" ##       \n");
    printf(" ##       \n");
    printf("  ##    ##\n");
    printf("   ###### \n");
}
//比较两个整型大小并输出较大值的函数
#include<stdio.h>
int max(int,int);//声明里可以只写参数类型不写名字
int max(int x,int y)
{
    if(x>y)
    {
        return x;
        //函数只会返回一个值,一旦执行return语句,后面的代码就不会执行
    }
    else
        return y;
}
int main()
{
    int a,b,c;
    printf("请输入两个不同的整数:\n");
    scanf("%d%d",&a,&b);
    c=max(a,b);
    printf("两数中较大的数为:%d\n",c);
}
#include <stdio.h>

int sum(int n);

int sum(int n)
{
        int result = 0;

        do
        {
                result += n;
        } while(n-- > 0);

        return result;
}

int main(void)
{
        int n;

        printf("请输入n的值:");
        scanf("%d", &n);

        printf("1+2+3+...+(n-1)+n的结果是:%d\n", sum(n));

        return 0;
}
//交换两个变量的值的函数,也可以看出程序运行顺序和交换只在函数内部有效,作用域
#include<stdio.h>
void swap(int x, int y);//不返回任何值就用void
void swap(int x,int y)
{
	int temp;
	printf("swap函数里交换前 x=%d,y=%d\n", x, y);//第二执行这一语句
	temp=x;
	x = y;
	y = temp;
	printf("swap函数里交换后 x=%d,y=%d\n", x, y);//第三执行这一语句
}
int main()
{
	int x = 3,y = 5;
	printf("main函数里交换前 x=%d,y=%d\n", x, y);//最先执行这一语句
    swap(x, y);
	printf("main函数里交换后 x=%d,y=%d\n", x, y);//第四执行这一语句
	return 0;
}

所以交换两变量的值用指针,交换他们的址是最方便的。

//交换两个变量的地址来实现交换变量的值
#include<stdio.h>
void swap(int *x, int *y);//不返回任何值就用void
void swap(int *x, int *y)
{
	int temp;
	printf("swap函数里交换前 x=%d,y=%d\n", *x, *y);
          // x,y 是地址,函数里要对他们解引用
	temp = *x;
	*x = *y;
	*y = temp;
	printf("swap函数里交换后 x=%d,y=%d\n", *x, *y);
	      //x,y 是地址,函数里要对他们解引用
}
int main()
{
	int x = 3, y = 5;
	printf("main函数里交换前 x=%d,y=%d\n", x, y);//最先执行这一语句
	swap(&x, &y);
	printf("main函数里交换后 x=%d,y=%d\n", x, y);//第四执行这一语句
	return 0;
}

主函数与自定义函数的区别,都是函数,本质没有区别,主函数由系统调用,我们自定义的函数由我们程序员自己调用

系统函数与自定义函数的区别,系统函数如printf()scanf()是提前定义好的,本质也没区别

程序运行顺序是主函数从上往下执行,遇到有调用函数,就从调用位置跳转进函数,函数走完了,再跳回调用位置

函数调用的本质:函数地址+(参数列表)

函数名就是函数地址 fun==&fun()这两者等价。这点与数组名不同

函数声明(函数原型)的格式:就是函数头加分号,里面变量可以不要fun(int x,double y);可以省略为fun(int,double);
可以没有声明,可以声明了等一段再定义,但声明了就一定要放在定义之前

函数参数:函数内部与外部主函数的数据交互——函数外部的数据传递进去,内部的数据传递出去

参数列表写法:连续定义几个变量,用逗号隔开(不要用分号)。变量别初始化。名字别冲突
类型、个数、顺序与调用时候对应一致,首尾呼应。如定义时候fun(int x,double y,int* p),调用的时候fun(2,1.3,&a)逐个对应

调用过程:把实参赋值给形参,int x=2,double y=1.3,int* p=&a

实参可以是常量也可以是变量fun(int b)主函数里int a=3;fun(a)这样就相当于b=a

#include<stdio.h>
void fun(int);
//函数功能就是打印此变量的值
void fun(int b)
{
    printf("%d\n",b);
}
int main(void)
{
    int a=3;
    fun(a);
    //出现函数名——调用——a的值传递给函数参数列表里的b
    return 0;
}
//找出一个数组中的最大值
#include<stdio.h>
int funMaxnum(int *a,int length)
//两个参数,第一个是指针类型,指向数组的地址。
//第二个是整型,数组的长度
{
    int i,Maxnum=a[0];
    for(i=0;i<length;i++)
    {
        if(Maxnum<a[i])
        {
            Maxnum=a[i];
        }
    }
    return Maxnum;//返回的Maxnum的值就是函数的值
}

int main(void)
{
    int a[5]={91,2,5,63,854};//都定义整型
    printf("%d\n",funMaxnum(a,5));
    //返回的Maxnum的值就是函数的值
}
    return 0;
}
//函数内部无法直接修改变量的值,
//要通过指针直接对内存进行操作
//例中只是交换了xy的值,并没有交换ab的值
#include<stdio.h>
void fun(int x,int y)
{
    int m;
    printf("%d,%d\n",x,y);
    m=x;//交换前后输出xy的值
    x=y;
    y=m;
    printf("%d,%d\n",x,y);
}

int main(void)
{
    int a=2,b=3;
    printf("%d,%d\n",a,b);
    fun(a,b);//调用函数前后各输出一遍ab的值
    printf("%d,%d\n",a,b);
}

在这里插入图片描述

//通过指针直接对内存进行操作,来修改指针所指向的变量的值
//ab的地址没变,xy里装的地址数据也没变
#include<stdio.h>
void fun(int* x,int* y)
{
    int m;
    printf("%d,%d\n",*x,*y);
    //交换前后输出xy的值
    m=*x;
    //m不能加*号,
    //这步操作是交换值不是交换地址
    *x=*y;
    *y=m;
    printf("%d,%d\n",*x,*y);
}

int main(void)
{
    int a=2,b=3;
    printf("%d,%d\n",a,b);
    fun(&a,&b);//调用函数前后各输出一遍ab的值
    printf("%d,%d\n",a,b);
}

在这里插入图片描述

//函数之间互相调用(不声明的情况),被调用的函数一定要出现在调用者之前
#include<stdio.h>

void fun1(void)
{
    printf("我是fun1函数\n");
}
void fun2(void)
{
    fun1();
    printf("我是fun2函数\n");
}

int main(void)
{
    fun2();
    return 0;
}
//怎么将函数内部的数据传递出来
//就是说怎么通过函数内部修改b的值从而修改a的值
#include<stdio.h>
void fun(int);
void fun(int b)
{
    printf("b=%d\n",b);
    b=8;
    printf("b=%d\n",b);
}
int main(void)
{
    int a=3;
    printf("a=%d\n",a);
    fun(a);
    printf("a=%d\n",a);
    return 0;
}
//怎么将函数内部的数据传递出来
//就是说怎么通过函数内部修改a的值
#include<stdio.h>
void fun(int*);
void fun(int* p)
//这时要接收的是地址信息
{
    printf("b=%d\n",*p);
    *p=8;
    //星号为内存操作符(不理解为解引用),直接对内存进行操作。
    printf("b=%d\n",*p);
}
int main(void)
{
    int a=3;//int* p=&a;
    printf("a=%d\n",a);
    fun(&a);//这里要把a的地址传递给函数
    printf("a=%d\n",a);
    return 0;
}

在这里插入图片描述

//函数之间互相调用(不声明的情况),被调用的函数一定要出现在调用者之前。在后面就会报错
#include<stdio.h>

void fun1(void)
{
    fun2();
    printf("我是fun1函数\n");
}
void fun2(void)
{
    printf("我是fun2函数\n");
}

int main(void)
{
    fun1();
    return 0;
}

函数一但多了,调用关系复杂,那排序就很痛苦。如何让函数更加灵活没有负担。就得先声明函数(函数原型)
理论上,函数声明放在调用之前就行了。实际中放所有函数前面,紧接头文件下面。
函数声明一定要有函数定义才能使用。
函数声明可以有多个,函数定义有且只能有一个。
注意,不要两函数互相调用,自己调用自己。

//两个函数互相调用(有声明的情况),出现死循环
#include<stdio.h>
void fun1(void);
void fun2(void);

int main(void)
{
    fun2();
    return 0;
}

void fun2(void)
{
    printf("我是fun1函数\n");
    fun1();
}
void fun1(void)
{
    printf("我是fun2函数\n");
    fun2();
}
//两个函数互相调用(有声明的情况),出现死循环
#include<stdio.h>
void fun1(void);
void fun2(void);

int main(void)
{
    fun2();
    return 0;
}

void fun2(void)
{
    fun1();//调用 fun1跟fun2之间不停的跳,不会运行到printf函数
    printf("我是fun1函数\n");
}
void fun1(void)
{
    fun2();
    printf("我是fun2函数\n");
}

函数类型:
无参数无返回值
void fun(void)
{
printf(“我是fun函数\n”);
}
无参数有返回值
int fun(void)
{
printf(“我是fun函数\n”);
return 1;
}

定义了函数有返回值,那么返回的数据与定义的类型必须一致。如例中返回2与定义的int型一致

返回值为2就相当于给函数赋值了一个2。函数的值由return 来决定。

有返回值也可以不用理会,直接调用fun()。只执行函数内的程序。例中就只打印"我是函数fun"。不会输出2

出现函数名就是函数调用,只要主函数中出现他一次就调用一次。例中出现三次就打印三次"我是函数fun"

可以int a=fun(),也可直接打印,也可参与运算。但fun()是常量,不能给他赋值fun()=3

return 的作用,用于有返回值的函数,终止所在函数的执行,并返回指定数据。
出现return,函数就结束,后面的代码就不执行

#include<stdio.h>
int fun(void)
{
    printf("我是函数fun\n");
    return 2;
}
 int main(void)
 {
     fun();
     int a=fun();
     printf("%d,%d\n",a,fun());
     return 0;
 }
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值