目录
前言
一个较大的程序一般分为若干个小的程序模块,每个模块实现一个特定的功能。所有的 高级语言中都有子程序的概念,用子程序来实现模块的功能。 在 C 语言中,子程序的功能是由函数来完成的。函数是 C 语言源程序的基本组成单位, 用来实现特定的功能。一个 C 程序可以由一个主函数和若干个子函数构成。由主函数调用其 他函数,其他函数之间也可以互相调用。可以说,C 程序的全部工作是由函数完成的,所以 也把 C 语言称为函数式语言。 由于采用了函数模块式的结构,使 C 语言易于实现结构化程序设计,模块化的程序层次 结构清晰,便于程序的编写、阅读和调试。 在程序设计中,常将一些常用的功能模块编写成函数,放在函数库中供公共选用。程序 设计时要善于利用函数,以减少重复编写程序段的工作量。
(1)一个源程序文件由一个或多个函数组成。一个源程序文件是一个编译单位,即以源 文件为单位进行编译,而不是以函数为单位进行编译。
(2)一个 C 程序由一个或多个源程序文件组成。对较大的程序,一般不希望全放在一个 文件中,而将函数及其他有关内容(如指令、数据声明与定义等)分别放到若干个源文件 中,再由源文件组成一个 C 程序,分别编写和编译,从而提高工作效率。一个源文件可以为多 个 C 程序公用。 (3)C 程序的执行从 main 函数开始,调用其他函数后流程回到 main 函数,在 main 函数 中结束整个程序的运行。main 函数是由系统定义的函数。
(4)所有函数都是平行的,即在定义函数时是互相独立的,一个函数并不从属于另一个 函数,即函数不能嵌套定义。函数可以互相调用,但不能调用 main 函数。
(5)从函数定义的角度看,函数分为标准函数和用户自定义函数。 ①标准函数即库函数,如由 C 编译系统提供,用户无须定义,也不必在程序中作类型说 明,只需在程序前包含该函数原型的头文件,即可在程序中直接调用,printf 函数和 scanf 函 数。不同的编译系统提供的库函数的数量和功能不同,但有一些基本的函数是共同的。 ②用户自定义函数是由用户按需要编写的函数,对于用户自定义函数,不仅要在程序中 定义函数,而且要在主调函数模块中对该被调函数进行类型声明后才能使用。
(6)从函数的形式看,函数分无参函数和有参函数。
①无参函数。在调用无参函数时,主调函数并不将数据传送给被调函数,而用来执行指 定的一组操作。无参函数可以返回或不返回函数值,一般不返回函数值。 ②有参函数。在调用函数时,在主调函数和被调用函数之间有参数传递,即主调函数将 数据传给被调用函数使用,被调用函数中的返回数据供主调函数使用。
本人每天不定时分享一些自己以往总结的笔记
一.函数的定义
c语言要求,在程序中遇到的所有函数,都必须“先定义,后使用”的原则。
1.函数定义包括的内容:
Ⅰ.指定函数类别
表明该函数是内部函数(static)还是外部函数(extern)。若为内部 函数,则该函数只能在定义它的文件中被使用,而不能被引用到其他文件中;若为外部函 数,则该函数可以被引用到整个程序的其他文件中。若省略函数的类别,系统则默认为外部 函数。
Ⅱ.指定函数类型
即函数返回值的类型。表明该函数是否有返回值,若没有返回 值,则该部分应为 void;若有返回值,则该部分要标明返回值的具体类型,并与 return 语句 中的表达式类型相一致。若省略函数的返回值类型,则系统默认为 int。
Ⅲ.指定函数名
指定函数名,以便以后按名调用。函数名是一个标识符,应符合标识符的起名规 则。函数名是函数的入口地址。
Ⅳ.指定函数的参数名称和类型
指定函数的参数名称和类型,以便在调用函数时传递数据。对无参函数该项为 Void 或为空。
Ⅴ.指定函数的函数体
指定函数的函数体,即函数的执行部分。函数体是函数的主体,只有按功能编写相 应的语句行,才能实现程序的功能。
2.函数定义的一般形式:
Ⅰ.有参函数的定义形式:
类型标识符 函数名(形式参数表列) /*函数的首部,形式参数简称为形参*/
{
声明部分
执行部分
}
Ⅱ.无参函数的定义形式:
类型标识符 函数名()
{
声明部分
执行部分
}
注:若函数无返回值,可用类型标识符void表示。
二.函数的调用形式
1.有参函数的调用形式:
函数名(实际参数表列)
2.无参函数的调用形式:
函数名()
注:无参函数调用时,函数名后面的“ ()”不能省略。
3.调用函数的三种形式:
Ⅰ.调用函数作为c语言单独的c程序语句:
调用函数作为单独的 C 程序语句,是为了实现被调函数的功能,不要求函数带返回值。 此时,只需在函数名(实参表列)后增加分号“;”。
函数名(实参表列);
若被调用函数是无参函数,则为: 函数名();
Ⅱ.调用函数作为表达式的运算对象:
函数的调用出现在表达式里,要求函数返回一个确定值。
Ⅲ.带返回值的函数调用作为其他函数的实参
注:在调用函数时,函数名后的多个实参之间用逗号分开。实参与形参的数量必须相 等,对应类型应一致,实参与形参按顺序对应,一一传递数据。
三.函数的调用过程
函数的调用过程是:
(1)传递参数,为函数的所有形参分配内存单元,计算各个实参表达式的值,并按顺序 为相应的形参赋值。若是无参函数,则不执行此过程。
(2)进入声明部分,为函数的局部变量分配内存单元。
(3)进入函数体,执行函数中的语句,实现函数的功能,遇到 return 语句时,计算 return 语句中表达式的值(若函数无返回值,本项不做),释放形参及本函数内定义的变量所 占的内存空间,返回主调函数。
(4)继续执行主调函数中的后继语句。
四.函数的声明
调用一个函数,首先要求该函数已经被定义,但仅有定义,有时仍然不能正确调用该函数,这时需要增加对被调函数的声明。因此,一个函数调用另一个函数必须具备的前提是:
(1)被调函数已存在,即被调函数是标准库函数或者用户已定义的函数。
(2)对于标准库函数,要在主调函数所在文件前面,用#include 命令将相应的头文件包 含进来;对于用户定义函数要在主调函数调用前对其进行声明。 函数声明的目的是使编译系统在编译阶段对函数的调用进行合法性检查,判断形参与实 参的类型、数量是否匹配。未进行函数声明而直接调用函数,可能会产生错误。 用户自定义函数时,每个函数只能被定义一次,而函数声明可以有多次。主调函数在调 用函数之前,对该函数进行声明,即通知编译系统将调用该函数。 对函数声明采用函数原型的方法,函数声明格式如下: 类型标识符 函数名(形参表列); 无参函数的形参表列为空,但“()”不能省略。
在下面 3 种情况可以省略对被调用函数的声明:
(1)被调用函数的定义出现在主调函数之前,编译系统预先知道函数的类型,可根据函 数首部对函数的调用进行检查。
(2)被调函数的返回值是整型或字符型,整型是系统默认的类型。
(3)在所有函数定义之前,在函数的外部对函数已经做了声明。 调用函数前,对函数进行声明是一种良好的程序设计习惯,既能提高程序的正确性,把 错误限制在较小的范围内(函数内),还能提高程序的可读性。为了程序的清晰和安全,建 议在主调函数中对返回值为整型或字符型的被调函数进行声明。
五.函数的返回值
函数是完成特定功能的程序段,主调函数通过函数调用完成相应功能,有时调用函数的 目的不仅是完成其功能,也需要得到计算结果,这就需要函数的返回值。返回值的类型即为 函数的类型。
函数的返回值通过被调用函数中的 return 语句实现。其调用格式为:
return 表达式;
或
return (表达式);
注:(1)return 语句的执行过程是首先计算表达式的值,然后将计算结果返回给主调 函数。 (2)在定义函数时指定的函数类型一般应与 return 语句中的表达式类型一致。
(3)如果函数返回值的类型和 return 语句中表达式的值不一致,则以函数类型为 准。数值型数据可以自动进行类型转换。函数返回值的类型由定义函数时,函数的首部 类型决定。
(4)没有返回值的函数用 void 定义其函数值的类型,否则,函数将返回一个不确定 的值。
(5)return 语句的另一项功能是结束被调用函数的运行,返回到主调函数中继续执 行后继语句。在无返回值的函数中也可以有 return 语句
其格式为: return;
这里的 return 语句用于结束函数的执行,返回主调函数。无 return 语句的函数执行 到函数体后的“}”结束,返回主调函数。
六.经验总结和题目分享
1.经验总结:
学习C语言函数,关键在于理解函数的定义、调用和参数传递。首先,要掌握函数的定义语法,包括返回值类型、函数名、参数列表和函数体。其次,要学会如何调用函数,并理解参数传递的方式,例如值传递和地址传递。最后,要注重函数的封装性,将代码模块化,提高代码的可读性和可维护性。
此外,学习C语言函数还需要注意以下几点:
函数的返回值:函数可以返回一个值,也可以不返回值。返回值类型要与函数定义中的返回值类型一致。
函数的参数: 函数可以接受多个参数,参数类型要与函数定义中的参数类型一致。
函数的递归调用:函数可以调用自身,这种调用方式称为递归调用。递归调用可以解决一些复杂的问题,但要注意递归的深度,避免栈溢出。
函数的指针参数: 函数可以接受指针类型的参数,通过指针可以修改函数外部变量的值。
函数的数组参数:函数可以接受数组类型的参数,但实际上传递的是数组的首地址。
通过不断练习和实践,你将能够熟练掌握C语言函数的使用,并将其应用于各种编程场景。
后面三点我会在下一篇中讲到。
2.题目分享:
题目1: 编写一个C语言函数,该函数名为 find_max_min
,它接受一个整数数组和数组的大小,然后在数组中找到最大值和最小值,并通过指针参数返回这两个值。
#include <stdio.h>
// 函数声明
void find_max_min(int arr[], int size, int *max, int *min);
int main() {
int numbers[] = {3, 6, 2, 8, 4, 7};
int size = sizeof(numbers) / sizeof(numbers[0]);
int max, min;
find_max_min(numbers, size, &max, &min);
printf("Maximum element is: %d\n", max);
printf("Minimum element is: %d\n", min);
return 0;
}
// 函数定义
void find_max_min(int arr[], int size, int *max, int *min) {
if (size <= 0) {
// 如果数组为空或大小不正确,设置最大值和最小值为0
*max = 0;
*min = 0;
return;
}
*max = arr[0];
*min = arr[0];
for (int i = 1; i < size; i++) {
if (arr[i] > *max) {
*max = arr[i]; // 更新最大值
}
if (arr[i] < *min) {
*min = arr[i]; // 更新最小值
}
}
}
题目2: 编写一个C语言函数,该函数名为 reverse_array
,它接受一个整数数组和数组的大小,然后将数组中的元素顺序反转。
#include <stdio.h>
// 函数声明
void reverse_array(int arr[], int size);
void print_array(int arr[], int size) {
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
int numbers[] = {1, 2, 3, 4, 5};
int size = sizeof(numbers) / sizeof(numbers[0]);
printf("Original array: ");
print_array(numbers, size);
reverse_array(numbers, size);
printf("Reversed array: ");
print_array(numbers, size);
return 0;
}
// 函数定义
void reverse_array(int arr[], int size) {
int temp;
for (int i = 0; i < size / 2; i++) {
temp = arr[i];
arr[i] = arr[size - 1 - i];
arr[size - 1 - i] = temp;
}
}
题目3: 编写一个C语言函数,该函数名为 sort_array
,它接受一个整数数组和数组的大小,然后使用冒泡排序算法对数组进行升序排序。
#include <stdio.h>
// 函数声明
void sort_array(int arr[], int size);
void swap(int *xp, int *yp);
void print_array(int arr[], int size) {
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
int numbers[] = {64, 34, 25, 12, 22, 11, 90};
int size = sizeof(numbers) / sizeof(numbers[0]);
printf("Original array: ");
print_array(numbers, size);
sort_array(numbers, size);
printf("Sorted array: ");
print_array(numbers, size);
return 0;
}
// 交换两个元素的值
void swap(int *xp, int *yp) {
int temp = *xp;
*xp = *yp;
*yp = temp;
}
// 使用冒泡排序算法对数组进行排序
void sort_array(int arr[], int size) {
int i, j;
for (i = 0; i < size - 1; i++) {
// 最后 i 个元素已经是排好序的了
for (j = 0; j < size - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
swap(&arr[j], &arr[j + 1]);
}
}
}
}
题目4: 编写一个C语言函数,该函数名为 calculate_factorial
,它接受一个非负整数 n
,然后计算并返回 n
的阶乘(n!
)。如果 n
是负数,则函数应返回 -1 表示错误。使用递归来实现这个函数。
#include <stdio.h>
// 函数声明
long long calculate_factorial(int n);
int main() {
int number;
printf("Enter a non-negative integer: ");
scanf("%d", &number);
long long factorial = calculate_factorial(number);
if (factorial == -1) {
printf("Error: Factorial of a negative number doesn't exist.\n");
} else {
printf("Factorial of %d is %lld\n", number, factorial);
}
return 0;
}
// 使用递归计算阶乘
long long calculate_factorial(int n) {
if (n < 0) {
return -1; // 如果输入是负数,返回错误
} else if (n == 0) {
return 1; // 0的阶乘是1
} else {
return n * calculate_factorial(n - 1); // 递归调用
}
}
注意,由于阶乘的结果很快就会变得非常大,这里使用了 long long
类型来存储结果,这样可以处理比 int
类型更大的数。但是,即使使用 long long
,对于较大的 n
值,仍然可能会发生溢出。在实际应用中,可能需要使用特殊的库来处理非常大的整数。
题目5: 编写一个C语言函数,该函数名为 string_reverse
,它接受一个字符数组(字符串)作为参数,然后原地(不使用额外的字符串)反转该字符串。假设传入的字符数组足够大,可以容纳反转后的字符串,包括结尾的空字符(\0
)。
#include <stdio.h>
#include <string.h>
// 函数声明
void string_reverse(char str[]);
int main() {
char str[] = "Hello, World!";
printf("Original string: %s\n", str);
string_reverse(str);
printf("Reversed string: %s\n", str);
return 0;
}
// 反转字符串的函数
void string_reverse(char str[]) {
int len = strlen(str); // 获取字符串长度
for (int i = 0; i < len / 2; i++) {
// 交换字符
char temp = str[i];
str[i] = str[len - 1 - i];
str[len - 1 - i] = temp;
}
}
题目6: 编写一个C语言函数,该函数名为 allocate_and_fill_array
,它接受一个整数 n
作为参数,动态分配一个整数数组,并用从1到n
的连续整数填充该数组。然后,函数返回指向该数组的指针。如果内存分配失败,则返回 NULL
。
#include <stdio.h>
#include <stdlib.h>
// 函数声明
int* allocate_and_fill_array(int n);
int main() {
int n;
printf("Enter the number of elements: ");
scanf("%d", &n);
int* array = allocate_and_fill_array(n);
if (array == NULL) {
printf("Memory allocation failed.\n");
return 1;
}
// 打印数组
for (int i = 0; i < n; i++) {
printf("%d ", array[i]);
}
printf("\n");
// 释放动态分配的内存
free(array);
return 0;
}
// 动态分配数组并填充的函数
int* allocate_and_fill_array(int n) {
int* arr = (int*)malloc(n * sizeof(int)); // 动态分配内存
if (arr == NULL) {
return NULL; // 如果分配失败,返回NULL
}
for (int i = 0; i < n; i++) {
arr[i] = i + 1; // 填充数组
}
return arr; // 返回指向数组的指针
}
在这个例子中,allocate_and_fill_array
函数首先使用 malloc
动态分配了一个整数数组。然后,它使用一个循环来填充这个数组,将值从1增加到 n
。如果内存分配失败,malloc
返回 NULL
,函数也返回 NULL
。
在 main
函数中,我们调用 allocate_and_fill_array
并检查返回值。如果成功,我们打印数组的内容,然后使用 free
释放分配的内存。这是非常重要的一步,因为如果不释放动态分配的内存,将会导致内存泄漏。
注意:动态内存分配和释放是C语言编程中的高级主题,需要谨慎处理以避免内存泄漏和其他错误。
新手c语言讲解及题目分享(十四)--函数专项练习(二)-CSDN博客
————由于博主还是大三的在读生,时间有限,每天会不定时更新一些学习经验和一些32的项目,如果喜欢就点点关注吧,大佬们!!!!————