16 函数的基本概念、声明与调用、返回值、值传递、原型声明,文档注释,多文件编程(简易版)

本文主要是介绍16 函数的基本概念、声明与调用、返回值、值传递、原型声明,文档注释,多文件编程(简易版),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

1 函数基本概念

1.1 为什么需要函数

1.2 什么是函数

1.3 函数的作用

1.4 函数的分类

2 函数的声明与调用

2.1 结构说明

2.2 案例演示

2.3 函数不能嵌套声明

2.4 函数的调用

3 函数的返回值

3.1 无返回值类型

3.2 有返回值类型

3.3 有返回值但无 return 语句

3.4 返回类型不一致

3.4.1 如果转换是安全的

3.4.2 如果转换不安全或不可能

3.5 综合案例演示

4 函数的参数

4.1 形参与实参

4.2 参数传递 - 值传递 

5 文档注释

6 函数原型

7 案例:通过函数原型实现多文程

7.1 搭建文件结构

7.2 编写源代码

7.3 统一编译源文件


1 函数基本概念

1.1 为什么需要函数

        《街霸》游戏中,实现人物出拳、出脚或跳跃等动作都需要编写 50-80 行的代码,在每次出拳、出脚或跳跃的地方都需要重复地编写这 50-80 行代码,这样程序会变得很臃肿,可读性也非常差。为了解决代码重复编写的问题,可以将出拳、出脚或跳跃的代码提取出来放在一个 {} 中,并为这段代码起个名字,这样在每次的出拳、出脚或跳跃的地方通过这个名字来调用这个 {} 的代码就可以了。

        提取出来的代码可以看作是程序中定义的一个函数,程序在需要出拳、出脚或跳跃时调用该函数即可。

1.2 什么是函数

        函数是一种可重复使用的代码块,用于执行特定任务或操作。它允许我们将代码逻辑组织成独立的单元,从而提高代码的可读性、可维护性和重用性。

        在 C 语言中,一个程序可以由一个或多个源文件组成(多文件编程),源文件的扩展名为 .c。每个源文件都是一个编译单位,并且可以包含多个函数。这些函数之间可以相互调用,因此函数是 C 程序的基本组成单位

1.3 函数的作用

  • 封装功能,将一个完整的功能封装成函数,提高代码的结构化和复用性。
  • 代码模块化,将程序按照功能拆分成若干模块单元,有助于降低复杂度。
  • 增强可维护性,如果需要修改某项功能,只需要调整对应的函数代码。
  • 隔离细节,通过函数调用可以隐藏实现细节,只关心输入输出。

1.4 函数的分类

        C 语言中,从使用的角度,函数可以分类两类。

        1. 库函数,也称为标准函数,是由 C 系统提供的,用户不必自己定义,可直接使用它们,使用库函数,必须包含 #include 对应的头文件

        2. 自定义函数,解决具体需求而自己定义的函数,需先定义再使用


2 函数的声明与调用

返回类型 函数名(参数列表)
{函数体语句1;函数体语句2;…………………………函数体语句n;return 返回值;
}

2.1 结构说明

        函数名:函数被调用时使用的名字,函数名要符合标识符规范

        函数体:函数中所包含的代码块,用于实现函数的具体功能和操作

        参数列表(形参列表):用于接收调用函数时传递进来的值(实参)

        返回值:函数执行完毕后,从函数传回到调用点的值,返回值的类型要与函数名前面的返回类型对应,否者会发生未定义行为如果没有返回值,返回类型可以写 void

2.2 案例演示

        下面的代码演示了如何在 C 语言中声明和定义函数:

#include <stdio.h>// 声明了一个名为 func 的函数
// 它没有参数也没有返回值,只是做简单的打印输出
void func()
{printf("hello func\n");
}// 声明了一个名为 minus 的函数
// 接受两个整数参数 m 和 n,并返回它们的差
int minus(int m, int n)
{return m - n;
}// 声明了一个名为 adds 的函数
// 接受两个 double 类型的参数 i 和 j,并返回它们的和
double add(double i, double j)
{double addRes = i + j;return addRes;// return i + j;  也可以直接返回数据
}// 声明了一个名为 max 的函数
// 接受两个整数参数 a 和 b,并返回较大的一个
int max(int a, int b)
{// 定义一个变量,存储结果int maxNum;maxNum = a > b ? a : b;return maxNum;
}// 主函数
int main()
{return 0;
}

2.3 函数不能嵌套声明

        C 语言中,所有的函数都是互相独立的,它们之间不能嵌套定义。这意味着一个函数不能定义在另一个函数的内部。

  • C 语言不允许在一个函数内部定义另一个函数。
  • 每个函数都是独立存在的,并且可以通过函数名来调用。
  • 函数可以互相调用,但不能嵌套定义。
//错误演示
int func1(int a,int b) //第 1 个函数的定义
{   ...int func2(int c,int d)  //第 2 个函数的定义{   ...}...
}

        有些编译器的扩展允许函数嵌套声明,但这不是 C 标准的一部分,代码的可移植性可能会受到影响,强烈不建议。

2.4 函数的调用

        函数名后面加上圆括号即表示函数的调用,有参数的话,参数需要写在圆括号内。每当函数被调用一次,函数体内的语句都会被执行一遍。

#include <stdio.h>// 声明了一个名为 func 的函数
// 它没有参数也没有返回值(void类型),只是简单地打印输出
void func()
{printf("hello func\n"); // 打印字符串 "hello func" 到控制台
}// 声明了一个名为 minus 的函数
// 它接受两个整数参数 m 和 n,并返回它们的差
int minus(int m, int n)
{return m - n; // 返回两个参数的差
}// 声明了一个名为 add 的函数
// 它接受两个 double 类型的参数 i 和 j,并返回它们的和
double add(double i, double j)
{double addRes = i + j; // 计算两个参数的和,并存储在局部变量 addRes 中return addRes;         // 返回和// 注意:return i + j; 也可以直接在这里返回数据,无需中间变量
}// 声明了一个名为 max 的函数
// 它接受两个整数参数 a 和 b,并返回较大的一个
int max(int a, int b)
{// 使用三元运算符来比较两个数并返回较大的一个int maxNum = a > b ? a : b;return maxNum; // 返回较大的数
}// 主函数,程序的入口点
int main()
{// 函数的调用示例// 1. 调用没有参数和返回值的函数func(); // 直接调用,打印 "hello func"func(); // 再次调用,再次打印 "hello func"// 2. 调用有参数和返回值的函数// 传递字面量作为参数printf("10-20的结果:%d\n", minus(10, 20)); // 打印 10 减去 20 的结果printf("20-10的结果:%d\n", minus(20, 10)); // 打印 20 减去 10 的结果// 传递变量作为参数double d1 = 10.0, d2 = 90.0;                        // 定义并初始化两个 double 类型的变量printf("10.0+90.0的结果:%.2f\n", add(d1, d2));     // 打印 d1 和 d2 的和,保留两位小数// 传递字面量作为参数printf("20.0+80.0的结果:%.2f\n", add(20.0, 80.0)); // 调用 max 函数来比较整数// 传递字面量作为参数printf("66和88之间较大的是:%d\n", max(66, 88)); // 打印 66 和 88 中较大的数printf("45和31之间较大的是:%d\n", max(45, 31)); // 注意更正了参数,以保持逻辑一致性// 演示对返回值的操作printf("可以操作返回来的数据:max(66, 88) + max(12,6) = %d \n", max(66, 88) + max(12, 6)); // 打印两个 max 函数返回值的和return 0; // 程序正常结束
}

        输出结果如下所示:


3 函数的返回值

        函数调用后数能得到一个确定的值,这就是函数的返回值,返回值常常是一个计算的结果,或是用来作为判断函数执行状态的标记。

3.1 无返回值类型

        当函数无返回值或明确不需要返回值时,使用 void(即空类型)作为返回类型。

#include <stdio.h>// 无返回值类型的函数,使用 void(即空类型)表示。
void fun01()
{printf("调用了 fun01 函数\n");
}int main()
{// 调用无返回值的函数,仅执行其内部的打印操作fun01();return 0;
}

3.2 有返回值类型

        明确指定返回值类型,如 int、float、char 等。在函数体内部使用 return 语句返回具体的值。

#include <stdio.h>// 有返回值类型的函数,返回 double 类型的值
double fun02()
{return 3.14;
}int main()
{  // 调用返回 double 类型值的函数,并打印其返回值printf("fun02() 返回的数据:%.2f \n", fun02());return 0;
}

3.3 有返回值但无 return 语句

        如果函数声明的返回类型不是 void,说明函数需要返回数据,但函数体内部没有 return 语句,那么函数会返回一个不确定的值

#include <stdio.h>// 返回值类型为 int,但函数中没有 return 语句
// 这将导致函数返回一个不确定的值(通常是未定义行为)
int fun03()
{10 + 20; // 这行代码仅计算了 30,但没有将其赋值给任何变量,也没有返回它// 由于缺少 return 语句,函数将返回一个不确定的值
}int main()
{// 调用返回不确定值的函数,打印结果可能是任何整数printf("fun03() 返回的数据(不确定值):%d \n", fun03());return 0;
}

3.4 返回类型不一致

        如果函数的返回类型与 return 语句中表达式的类型不一致,编译器会尝试进行隐式类型转换

3.4.1 如果转换是安全的

        编译器会进行类型转换,将 return 语句中的值转换为函数声明的返回类型。这种转换可能涉及数据截断(例如,从 int 转换为 char)、符号扩展(例如,从 unsigned char 转换为 int)或浮点数的精度损失(例如,从 double 转换为 float)。

#include <stdio.h>// 返回值类型为 int,但 return 语句中的值是 double 类型
// 这种情况下,double 值会被隐式转换为 int 类型,导致精度损失
int fun04()
{return 20.89;
}int main()
{// 调用返回值类型不匹配的函数,打印结果为 20,因为 20.89 被隐式转换为整数printf("fun04() 返回的数据(精度损失):%d \n", fun04());return 0;
}

3.4.2 如果转换不安全或不可能

        编译器会报错。例如,如果尝试从 void 类型的函数返回一个值(因为 void 类型表示“无类型”或“无返回值”),或者尝试返回一个指向局部变量的指针(因为局部变量在函数返回后会被销毁,导致悬垂指针),编译器可能会报错或给出警告。如下图所示:

3.5 综合案例演示

        综合上述四种情况,我们通过下面的代码来进行综合的案例演示:

#include <stdio.h>// 无返回值类型的函数,使用 void(即空类型)表示。
void fun01()
{printf("调用了 fun01 函数\n");
}// 有返回值类型的函数,返回 double 类型的值
double fun02()
{return 3.14;
}// 返回值类型为 int,但函数中没有 return 语句
// 这将导致函数返回一个不确定的值(通常是未定义行为)
int fun03()
{10 + 20; // 这行代码仅计算了 30,但没有将其赋值给任何变量,也没有返回它// 由于缺少 return 语句,函数将返回一个不确定的值
}// 返回值类型为 int,但 return 语句中的值是 double 类型
// 这种情况下,double 值会被隐式转换为 int 类型,导致精度损失
int fun04()
{return 20.89;
}// 这个函数声明为 void 类型,意味着它不应该有返回值
// 如果尝试返回一个值(如已被注释的代码所示),编译器会发出警告或错误
void fun05()
{return 666; // 如果不注释这行代码,这会导致编译错误或警告,因为 void 函数不能返回值
}int main()
{// 调用无返回值的函数,仅执行其内部的打印操作fun01();// 调用返回 double 类型值的函数,并打印其返回值printf("fun02() 返回的数据:%.2f \n", fun02());// 调用返回不确定值的函数,打印结果可能是任何整数printf("fun03() 返回的数据(不确定值):%d \n", fun03());// 调用返回值类型不匹配的函数,打印结果为 20,因为 20.89 被隐式转换为整数printf("fun04() 返回的数据(精度损失):%d \n", fun04());return 0;
}

        输出结果如下所示:

        提示:为了避免潜在的问题和保持代码的可读性,建议总是确保函数的返回类型与 return 语句中表达式的类型一致,或者至少确保它们之间的转换是明确且安全的。如果需要进行类型转换,最好显式地进行(使用类型转换运算符,如 (int) ),这样可以使代码的意图更加清晰。

        现代 C 编译器通常会在检测到可能的类型转换问题时发出警告或错误。这些警告和错误可以帮助开发者识别并修复潜在的问题。因此,建议开启编译器的所有警告选项,并仔细审查编译器输出的任何警告信息。


4 函数的参数

        函数的参数分为形参与实参。

4.1 形参与实参

        在定义函数时,函数名后面括号 () 中声明的变量称为形式参数,简称形参。

        在调用函数时,函数名后面括号 () 中的使用的常量、变量、表达式称为实际参,简称实参。

        注意:实参的数量要与形参的数量一致,否则会报错。

4.2 参数传递 - 值传递 

        在 C 语言中,当我们调用一个函数时,实参(实际参数)用于初始化形参(形式参数)。这个过程通常被称为 “参数传递”。

        当调用一个函数时,实参的值会被复制给形参,形参就像是一个临时变量,用于存储实参的值。因此,可以认为实参 “初始化” 了形参,但这并不是一个标准术语,而是描述这一过程的一种说法。

        C 语言本身不直接支持引用传递,但可以通过传递实参的地址来间接实现(后续学习)。此时,形参通常是指针,指向实参的地址。

#include <stdio.h>/*** 函数功能:计算两个整数的和* @param x 第一个整数(形参)* @param y 第二个整数(形参)* @return 返回 x 和 y 的和*/
int func(int x, int y)
{// 返回两个整数的和return x + y;
}int main()
{// 调用 func 函数,实参为 3 和 5,用于计算它们的和int sum = func(3, 5);printf("%d \n", sum); // 输出:8// 如果实参数量与形参不一致时,编译器会报错func(100, 299, 300); // 错误:func 函数只接受两个参数func(100);           // 错误:func 函数需要两个参数return 0;
}

        如果实参数量与形参数量不一致,编译器会报错,如下所示:


5 文档注释

        在 C 语言中,传统的注释有两种形式:

  • 单行注释:使用 // 开头。
  • 多行注释:使用 /* 开始并以 */ 结束。

        然而,这些传统的注释方式并不能直接生成可读性高的文档。为了生成易于阅读和理解的文档,程序员们常常使用一种特殊的注释格式——文档注释。虽然 C 语言本身并不支持特定的文档注释语法,但一些工具可以解析特定格式的注释来生成文档。

        VS Code 中文档注释的快捷键是 /**

/*** @brief 计算两个整数的和* * @param x 第一个整数* @param y 第二个整数* * @return 返回 x 和 y 的和*/
int func(int x, int y)
{// 返回两个整数的和return x + y;
}
  • @brief:简要描述函数的功能。
  • @param:描述每个参数的作用。
  • @return:描述函数返回值的意义。

6 函数原型

        在 C 语言中,默认情况下,函数必须先声明后使用因为在调用一个函数之前,编译器需要了解函数的返回类型和参数列表等基本信息。鉴于程序执行通常是从 main() 函数开始的,我们习惯于将所有函数的声明放置在 main() 函数之前。

        如果想将函数的声明放在 main() 函数之后,可以通过在程序的开头部分给出函数的原型。函数原型包含了必要的信息(如返回类型、参数类型及数量),但不包括具体的实现逻辑。这样一来,即使函数的实际定义位于程序的较后位置,编译器也能正确地进行处理。

#include <stdio.h>// 函数原型声明
int twice1(int num1, int num2); // 分号;是必需的
int twice2(int, int, int);      // 形参的名称可以省略掉// 主函数
int main(void)
{int result1, result2;// 调用函数 twice1 并打印结果result1 = twice1(10, 5); // 30printf("twice1(10, 5) = %d\n", result1);// 调用函数 twice2 并打印结果result2 = twice2(10, 5, 2); // 34printf("twice2(10, 5, 2) = %d\n", result2);return 0;
}// 函数 twice1 的定义
/*** @brief 这个函数接收两个整数作为参数,并返回它们乘以 2 的结果** @param num1* @param num2* @return int*/
int twice1(int num1, int num2)
{return (num1 + num2) * 2;
}// 函数 twice2 的定义
/*** @brief 这个函数接收三个整数作为参数,并返回它们乘以 2 的结果** @param num1* @param num2* @param num3* @return int*/
int twice2(int num1, int num2, int num3)
{return (num1 + num2 + num3) * 2;
}

7 案例:通过函数原型实现多文程

        学习完函数原型之后,我们可以利用这一功能特性来实现简易版的多文件编程。具体步骤如下:

7.1 搭建文件结构

        首先创建一个名为 math 的文件夹,在 math 里面创建五个源文件,分别为 main.c、add.c、sub.c、mul.c 和 div.c。

  • main.c:包含 main 函数和所有函数的原型声明。
  • add.c:只包含加法函数的实现。
  • sub.c:只包含减法函数的实现。
  • mul.c:只包含乘法函数的实现。
  • div.c:只包含除法函数的实现,并注意处理除数为 0 的情况。

7.2 编写源代码

        main.c 中代码内容如下所示:

#include <stdio.h>// 函数原型声明
int add(int a, int b);
int sub(int a, int b);
int mul(int a, int b);
int div(int a, int b); int main()
{printf("5 + 3 = %d\n", add(5, 3));printf("5 - 3 = %d\n", sub(5, 3));printf("5 * 3 = %d\n", mul(5, 3));printf("5 / 3 = %d\n", div(5, 3));return 0;
}

        add.c 中代码内容如下所示:

// add.c
// 只包含加法函数的实现int add(int a, int b)
{return a + b;
}

        sub.c 中代码内容如下所示:

// sub.c  
// 只包含减法函数的实现  int sub(int a, int b) {  return a - b;  
}

        mul.c 中代码内容如下所示:

// mul.c  
// 只包含乘法函数的实现  int mul(int a, int b) {  return a * b;  
}

        div.c 中代码内容如下所示:

// div.c
// 只包含除法函数的实现,注意处理除数为 0 的情况int div(int a, int b)
{if (b == 0){// 这里只是简单地返回 0,实际中可能需要更复杂的错误处理// 这通常不是一个好的做法,因为它隐藏了错误return 0; }return a / b;
}

7.3 统一编译源文件

        在 VS Code 中,如果想编译多个 C 文件,通常会使用 tasks.json 文件来配置一个编译任务。这个任务会告诉 VS Code 如何调用编译器(如 gcc 或 clang )来编译你的代码。

        在这里,我们的目标是简单地实现一个多文件编程的 C 项目,而不想通过 VS Code 的复杂配置(如修改 tasks.json)来达成。我们可以直接利用命令行(cmd)这一更为基础且灵活的工具来编译我们的多个 C 源文件,具体操作如下图所示:

这篇关于16 函数的基本概念、声明与调用、返回值、值传递、原型声明,文档注释,多文件编程(简易版)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1104960

相关文章

Java调用Python代码的几种方法小结

《Java调用Python代码的几种方法小结》Python语言有丰富的系统管理、数据处理、统计类软件包,因此从java应用中调用Python代码的需求很常见、实用,本文介绍几种方法从java调用Pyt... 目录引言Java core使用ProcessBuilder使用Java脚本引擎总结引言python

SpringBoot3集成swagger文档的使用方法

《SpringBoot3集成swagger文档的使用方法》本文介绍了Swagger的诞生背景、主要功能以及如何在SpringBoot3中集成Swagger文档,Swagger可以帮助自动生成API文档... 目录一、前言1. API 文档自动生成2. 交互式 API 测试3. API 设计和开发协作二、使用

Java向kettle8.0传递参数的方式总结

《Java向kettle8.0传递参数的方式总结》介绍了如何在Kettle中传递参数到转换和作业中,包括设置全局properties、使用TransMeta和JobMeta的parameterValu... 目录1.传递参数到转换中2.传递参数到作业中总结1.传递参数到转换中1.1. 通过设置Trans的

java如何调用kettle设置变量和参数

《java如何调用kettle设置变量和参数》文章简要介绍了如何在Java中调用Kettle,并重点讨论了变量和参数的区别,以及在Java代码中如何正确设置和使用这些变量,避免覆盖Kettle中已设置... 目录Java调用kettle设置变量和参数java代码中变量会覆盖kettle里面设置的变量总结ja

C#反射编程之GetConstructor()方法解读

《C#反射编程之GetConstructor()方法解读》C#中Type类的GetConstructor()方法用于获取指定类型的构造函数,该方法有多个重载版本,可以根据不同的参数获取不同特性的构造函... 目录C# GetConstructor()方法有4个重载以GetConstructor(Type[]

C++11的函数包装器std::function使用示例

《C++11的函数包装器std::function使用示例》C++11引入的std::function是最常用的函数包装器,它可以存储任何可调用对象并提供统一的调用接口,以下是关于函数包装器的详细讲解... 目录一、std::function 的基本用法1. 基本语法二、如何使用 std::function

基于C#实现将图片转换为PDF文档

《基于C#实现将图片转换为PDF文档》将图片(JPG、PNG)转换为PDF文件可以帮助我们更好地保存和分享图片,所以本文将介绍如何使用C#将JPG/PNG图片转换为PDF文档,需要的可以参考下... 目录介绍C# 将单张图片转换为PDF文档C# 将多张图片转换到一个PDF文档介绍将图片(JPG、PNG)转

hdu1171(母函数或多重背包)

题意:把物品分成两份,使得价值最接近 可以用背包,或者是母函数来解,母函数(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v) 其中指数为价值,每一项的数目为(该物品数+1)个 代码如下: #include<iostream>#include<algorithm>

如何在页面调用utility bar并传递参数至lwc组件

1.在app的utility item中添加lwc组件: 2.调用utility bar api的方式有两种: 方法一,通过lwc调用: import {LightningElement,api ,wire } from 'lwc';import { publish, MessageContext } from 'lightning/messageService';import Ca

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来