探索C语言{函数}的奥秘与精彩

一、函数的概念

  • 函数是 C 语言中非常重要的概念,它是将一段具有特定功能的代码块进行封装,以便重复使用和管理。
  • 在C语⾔中⼀般会用到两类函数:
    库函数
    自定义函数

二、库函数

1、标准库和头文件

  • C语言给我们规定了各种语法规则,但不提供库函数;国际标准的ANSI C规定了一些常用的函数的标准,被称为标准库。
  • 不同的编译器厂商根据ANSI提供的C语言标准,将一系列函数进行实现,这些函数就被称为库函数。
  • C语言中库函数相关头文件资料可参考:C 标准库头文件

2、库函数的一般格式

  • 以一个简单的数学库函数 sqrt() 为例,其一般格式为:
	 result = sqrt(value); 
  • 其中 sqrt() 是函数名, value 是要计算平方根的数值, result 是得到的结果。不同的库函数有不同的参数要求和返回值类型,需要根据具体函数的文档进行正确使用。

3、库函数的使用方法

int main(void)
{
	float x = 0.0;
	float y = 0.0;
	float z = 0.0;
	printf("输入三个数值:\n");
	scanf("%f %f %f", &x, &y, &z);
	printf("(x*y)+z=%f", fmaf(x,y,z));
	return 0;
}

运算结果:
在这里插入图片描述

4、库函数文档的一般格式

  • 1、函数原型
  • 2、函数功能介绍
  • 3、参数和返回类型说明
  • 4、代码举例
  • 5、代码输出
  • 6、相关知识链接

三、自定义函数

1、函数的语法形式

  • 自定义函数是程序员自行创造,能带给代码更多的可能性,形式如下:
ret_type fun_name(形式参数)
{
}
• ret_type 是函数返回类型
• fun_name 是函数名
• 括号中放的是形式参数
• {}括起来的是函数体

在这里插入图片描述

  • 函数就像是一个加⼯⼚,参数就是输入的原材料,经过函数内的计算,得出结果。
    ret_type 是⽤来表⽰函数计算结果的类型。
    fun_name 是为了⽅便使⽤函数;就像工厂的名字⼀样,有了名字⽅便称呼,函数有了名字⽅便调⽤。
    函数的参数就相当于,⼯⼚中送进去的原材料,要交代清楚参数的类型和名字,以及参数个数。
    {} 括起来的部分被称为函数体,函数体就是完成计算的过程。

2、函数的举例

例 1:计算两个数之和的函数

#include <stdio.h>

int add(int num1, int num2) 
{
    return num1 + num2;
}

int main() 
{
    int a = 5, b = 3;
    int result = add(a, b);
    printf("结果: %d\n", result);
    return 0;
}

例 2:判断一个数是否为偶数的函数

#include <stdio.h>

int isEven(int num) 
{
    if (num % 2 == 0) 
    {
        return 1;
    } 
    else 
    {
        return 0;
    }
}

int main() {
    int num = 7;
    if (isEven(num)) 
    {
        printf("%d 是偶数\n", num);
    } 
    else
    {
        printf("%d 不是偶数\n", num);
    }
    return 0;
}

四、形参和实参

  • 在函数的使用中,把函数的参数分为实参和形参。
  • 例如:
#include <stdio.h>

int add(int num1, int num2) 
{
    return num1 + num2;
}

int main() 
{
    int a = 5, b = 3;
    int result = add(a, b);
    printf("结果: %d\n", result);
    return 0;
}

1、实际参数(实参)

  • 上述代码中,int a = 5, b = 3;对a、b进行了赋值,下一步调用add函数时,将ab的值传递给了函数,称之为实际参数,简称实参
    在这里插入图片描述

  • 实际参数就是真实传递给函数的参数

2、形式参数(形参)

  • 在定义add函数的时候,在括号中写的 int num1int num2,称之为形式参数,简称形参
    在这里插入图片描述
  • 为什么称之为形式参数呢?因为我们虽然定义了add函数,但如果我们不调用,add函数中的num1和num2只是形式上的,不会向内存申请空间,不会真实的存在。
  • 形式参数在函数被调⽤的过程中为了存放实参传递过来的值,才向内存申请空间,这个过程就是形式的实例化。

3、实参和形参的关系

  • 从之前例子可以看出,形参的值是实参传递过来的,虽然有着一定的联系,但形参和实参是两个不同的空间。
    例如:
#include <stdio.h>

int add(int num1, int num2) 
{
    return num1 + num2;
}

int main() 
{
    int a = 5, b = 3;
    int result = add(a, b);
    printf("结果: %d\n", result);
    return 0;
}

在这里插入图片描述

  • 进入调试界面,可以发现,num1和num2得到了a和b传递过来的值,但地址却不一样,可见,在使用时,形参和实参是不同的参数。
  • 可以理解为形参是实参的一份临时拷贝,函数结束地址就会被是释放掉。

五、return语句

  • 在函数的设计和使用中,经常会使用到 return 语句,在使用时有几点注意事项:
  • 1、return后边可以是⼀个数值,也可以是⼀个表达式,如果是表达式则先执⾏表达式,再返回表达式的结果。
return a+b;
  • 2、return后边也可以什么都没有,直接写 return; 这种写法适合函数返回类型是void的情况。
return;
  • 3、return返回的值和函数返回类型不⼀致,系统会⾃动将返回的值隐式转换为函数的返回类型。
int add(int x,int y)//定义返回值类型是int
{
	float c = x + y;//接收x+y的值为float
	return c;		//这里C会被强制类型转换为int类型
}
  • 4、return语句执⾏后,函数就彻底返回,后边的代码不再执⾏。
int add(int num1, int num2) 
{
    return num1 + num2;		//return会带着num1+num2的返回值,跳出add函数
}
  • 5、如果函数中存在if等分⽀的语句,则要保证每种情况下都有return返回,否则会出现编译错误。
#include <stdio.h>

int checkNumber(int num) {
    if (num > 0) 
    {
        return 1;  // 正数情况有返回
    } 
    else if (num == 0) 
    {
        return 0;  // 零的情况有返回
    } 
    else
    {
    // 这里如果没有返回,就会导致编译错误
    }
}

int main() {
    int result = checkNumber(-1);
    printf("结果: %d\n", result);
    return 0;
}
  • 在这个例子中,如果 checkNumber 函数中最后少了对 num<0 这种情况的返回,就会引发编译警告或错误。

六、数组做函数参数

1、数组做函数参数时的使用

  • 在函数的使用中,难免会碰到需要将数组作为参数进行传递的操作,例如:
#include <stdio.h>

// 函数用于计算数组元素之和
int sumArray(int arr[], int size) 
{
    int sum = 0;
    for (int i = 0; i < size; i++)
    {
        sum += arr[i];
    }
    return sum;
}

int main() {
    int myArray[] = {10, 20, 30, 40, 50};
    int n = sizeof(myArray) / sizeof(myArray[0]);

    int total = sumArray(myArray, n);

    printf("数组元素之和为: %d\n", total);

    return 0;
}

在这个例子中:

  • sumArray 函数接受一个整型数组和数组的大小作为参数。这里的数组参数实际上是一个指针,它指向数组的起始地址。
  • 在 main 函数中,定义了一个数组 myArray ,并计算出数组的元素个数 n
  • 然后调用 sumArray 函数,将数组和其大小传递进去,函数通过遍历数组计算元素之和并返回。
    在这里插入图片描述
  • 通过调试,我们可以发现,形参中的arr地址就是myArray的地址,也就是说,函数例操作的就是myArray数组。

2、使用数组传参时的注意事项

  • 在使用数组时,有几点需要注意:
    1、函数的形式参数要和函数的实参个数匹配
    2、函数的实参是数组,形参也是可以写成数组形式的
    3、形参如果是⼀维数组,数组⼤⼩可以省略不写
    4、形参如果是⼆维数组,⾏可以省略,但是列不能省略
    5、数组传参,形参是不会创建新的数组的
    6、形参操作的数组和实参的数组是同⼀个数组
    7、当数组作为函数参数传递时,在函数内部对数组的修改会影响到原始数组。同时,数组的大小需要另外传递,因为通过数组参数本身无法确定其实际大小。

七、嵌套调用和链式访问

1、嵌套调用

  • 嵌套调用就是函数之间的调用,每一个函数就像一个模块,需要的时候可以直接拿过来装上使用。
  • 例如:
#include <stdio.h>

// 函数 new_line 用于打印一行字符串 "hehe"
void new_line() 
{
    printf("HEHE\n");
}

// 函数 three_line 用于调用 new_line 函数打印三行 "HEHE"
void three_line() 
{
    int i = 0;
    for (i = 0; i < 3; i++) 
    {
        new_line();
    }
}

int main() 
{
    three_line();
    return 0;
}
  • 上述代码中, new_line 函数用于打印一行字符串"HEHE", three_line 函数用于调用 new_line 函数打印三行"HEHE"。在 main 函数中,我们调用 three_line 函数,从而实现打印三行"HEHE"的功能。

2、链式访问

  • 所谓链式访问,就是将一个函数的返回值作为另一个函数的参数,像链条一样连接起来的访问形式。
  • 例如:
#include <stdio.h>
#include <string.h>

int main() 
{
    // 计算字符串 "abcdef" 的长度,并将结果作为参数传递给 printf 函数进行打印
    int len = strlen("abcdef");
    printf("%d\n",len);
    return 0;
}
  • 如果直接将 strlen 的返回值作为 printf 函数的参数呢?
#include <stdio.h>
#include <string.h>

int main() 
{
    // 计算字符串 "abcdef" 的长度,并将结果作为参数传递给 printf 函数进行打印
    printf("%d\n", strlen("abcdef"));
    return 0;
}
  • 上述代码中,使用了两个函数, strlen 函数和 printf 函数。先使用 lenstrlen 函数计算出来的字符串长度保存起来,然后交给 printf 函数,作为 printf 函数的参数,最终实现打印。

3、经典例题

  • 一段有趣的代码,猜一下执行结果是什么?
#include <stdio.h>
 int main()
 {
 printf("%d", printf("%d", printf("%d", 43)));
 return 0;
 }
  • 该代码的关键是,我们要知道printf函数的返回值是什么?
  • 打开cplusplus.com网站对printf进行搜索,在Return Value一项中,我们可以看到:
    在这里插入图片描述
  • 翻译为:成功后,将返回写入的字符总数。
  • 在这里,我们就知道printf返回的是打印在屏幕上的字符个数。
  • 例⼦中,我们就第⼀个printf打印的是第⼆个printf的返回值,第⼆个printf打印的-是第三个printf的返回值。
    第三个printf打印43,在屏幕上打印2个字符,再返回2
    第⼆个printf打印2,在屏幕上打印1个字符,再放回1
    第⼀个printf打印1
    所以屏幕上最终打印:4321

八、函数的声明和定义

1、函数声明

  • 函数声明向编译器提供函数的名称、参数类型和返回类型等基本信息,它通常出现在程序的开头部分或头文件中,目的是让编译器在后续代码中知道如何使用该函数。

  • 示例:

int add(int num1, int num2);  // 这就是一个函数声明

2、函数定义

  • 函数定义则完整地描述了函数的具体实现,包括函数体中的代码逻辑,用于真正实现函数的功能。

示例:

int add(int num1, int num2) {  // 这里是函数定义
    return num1 + num2;
}

3、单个文件

#include <stdio.h>

// 函数声明
int multiply(int a, int b);

int main() {
    int result = multiply(5, 3);
    printf("结果: %d\n", result);
    return 0;
}
// 函数定义
int multiply(int a, int b) {
    return a * b;
}
  • 在这个例子中,先进行了 multiply 函数的声明,然后在 main 函数中调用它,最后给出了 multiply 函数的具体定义。这样编译器就能够正确地处理函数的调用和执行。
  • 但如果我们只对函数进行定义而不声明,则会出现以下警告信息:
    在这里插入图片描述
  • 所以:
  • 函数的调⽤⼀定要满足先声明后使⽤。
  • 函数的定义也是⼀种特殊的声明,所以如果函数定义放在调⽤之前也是可以的。

4、多个文件

  • 在实际工作中,因为代码量比较大,不会将所有的代码放在一个文件中,而是会根据功能的不同,拆分放在多个文件中。
  • 一般而言,函数的声明、类型的声明放在头文件中(.h),函数的实现则是放在源文件中(.c),如下:
  • 主函数
//使用自己制作的头文件时用" "
#include"add.h"
//使用库函数的头文件时用< >
#include<stdio.h>
int main() 
{
    int result = add(5, 3);
    printf("%d\n", result);

    return 0;
}
  • add.c
//函数的定义
int add(int num1, int num2) 
{
    return num1 + num2;
}
  • add.h
//函数的声明
int add(int num1, int num2) ;
  • 运行结果为:
    在这里插入图片描述

相关推荐

最近更新

  1. TCP协议是安全的吗?

    2024-06-06 23:14:04       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-06-06 23:14:04       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-06-06 23:14:04       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-06-06 23:14:04       18 阅读

热门阅读

  1. 赶紧收藏!2024 年最常见 20道 Kafka面试题(十)

    2024-06-06 23:14:04       10 阅读
  2. 详细分析Vue3中的卡槽知识点(附Demo)

    2024-06-06 23:14:04       9 阅读
  3. Vue Router (创建 挂载)

    2024-06-06 23:14:04       7 阅读
  4. 单片机排水泵高压方案

    2024-06-06 23:14:04       7 阅读
  5. 负载均衡

    2024-06-06 23:14:04       9 阅读
  6. php的default_socket_timeout会不会影响ES连接查询

    2024-06-06 23:14:04       9 阅读
  7. Linux 多台机器之间的免密登录设置

    2024-06-06 23:14:04       9 阅读
  8. Cargo字节镜像源

    2024-06-06 23:14:04       7 阅读
  9. Flappy bird小游戏

    2024-06-06 23:14:04       11 阅读
  10. Spark大数据处理 掌握Scala运算符

    2024-06-06 23:14:04       9 阅读
  11. Debian 常用命令指南:基础篇

    2024-06-06 23:14:04       9 阅读
  12. Scala学习笔记8: 包

    2024-06-06 23:14:04       9 阅读