BIT-3-函数(6000字详解析)

本文涉及的产品
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
简介: BIT-3-函数(6000字详解析)

在数学中,函数通常被定义为一种从一个集合(称为定义域)到另一个集合(称为值域)的映射关系。数学中的函数可以用公式、图形、表格等方式来表示,其主要特点是每个输入值都有唯一的输出值。

而在C语言中,函数是一组执行特定任务的代码块。C语言中的函数定义由函数头和函数体组成。函数头包括函数的返回类型、函数名和参数列表的定义,而函数体则包含了具体的代码实现。

因此,数学中的函数是一个概念上的映射关系,而C语言中的函数则是程序中的一种可调用的代码结构,用于执行特定的任务。虽然两者的概念有些相似,但在定义和使用上存在一些区别。

接着我们对进行对函数的学习

一:函数

在c语言中,函数分为两种

  1. 库函数
  2. 自定义函数

1.1库函数

C语言中的库函数是预先编写好的可供调用的函数集合,它们实现了一些常见的功能和算法,并以库的形式提供给开发者使用。库函数的存在有以下几个主要意义:

  1. 提高代码的重用性:库函数实现了一些常用的功能和算法,开发者可以直接调用这些函数来完成相应的任务,而无需从头开始编写代码。这大大提高了代码的重用性,减少了开发时间和劳动成本。
  2. 提高代码的可维护性:库函数是经过充分测试和优化的,在使用过程中一般不需要关注其内部实现细节,只需了解函数的接口和功能即可。这使得代码更易于维护,开发者可以将精力集中在自己的业务逻辑上,而不必关注底层实现。
  3. 加速开发过程:库函数已经实现了一些常见的功能,包括数学运算、字符串处理、IO操作等,使用库函数可以快速地完成这些任务,加速开发过程。

简单的总结,C语言常用的库函数都有:

  • IO函数
  • 字符串操作函数
  • 字符操作函数
  • 内存操作函数
  • 时间/日期函数
  • 数学函数
  • 其他库函数

1.2库函数的使用

使用库函数的方法如下:

  1. 引入头文件:库函数通常通过头文件进行声明,开发者需要在代码中引入相应的头文件来包含库函数的声明。
  2. 调用函数:在程序中需要使用库函数时,通过函数名和参数列表调用相应的函数即可完成相应的功能。

下面是具体的库函数使用例子:

下面是一个简单的C语言例子,展示如何调用标准库函数 printf()

#include <stdio.h> // 包含标准库头文件
int main() {
    int num = 10;
    printf("The number is: %d\n", num); // 调用 printf() 函数打印输出
    return 0;
}

在这个例子中,我们包含了标准库头文件 <stdio.h>,这个头文件中包含了 printf() 函数的声明。然后,在 main() 函数中,我们声明了一个整数变量 num,并使用 printf() 函数将其打印输出到控制台。

通过在代码中包含 <stdio.h> 头文件,我们就能够使用标准库函数 printf()。该函数可以格式化输出到标准输出流(通常是控制台),并接受格式化字符串和相应的参数。

1.3自定义函数

因为在实际开发中,需求和场景千变万化非常复杂,所以库中的函数并不能满足我们所有的需要,所以就有了自定义函数,自定义函数对于程序员来说更加重要

当在C语言中编写自定义函数时,需要按以下步骤进行:

  1. 函数声明:在函数使用之前,需要提前声明函数的原型。函数声明包括函数返回类型、函数名以及函数参数列表。
  2. 函数定义:在函数声明之后,可以定义函数的实际逻辑。函数定义包括函数头和函数体。函数头包括函数返回类型、函数名和参数列表,而函数体则包括具体的代码逻辑。
  3. 函数调用:在其他地方需要使用函数时,可以通过函数名和参数列表来调用函数。

下面是一个示例,展示如何编写一个自定义函数来计算两个整数的和:

#include <stdio.h>
// 函数声明
int sum(int a, int b);
// 主函数
int main() {
    int x = 5;
    int y = 3;
    int result = sum(x, y); // 函数调用
    printf("The sum of %d and %d is %d\n", x, y, result);
    return 0;
}
// 函数定义
int sum(int a, int b) {
    int result = a + b;
    return result;
}

上面的代码中,我们先在程序开始处进行函数声明,告诉编译器有一个名为 sum 的函数,接受两个整数参数并返回一个整数结果。在 main 函数中,我们通过调用 sum 函数来计算两个整数的和,并打印结果。随后,我们使用 sum 函数进行定义,函数体内部计算了两个整数的和并将结果返回。

1.4实际参数和形式参数

在C语言中,函数的参数分为实际参数(实参)和形式参数(形参)。实际参数是在调用函数时传递给函数的值,而形式参数是函数定义中声明的参数。

下面是一个简单的示例代码来说明实际参数和形式参数的概念:

#include <stdio.h>
// 定义一个函数,接受两个形式参数
void add(int a, int b) {
    int sum = a + b;
    printf("The sum is: %d\n", sum);
}
int main() {
    int num1 = 5;
    int num2 = 3;
    // 调用 add 函数,num1 和 num2 作为实际参数传递 
    add(num1, num2);
    return 0;
}

在上面的代码中,add 函数有两个形式参数 ab,它们的类型都是 int。在 main 函数中,我们声明了两个整型变量 num1num2,并将它们作为实际参数传递给 add 函数。在函数中,形式参数 ab 接受了实际参数 num1num2 的值。

当我们调用 add(num1, num2) 时,实际参数 num1 的值被赋给了形式参数 a,实际参数 num2 的值被赋给了形式参数 b。在函数内部,我们可以使用形式参数执行计算操作,这里是将两个参数相加并打印结果。

下面是图解说明

运行这段代码,输出将会是 The sum is: 8,因为 add 函数计算了 num1num2 的和,并打印了结果。

1.5函数的传值调用和传址调用

在C语言中,有两种方式可以在函数之间传递参数,分别是传值调用和传址调用。

传值调用(Call by Value)是指在函数调用过程中,将实际参数的值复制给形式参数,但是函数内部对形式参数的修改不会影响到实际参数。

下面是一个传值调用的示例代码:

#include <stdio.h>
void changeValue(int num) {
    num = 10;  // 修改形式参数的值
}
int main() {
    int num = 5;
    printf("Before function call: %d\n", num);
    changeValue(num);  // 传值调用
    printf("After function call: %d\n", num);
    return 0;
}

输出结果为:

Before function call: 5
After function call: 5

可以看到,在函数changeValue内部,形式参数num被修改为10,但是在main函数中,实际参数num的值仍然是5,并没有改变。

下面通过图解说明

首先我们定义了一个int类型的num,接着把这个num的值10赋值给了形式参数num(这里也叫num,但是这两个num是不一样的),接着通过changeValue这个函数,对num的值进行修改,但是在函数内部修改形式参数的值并不会影响实参的值。

因此,当我们在main函数中打印num的值时,它仍然是初始值5,并没有被修改。

如果你想要在函数中修改原始变量的值,你可以通过传递指针参数来实现。这样,在函数中就可以通过解引用指针来修改原始变量的值。你可以尝试修改代码来使用指针参数进行值的修改。

传址调用(Call by Reference)是指在函数调用过程中,将实际参数的地址传递给形式参数,函数内部可以通过解引用操作符*修改实际参数的值。

下面是一个传址调用的示例代码:

#include <stdio.h>
void changeValue(int *num) {
    *num = 10;  // 修改实际参数的值
}
int main() {
    int num = 5;
    printf("Before function call: %d\n", num);
    changeValue(&num);  // 传址调用
    printf("After function call: %d\n", num);
    return 0;
}

输出结果为:

Before function call: 5
After function call: 10

可以看到,在函数changeValue内部,通过解引用操作符*修改了实际参数num的值,所以在main函数中,实际参数的值变为了10。

下面通过图解说明

因为虽然有一个形参,但是实参传递给形参的值是一个地址,形参可以通过这个地址解引用来找到num所在的空间,并进行值的修改

总结起来,传值调用是将实际参数的值复制给形式参数,在函数内部对形式参数的修改不会影响到实际参数;而传址调用是将实际参数的地址传递给形式参数,函数内部可以通过解引用操作符修改实际参数的值。

1.6函数的嵌套调用和链式访问

函数的嵌套调用是指在一个函数内部调用另一个函数,而链式访问是指在连续的多个函数调用中使用返回值作为下一个函数调用的参数。下面我将通过代码示例来解释这两个概念。

首先,让我们看一个函数的嵌套调用的示例:

#include <stdio.h>
void innerFunction() {
    printf("This is the inner function.\n");
}
void outerFunction() {
    printf("This is the outer function.\n");
    innerFunction(); // 在外部函数调用内部函数
}
int main() {
    outerFunction(); // 在主函数中调用外部函数
    return 0;
}

在上面的代码中,我们定义了两个函数innerFunction和outerFunction。在outerFunction函数中,我们调用了innerFunction函数。然后,在主函数中,我们调用了outerFunction函数。

当我们运行这段代码时,输出将是:

This is the outer function.
This is the inner function.

这是因为在外部函数调用内部函数后,内部函数的代码也会被执行。

接下来,让我们看一个链式访问的示例:

#include <stdio.h>
int add(int a, int b) {
    return a + b;
}
int subtract(int a, int b) {
    return a - b;
}
int multiply(int a, int b) {
    return a * b;
}
int main() {
    int result = multiply(subtract(add(5, 3), 1), 2);
    printf("Result: %d\n", result);
    return 0;
}

在上面的代码中,我们定义了三个函数add、subtract和multiply。在主函数中,我们使用连续的函数调用来进行链式访问。具体来说,我们首先将5和3作为实际参数传递给add函数,然后将add函数的返回值作为实际参数传递给subtract函数,最后将subtract函数的返回值作为实际参数传递给multiply函数。

运行这段代码时,输出将是:

Result: 14

这是因为按照从左到右的顺序进行函数调用,并且每个函数的返回值作为下一个函数调用的参数。

注意:函数可以嵌套调用,但是不能嵌套定义。

1.7函数的声明

函数声明的作用:

  1. 告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。但是具体是不是存在,函数声明决定不了。
  2. 函数的声明一般出现在函数的使用之前。要满足先声明后使用。
  3. 函数的声明一般要放在头文件中的。

对于第二点和为什么有函数声明,是因为c语言是一种解释性的语言,是从上向下边翻译边执行的,所以说如果函数在使用位置之前,则可以不用声明函数,而函数在使用位置之后,则需要声明函数了,因为不声明编译器就找不到,声明了编译器就会自动跳转到该函数体内执行代码

在C语言中,函数声明是指在程序中提前声明函数的名称、返回类型和参数列表的过程。函数声明在函数定义之前,通常放在头文件中,以便在其他部分使用该函数时能够正确地识别和调用它。

函数声明的一般形式如下:

返回类型 函数名(参数列表);

下面是一些函数声明的示例:

int max(int a, int b);
void greet();
float calculateAverage(float arr[], int size);

在这些示例中,max函数返回一个int类型的值,它有两个int类型的参数abgreet函数没有返回值,也没有参数。calculateAverage函数返回一个float类型的值,它有一个float类型的数组参数arr和一个int类型的参数size

函数声明的目的是告诉编译器有关函数的信息,以便在程序中正确地使用函数。这样,在函数定义之前,其他部分就可以使用函数并知道如何调用它。函数定义包含函数的实际代码实现,而函数声明只是提供有关函数的信息。

如果函数在声明之前被调用,编译器将查找函数的声明,根据声明中的信息生成符合函数调用的代码。然后,在编译器找到函数定义时,它将使用实际的函数实现替换函数调用。

函数声明在程序中的位置很重要。通常,我们将函数声明放在头文件中,并在需要使用函数的文件中包含该头文件。这样,当多个文件使用同一个函数时,它们可以共享同一个函数声明,避免重复的声明和定义。

1.8函数递归

程序调用自身的编程技巧称为递归,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。

递归的两个必要条件:

  • 存在限制条件,当满足这个限制条件的时候,递归便不再继续。
  • 每次递归调用之后越来越接近这个限制条件。

下面通过一个简单的递归求阶乘的例子讲解:

int factorial(int n) {
    // 递归基
    if (n == 0) {
        return 1;
    }  
    // 递归调用
    return n * factorial(n - 1);
}

让我们对上述代码进行解释:

因为对于一个阶乘,我们可以把阶乘转化为一个数再乘以阶乘的形式,比如说 100!= 100 x 99!,而 99!又可以写成 99 x 98!这就是把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解

函数递归和我们之前学过的循环语句非常相似,都是在重复的做某一件事情,但是函数递归做的事是它本身,而循环语句做的事情则会更加普遍和随机

1.8.1栈溢出

在c语言中,内存的分配大概有三种空间,一个是栈区,一个是堆区,一个是静态区,而函数栈帧的开辟则是在栈区,所以我们每一次的函数调用,都需要在栈区开辟一块内存空间,用来保存调用过程下的上下文消息,如果调用次数太多的话,就会导致栈区空间的满溢,也就是栈溢出,举个函数递归求阶乘的例子:

如果当我们递归的次数太多的时候,函数的栈会满,而函数只有当执行完所有的代码后才会销毁,所以我们在递归时,递归的次数不能过大

目录
相关文章
|
2月前
|
SQL 数据挖掘 测试技术
南大通用GBase8s数据库:LISTAGG函数的解析
南大通用GBase8s数据库:LISTAGG函数的解析
|
30天前
|
C语言 开发者
【C语言】断言函数 -《深入解析C语言调试利器 !》
断言(assert)是一种调试工具,用于在程序运行时检查某些条件是否成立。如果条件不成立,断言会触发错误,并通常会终止程序的执行。断言有助于在开发和测试阶段捕捉逻辑错误。
41 5
|
2月前
|
机器学习/深度学习 自然语言处理 语音技术
揭秘深度学习中的注意力机制:兼容性函数的深度解析
揭秘深度学习中的注意力机制:兼容性函数的深度解析
|
4月前
|
存储 前端开发 JavaScript
前端基础(十二)_函数高级、全局变量和局部变量、 预解析(变量提升)、函数返回值
本文介绍了JavaScript中作用域的概念,包括全局变量和局部变量的区别,预解析机制(变量提升),以及函数返回值的使用和类型。通过具体示例讲解了变量的作用域、函数的返回值、以及如何通过return关键字从函数中返回数据。
26 1
前端基础(十二)_函数高级、全局变量和局部变量、 预解析(变量提升)、函数返回值
|
3月前
|
存储
atoi函数解析以及自定义类型经典练习题
atoi函数解析以及自定义类型经典练习题
59 0
|
3月前
|
数据处理 Python
深入探索:Python中的并发编程新纪元——协程与异步函数解析
深入探索:Python中的并发编程新纪元——协程与异步函数解析
32 3
|
3月前
|
机器学习/深度学习 算法 C语言
【Python】Math--数学函数(详细附解析~)
【Python】Math--数学函数(详细附解析~)
|
3月前
|
安全 编译器 C++
【C++篇】C++类与对象深度解析(三):类的默认成员函数详解
【C++篇】C++类与对象深度解析(三):类的默认成员函数详解
28 3
|
4月前
|
设计模式 存储 算法
PHP中的设计模式:策略模式的深入解析与应用在软件开发的浩瀚海洋中,PHP以其独特的魅力和强大的功能吸引了无数开发者。作为一门历史悠久且广泛应用的编程语言,PHP不仅拥有丰富的内置函数和扩展库,还支持面向对象编程(OOP),为开发者提供了灵活而强大的工具集。在PHP的众多特性中,设计模式的应用尤为引人注目,它们如同精雕细琢的宝石,镶嵌在代码的肌理之中,让程序更加优雅、高效且易于维护。今天,我们就来深入探讨PHP中使用频率颇高的一种设计模式——策略模式。
本文旨在深入探讨PHP中的策略模式,从定义到实现,再到应用场景,全面剖析其在PHP编程中的应用价值。策略模式作为一种行为型设计模式,允许在运行时根据不同情况选择不同的算法或行为,极大地提高了代码的灵活性和可维护性。通过实例分析,本文将展示如何在PHP项目中有效利用策略模式来解决实际问题,并提升代码质量。
|
5月前
|
SQL 数据处理 数据库

推荐镜像

更多