C程序设计基础(5):函数基础详解(函数定义、调用、原型、参数)

  上一节中,我们学习了有关数组和字符串的常用方法。数组和字符串的加持大大降低了我们命名和使用变量的复杂程度,节约了代码量,但这还不够。程序编写过程中,我们往往会遇到一段相似的代码在多个不同的位置出现的情况,虽然依靠复制粘贴和小修改可以缩短编程时间,但还有很大一部分情况中,使用这段代码的次数不定,如果单靠使用循环,将对思考产生非常大的麻烦,这就需要模块化的编程思想。使用函数,将一段代码包装成一个块,使用它时只需要提供对应的参数,就可以方便地调用它并获取它的返回值。下面,就让我们一起来了解一下C语言中函数的详细使用方法。

《C程序设计基础(5):函数基础详解(函数定义、调用、原型、参数)》

函数简介

  函数,是一组执行一个功能的语句,被包装在一个函数名称中。只需要一个函数名称(必要时加上传递的参数,在函数中,参数可用类似操作变量的方法进行操作),就能调用这个函数。函数隔离于调用它的函数运行,即,函数内部语句执行不影响外部变量(除了按址传递的参数,后面会说到)。一般情况下,除了void类型。函数都有一个返回值,这时的函数相当于一个值(不可修改),函数执行并返回后的值将取代原来函数的位置,并能够加入运算表达式。(实际上,我们一直在使用的main就是一个整型函数,这个函数在程序开始运行时被调用,它的返回值就是整个程序的返回值)。下面,将给出一个输出a+b的例子,具体原理将在下面给出。

#include <stdio.h>

int add(int a, int b)
{ 
    return (a + b);
}

int main()
{ 
    int a = 1, b = 2;
    printf("a + b = %d\n", add(a, b));
    return (0);
}

样例中,函数add实现了返回两个参数和的功能,这个返回值被作为函数printf的参数1,被输出到控制台上

函数调用时,被调用的函数称为被调函数,调用它的函数称为主调函数

函数定义

  对函数进行定义是函数的核心内容。它制定了函数的功能以及具体实现方法。函数只有经过定义,才能被其他语句调用。

语法

语法格式中,除了名称类进行了替换,其他标识符,符号等没有改变,使用时应注意语法细节

Function_Type Function_Name(Param1_Type Param1_Name, Param2_Type Param2_Name)
{ 
    do-something;
    return (Return_value);
}

释义

  • Function_Type: 函数返回值类型,同变量类型,如果没有返回值,则使用void(无类型)
  • Function_Name: 函数名,为函数指定一个名称,在调用函数时会用到。
  • Param_Type: 参数类型,同变量类型
  • Param_Name: 参数名称,同变量名称,这个名称将在函数内部作为变量名称被使用
  • Return_value: 函数的返回值,注意,请返回一个与函数类型相匹配的值,以免对值进行强制类型转换而造成不必要的错误

注意

  • 以上语法样例仅提供了两个参数的情况,如果需要多个参数,请在两个参数中间用逗号分隔。如果不需要参数,可在括号中(不能省略括号)填void,或是不填
  • 不同于C++和其他高级语言,C语言不支持函数重载,这意味着,每个函数拥有唯一的一个名称

示例

下面一个示例展示了最大值函数,函数将返回两个参数中的较大值

int max(int a, int b)
{ 
    if (a > b) return a;
    return b;
}

函数调用

  函数调用时,需要指定被调函数名称,并在后面加上函数所要求的参数,即使没有参数,也必须加上括号。如函数有一个返回值,这个值将取代函数的位置,单作为一个语句使用也可。

示例:

int max(int, int);
//此处是函数原型声明,定义在上面已经给出,这里不再重复
//有关函数声明内容,将在下文说到

int main()
{ 
    printf("%d", max(a, b));
    return 0;
}

递归调用

在编程中,函数自身调用自身的现象叫做递归调用。下面会讲到,除了按地址传递的参数,两个函数之间不会有任何影响,即使参数和函数内局部变量重名,程序仍然能够进行区分,函数仅能调用当前过程中的局部变量,而不能访问其调用的或是调用它的函数中的局部变量,后者的传递方式一般用全局变量或函数返回值实现。下面求两数最大公约数的辗转相除法程序就是递归调用的很好示例,

int gcd(int a, int b);

int main()
{ 
    int a, b;
    scanf("%d %d", &a, &b);
    printf("%d\n", gcd(a,b));
	return 0;
}

int gcd(int a, int b)
{ 
	if (!b) return (a);
    return (gcd(b, a % b));
}

有关数学知识请自行查阅相关资料,通过几次手推过程和调试,相信你能对递归调用有一个很深刻的了解。

函数原型

功能

  在上面的示例中,函数定义在函数调用之前出现,而为了突出维持程序主体结构,我们往往将main函数作为第一个函数,而将其他函数放在其后。C++编译器从上到下进行编译,当遇到一个函数调用,但之前没有遇到这个函数的定义时,编译器会报错,这就需要在函数调用之前加上函数原型的声明。声明告诉编译器,存在一个这样的函数,并确定其参数个数与类型,其定义将在文件后面或其他文件中给出。C编译器不会对一调用而未声明和定义的函数报警,而是默认其已定义,未定义的函数将在链接器阶段报错。

  另外,函数原型可以指定函数的参数个数和名称,它将指定参数传递时的类型,如果没有声明原型,参数将升级(promote)传递,而这可能对参数的值产生影响,如果函数原型指定了参数类型,则将不会出现自动转换。(参数默认升级不在这里展开,仅作了解)

语法

函数原型声明与函数定义的第一行类似,只是在函数参数括号后直接用;结束,并且,可以不需要给出函数参数的名称,而仅给出参数的个数和类型

int max(int, int);

int main()
{ 
    do-something;
    return 0;
}

//function definition is here
int max(int a, int b)
{ 
    if (a > b) return a;
    return b;
}

函数的参数

作用

  在定义函数时,为了方便,我们往往使函数具有完成一类相近任务的功能,而在调用函数时,有了具体环境,我们也需要指定函数完成的具体任务。这些具体需求的指定就依靠参数的传递。它完成了向函数提供更具体需求的功能。

原理

  函数声明时就定义了函数的个数和类型。在函数内部,函数的参数就像是对应类型的变量,对参数的所有操作语法与变量完全相同。在调用函数时,主调函数提供被调函数声明所要求的全部参数,这些参数将被推入一个栈中,然后,使用call命令,系统控制权转交给被调函数,函数将声明对应参数的变量,并将参数栈中的值给它们。
  函数参数按照传递方式区分主要分为两种:按值传递按址传递参数,又被称为传值调用引用调用。顾名思义,前者传递的仅仅是一个值,参数在函数内部作为一个复制的变量使用,函数内部对参数的改变对主调函数没有任何影响;后者传递了一个地址,即将那个地址对应的值引用传给被调函数,实际上传递了对应变量地址的一个指针,这两个指针所指向的内存空间是同一个,所以,函数内部对参数有任何变化,到函数外部时,变化仍然有效,参数对应的值被永久改变了。
(在当前阶段,我们一般不会使用指针进行编程,也就不会使用按址传递的参数,故第二段仅作了解即可,有兴趣的读者可以深入研究)

语法

下面仅作按值传递参数的语法介绍,按址传参将在指针节中讲解。

仍用上面的示例,

int max(int, int);

int main()
{ 
    int a, b;
    do-something;
    int max_ab = max(a, b);
    return 0;
}

//function definition is here
int max(int a, int b)
{ 
    if (a > b) return a;
    return b;
}

主函数调用函数max时,ab参数被传入max,并在函数执行过程中被使用,即使修改了,也不会对main函数中的ab变量产生影响。

函数的变量

全局变量

定义在不包含任何一个函数(包括main函数)中的变量被称为全局变量。这个程序中的所有函数都可以调用并修改它。其优势是方便,减少了指针参数的数量,劣势则是污染了命名空间,造成名称资源浪费。

局部变量

  在C中,所有定义在函数中(包括main函数)的变量都被称为局部变量。顾名思义,这个变量的作用范围仅限函数内部,即使在外部遇到了相同名称的变量也不影响。其优点是灵活、节约名称空间,而缺点是不能跨函数调用,需要用到按址传递参数。

下面的示例给出了全局变量和局部变量的区别,

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

int a;

void init(void);

int main()
{ 
	int a = 0;
	init();
	printf("A private is: %d\n", a);
	return 0;
}

void init(void)
{ 
	a = 10;
	printf("A global is: %d\n", a);
}

冲突

当函数中定义了一个与全局变量名称相同的变量,那么将优先使用局部变量,而不是全局变量。

    原文作者:Zheng__Huang
    原文地址: https://blog.csdn.net/Zheng__Huang/article/details/109883684
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞