C语言函数全景解读:从基础到进阶

    在这篇文章中,我们将深入探讨C语言中的一个非常基础却极其重要的功能——函数。从最基础的概念开始,逐步深入到复杂的应用,我们将一步一步解锁C函数的奥秘。我们的旅程将从函数的定义开始,探索其语法和结构,然后通过实例学习如何在实际编程中有效地使用函数。

一,函数的定义和重要性

在C语言中,函数是执行特定任务的独立代码块。它们可以接受输入(参数),执行特定的操作,并产生输出(返回值)。函数的使用有助于提高代码的可读性和可重用性,使得程序结构更加清晰,更容易理解和维护。

二,函数的基本语法

C语言中的函数可以分为两大类:标准库函数和用户定义函数。标准库函数是C语言自带的,例如 printf()”,而用户定义函数则是由程序员根据需要自行定义的。一个基本的用户定义函数的语法结构如下:

返回类型 函数名(参数列表)
{
    函数体
}
  • 返回类型:指定函数返回值的数据类型。如果函数不返回任何值,则使用 void”
  • 函数名:函数的唯一标识,用于在其他地方调用此函数。
  • 参数列表:函数接收的输入值,参数之间用逗号分隔。如果函数不接受任何参数,则参数列表为空或写为 void”
  • 函数体:包含执行特定任务的一系列语句,这是函数的核心部分。

一个简单的函数示例

#include <stdio.h>

// 函数定义
int add(int a, int b)
{
    return a + b;
}

int main()
{
    int result = add(5, 3); // 调用函数
    printf("结果是:%d\n", result);//结果是:8
    return 0;
}

这就类似于我们之前在数学中学习的函数y=kx+b —>result=a+b,你们可以好好体会一下是不是这样的。

三,参数传递和返回值

函数的参数可以按值传递,也可以通过指针传递以允许函数修改参数的值。返回值则是函数执行完成后返回给调用者的结果,它的类型必须与函数定义中的返回类型相匹配。

按值传递参数的例子

在按值传递中,函数接收参数的原始值的副本。在函数内对参数的任何修改都不会影响原始数据。

#include <stdio.h>

// 函数定义,按值传递参数
void addTen(int num) {
    num += 10; // 对副本进行操作
    printf("在函数内部,值变为:%d\n", num);//在函数内部,值变为:15
}

int main() {
    int original = 5;
    addTen(original); // 调用函数,传入original的值
    printf("在函数外部,原始值仍为:%d\n", original); // original的值未改变,在函数外部,原始值仍为:5
    return 0;
}


在这个例子中,即使在 addTen 函数中对 num 变量增加了10,原始的 original 变量的值在函数外部仍然未改变,因为我们是按值传递的。

通过指针传递参数的例子

通过指针传递允许函数直接修改原始数据的值,因为传递的是数据地址的副本,而不是数据值的副本。

#include <stdio.h>

// 函数定义,通过指针传递参数
void addTen(int *numPtr) {
    *numPtr += 10; // 通过指针修改原始值
    printf("在函数内部,值变为:%d\n", *numPtr);在函数内部,值变为:15
}

int main() {
    int original = 5;
    addTen(&original); // 传入original的地址
    printf("在函数外部,原始值变为:%d\n", original); // original的值已改变,在函数外部,原始值变为:15
    return 0;
}

这个输出表明,通过指针传递参数允许 addTen 函数直接修改原始变量 original 的值。因此,当我们在函数外打印 original 的值时,可以看到它已经被修改。

函数返回值的例子

函数返回值允许函数将结果传回给调用者。返回值的类型必须与函数声明的返回类型相匹配。

#include <stdio.h>

// 函数定义,返回两数之和
int sum(int a, int b) {
    return a + b; // 返回结果
}

int main() {
    int result = sum(5, 3); // 调用函数,并接收返回值
    printf("两数之和为:%d\n", result);//两数之和为:8

    return 0;
}

四,函数的进阶使用

随着你对C语言的深入了解,你会遇到更多高级主题,如递归函数、指针作为函数参数、函数指针以及内联函数等。这些都是提高你C语言功力的重要知识点。

我会通过介绍更多的函数体使用示例,以及一些进阶概念,来扩展我们对函数的理解。我们将通过几个不同的例子,展示函数在解决实际问题中的强大能力。

递归函数

递归函数是一种自我调用的函数,它可以用来解决分而治之的问题,如阶乘计算、斐波那契数列生成等。递归函数必须有一个明确的终止条件,以防止无限递归导致程序崩溃。

示例:计算阶乘

#include <stdio.h>

// 递归函数定义
int factorial(int n)
{
    if (n == 0)
        return 1; // 终止条件
    else
        return n * factorial(n - 1); // 递归调用
}

int main()
{
    int num = 5;
    printf("%d 的阶乘是:%d\n", num, factorial(num));
    return 0;
}

为了更好地理解递归函数 factorial 如何工作,我们可以通过画图的方式展示函数调用的过程。以下是对给定代码的图解,它展示了计算 5 的阶乘 (5!) 的递归调用过程。

1.初始调用:开始时,我们从 main 函数调用 factorial(5)

main
 |
factorial(5)

2.递归调用过程factorial(5) 需要计算 5 * factorial(4),但在计算 factorial(4) 之前,无法完成计算。这一过程会一直持续,直到达到递归的基准情况(即 factorial(0))。

factorial(5)
 |
factorial(4)
 |
factorial(3)
 |
factorial(2)
 |
factorial(1)
 |
factorial(0)

3.达到基准情况:当调用 factorial(0) 时,根据函数定义,直接返回 1,无需进一步的递归调用。

factorial(0) -> returns 1

4.递归返回:一旦达到基准情况,每个递归调用开始依次返回其结果到上一层调用,直到最初的调用 factorial(5)

factorial(0) -> returns 1
factorial(1) -> returns 1 * 1 = 1
factorial(2) -> returns 2 * 1 = 2
factorial(3) -> returns 3 * 2 = 6
factorial(4) -> returns 4 * 6 = 24
factorial(5) -> returns 5 * 24 = 120

5.完成计算并返回最终结果:最终,factorial(5) 返回 120main 函数,并被打印出来。

main
 |
factorial(5) -> returns 120

通过这种方式,递归函数将一个大问题分解为更小、更易于管理的问题(在这个例子中,是通过减少 n 的值来实现的),直到达到一个简单的情况可以直接解决(即 n == 0 时)。每一步的计算结果都依赖于下一步的结果,直到最后一步计算完成,然后逐步返回到最初的调用处。

需要注意的递归必须要有条件,否则他就会无限的递归下去,从而导致程序崩溃

就类似下面:

五,指针和函数

通过指针,函数可以修改调用者的变量。这在处理数组、字符串和动态数据结构时尤其有用。

示例:交换两个变量的值

#include <stdio.h>

// 使用指针参数交换两个变量的值
void swap(int *a, int *b)
{
    int temp = *a;
    *a = *b;
    *b = temp;
}

int main()
{
    int x = 10, y = 20;
    printf("交换前:x = %d, y = %d\n", x, y);
    swap(&x, &y); // 调用函数
    printf("交换后:x = %d, y = %d\n", x, y);
    return 0;
}

这段代码演示了如何使用C语言中的指针来交换两个变量的值。下面是对这段代码的逐行详解:

包含标准输入输出库

#include <stdio.h>

这一行代码包含了标准输入输出库 stdio.h,它允许我们使用输入输出函数,如 printf

定义交换函数

void swap(int *a, int *b)
{
    int temp = *a;
    *a = *b;
    *b = temp;
}
  • void swap(int *a, int *b) 定义了一个名为 swap 的函数,它接受两个整型指针 a b 作为参数。这意味着,ab 存储的是两个整数变量的地址。
  • int temp = *a; 声明了一个名为 temp 的整型变量,并将 a 指向的值(即 a 指针所指向的变量的当前值)赋给 temp。这一步实际上保存了变量 a 指向的值。
  • *a = *b;b 指向的值赋给 a 指向的位置。这一步实际上将 b 的值复制到了 a 的位置。
  • *b = temp; 最后,将 temp即原先 a 指向的值)赋给 b 指向的位置。这一步完成了两个值的交换。

主函数

int main()
{
    int x = 10, y = 20;
    printf("交换前:x = %d, y = %d\n", x, y);
    swap(&x, &y); // 调用函数
    printf("交换后:x = %d, y = %d\n", x, y);
    return 0;
}
  • int main() 定义了程序的入口点,即主函数。
  • int x = 10, y = 20; 声明了两个整型变量 xy,并分别初始化为 1020
  • printf("交换前:x = %d, y = %d\n", x, y); 打印出交换前 x y 的值。
  • swap(&x, &y); 调用了 swap 函数,传入 xy 的地址。这允许 swap 函数直接操作 xy 变量的内存,从而交换它们的值。
  • printf("交换后:x = %d, y = %d\n", x, y); 再次打印 x y 的值,展示它们已经被交换。
  • return 0; 表示程序正常结束。

通过上述解析,可以看到,这个程序通过将变量地址传递给 swap 函数,并在函数内部通过指针操作来直接修改变量的值,从而实现了两个变量值的交换,而不需要使用第三个变量在主函数中进行中间存储。

六,函数指针

函数指针是指向函数的指针,通过它可以实现回调函数和高度灵活的编程模式。函数指针允许将函数作为参数传递给其他函数,增加了程序的灵活性。

示例:使用函数指针排序

#include <stdio.h>
#include <stdlib.h>

// 比较函数,用于整数升序排序
int compare(const void *a, const void *b)
{
    return (*(int *)a - *(int *)b);
}

int main()
{
    int arr[] = {10, 5, 15, 12, 90, 80};
    int n = sizeof(arr) / sizeof(arr[0]);

    qsort(arr, n, sizeof(int), compare); // 使用函数指针

    for(int i = 0; i < n; i++)
        printf("%d ", arr[i]);
    return 0;
}

这段代码演示了如何使用 C 语言的 qsort 函数对一个整数数组进行排序。qsort 是 C 标准库中提供的一个通用排序函数,可以用来对任意类型的数组进行排序。下面是对这段代码的详细解释:

这两行代码包含了两个头文件:stdio.hstdlib.hstdio.h 上面已经说了;而 stdlib.h 提供了一系列通用的工具函数,包括 qsort 函数。

定义比较函数

int compare(const void *a, const void *b)
{
    return (*(int *)a - *(int *)b);
}
  • compare 函数是一个比较函数,用于确定数组中两个元素的顺序。
  • 函数接受两个参数 a b它们的类型是 const void *意味着可以接受任意类型的指针在函数内部,这些指针被转换成 int * 类型,然后通过解引用操作 * 获取它们指向的整数值。
  • 函数通过计算 *(int *)a - *(int *)b 来确定 a b 指向的整数的相对大小。如果 a 指向的整数小于 b 指向的整数,返回值将为负;如果相等,返回值为零;如果 a 指向的整数大于 b 指向的整数,返回值为正。
  • qsort 函数使用这个比较函数来决定数组元素的排序顺序。

主函数和数组排序

int main()
{
    int arr[] = {10, 5, 15, 12, 90, 80};
    int n = sizeof(arr) / sizeof(arr[0]);

    qsort(arr, n, sizeof(int), compare); // 使用函数指针
  • arr 是一个包含整数的数组,需要被排序。
  • n 是数组 arr 的长度,通过 sizeof(arr) / sizeof(arr[0]) 计算得出。
  • qsort 函数用于对数组 arr 进行排序。它接受四个参数:排序的数组 arr,数组元素的数量 n,每个元素的大小 sizeof(int),以及一个比较函数 compare
  • 在这个例子中,compare 函数作为 qsort 的参数传递,用于指导如何比较数组中的元素。

打印排序后的数组

for(int i = 0; i < n; i++)
    printf("%d ", arr[i]);
return 0;
  • 这个循环遍历排序后的数组 arr,使用 printf 函数打印出每个元素。
  • 最后,main 函数返回 0,表示程序成功结束。

总结来说,这段代码演示了如何使用 qsort 和自定义的比较函数对一个整数数组进行升序排序,并展示了排序后的结果

很多人应该会很好奇,为什么这个qsort这个函数能够排序,下面会给你qsort底层的基本的思路

void Swap(char* buf1, char *buf2, size_t size)
{
	int i = 0;
	for (i = 0; i < size; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}


int Cmp_Int(const void* I1, const void* I2)
{
	return *(int*)I1 - *(int*)I2;
}


void Bubble_Sort(void * base,size_t size,size_t width,int(*cmp)(const void *p1,const void*p2))

{
	for (int i = 0; i < size - 1; i++)
	{
		for (int j = 0; j < size - i - 1; j++)
		{
			if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
			{
				Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
			}
		}
	}
}

void Print_Int(int* p, int size)
{
	int i = 0;
	for (i= 0; i < size;i++)
	{
		printf("%d ", *(p + i));
	}
}

int main()
{
	int arr[] = { 2,4,8,1,10,98,3,454,435, };
	int size = sizeof(arr) / sizeof(arr[0]);
	Bubble_Sort(arr, size, sizeof(int), Cmp_Int);
	Print_Int(arr, size);
	return 0;
}

这个就是它的底层逻辑,学有余力的可以看看

通过上述示例,我们展示了C函数的多样化应用,从基本的递归函数到高级的概念如函数指针和内联函数。每种技术都有其适用场景和优势,理解并掌握这些知识,将使你能够更加灵活和高效地使用C语言。

  • 24
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宿か命

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值