【C 语言入门】Day10 函数传参、递归函数与预处理命令全解析

📅 2026/7/31 5:04:03
【C 语言入门】Day10 函数传参、递归函数与预处理命令全解析
本文为 C 语言学习第十天的知识点整理涵盖函数三种传参方式、递归函数原理与实现、预处理命令宏定义、条件编译、头文件包含文章目录前言1. 函数的三种传参方式1.1 赋值传递复制传递 / 值传递1.3 数组传递1.3.1 一维整型数组传递1.3.2 一维字符数组传递2. 递归函数2.1 什么是递归2.2 递归的两个必要条件2.3 注意事项2.4 经典示例递归实现斐波那契数列3. 预处理命令3.1 宏定义3.1.1 不带参宏3.1.2 带参宏宏函数3.1.3 带参宏与函数的区别3.2 条件编译3.2.1 #if / #elif / #else / #endif3.2.2 #ifdef / #endif3.2.3 #ifndef / #endif3.3 头文件包含核心作用头文件通常存放的内容两种包含方式的区别今日知识点总结1. 函数的三种传参方式函数传参是主调函数向被调函数传递数据的核心方式常见有赋值传递、全局变量传递、数组传递三种形式各自适用场景与特性不同。1.1 赋值传递复制传递 / 值传递核心原理实参将自身的值拷贝一份传递给形参形参是实参的独立副本二者存储在不同的内存空间中。核心特点形参的值发生改变不会影响实参本身的值。仅能将外部数据传入函数内部无法通过形参修改外部变量。适用场景函数体内只需要读取外部变量的值不需要修改外部变量时使用。如果需要修改函数外部变量的值不能使用值传递。二、使用步骤1.引入库代码如下示例import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns import warnings warnings.filterwarnings(ignore) import ssl ssl._create_default_https_context ssl._create_unverified_context2.读入数据代码如下示例data pd.read_csv( https://labfile.oss.aliyuncs.com/courses/1283/adult.data.csv) print(data.head())该处使用的url网络请求的数据。总结提示这里对文章进行总结例如以上就是今天要讲的内容本文仅仅简单介绍了pandas的使用而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。全局重名 printf(局部范围内num%d\n, num); // 优先使用局部变量 } int main() { test(); printf(全局范围内num%d\n, num); return 0; }1.3 数组传递数组传递的本质是传递数组首元素的地址函数内部操作的数组与外部数组是同一块内存空间因此函数内修改数组元素外部数组会同步变化。1.3.1 一维整型数组传递传递时需要传入两个参数数组名数组长度。 函数形参中int array[]本质等价于指针形式不会额外创建数组副本。语法示例int a[5]; // 外部数组 int fun(int array[], int len); // 函数声明1.3.2 一维字符数组传递字符数组即字符串传递时可以只传数组名依靠结束符\0判断字符串结束也可以额外传递长度参数。语法示例char str[32] {hello}; int fun(char pstr[], int len); // 带长度 int fun(char pstr[]); // 依靠\0判断2. 递归函数2.1 什么是递归在定义一个函数时函数体内直接或间接调用函数本身这种函数称为递归函数。 递归的核心思想是把一个大规模的复杂问题拆解为一个或多个与原问题结构相同的小规模问题逐步缩小问题规模直到触达结束条件。2.2 递归的两个必要条件递归出口结束条件必须有明确的终止条件否则会无限递归最终导致栈溢出、程序崩溃。递归递推公式问题可以拆解为更小的同类子问题逐步向结束条件靠近。2.3 注意事项必须设置合理的结束条件避免死递归。避免使用深层次的递归每一层递归都会占用栈空间层级过深会导致栈溢出。2.4 经典示例递归实现斐波那契数列斐波那契数列规则第 1、2 项为 1从第 3 项开始每一项等于前两项之和1 1 2 3 5 8 13 21 ...递归公式n1 或 n2 时返回 1n2 时返回fun(n-1) fun(n-2)代码实现#include stdio.h int fib(int n) { // 递归出口 if (n 1 || n 2) { return 1; } // 递归递推 return fib(n - 1) fib(n - 2); } int main() { int n 6; printf(第%d项斐波那契数%d\n, n, fib(n)); // 输出8 return 0; }3. 预处理命令C 语言程序在正式编译之前会先执行预处理操作。所有预处理命令都以#开头不属于 C 语句不占运行时间仅在编译阶段生效。 主要包含三类宏定义、条件编译、头文件包含。3.1 宏定义宏定义的核心是纯文本替换预处理阶段会把代码中所有的宏名替换成对应的文本替换过程不做任何计算、不做类型检查。 约定俗成宏名全部使用大写与普通变量名做区分。3.1.1 不带参宏语法#define 宏名 替换文本作用提高代码可读性用有意义的名字替代魔法数字。统一管理常量参数修改一处、全局生效实现快速调整。可作为功能开关控制代码是否生效。示例#define MAX_SIZE 100 // 数组最大长度 #define PI 3.14159 // 圆周率3.1.2 带参宏宏函数带参宏看起来像函数但本质依然是文本替换没有参数类型、返回值的概念。语法#define 宏名(参数列表) 替换文本注意事项由于是纯文本替换容易出现运算符优先级问题建议给参数和整体都加上括号。错误示例与正确示例// 错误写法优先级问题 #define SQUARE(x) x * x // SQUARE(32) 会被替换成 32 * 32 362 11不是预期的25 // 正确写法参数加括号整体加括号 #define SQUARE(x) ((x) * (x))3.1.3 带参宏与函数的区别对比维度带参宏函数本质纯文本替换编译期处理完整的函数调用运行期执行类型检查无参数类型、返回值类型不做类型检查有严格的参数类型、返回值类型会做类型检查执行效率高无传参、栈帧开辟等开销相对低有函数调用的额外开销代码体积每调用一次就展开一次会增大代码体积只有一份函数代码不增加代码体积适用场景封装短小、高频的简单运算逻辑复杂、代码量较大的功能模块3.2 条件编译条件编译的作用是满足条件时对应代码才参与编译不满足条件时代码直接从编译结果中移除。 常用于跨平台适配、调试开关、功能裁剪等场景。3.2.1 #if / #elif / #else / #endif根据常量表达式的值选择编译哪一段代码。语法格式#if 常量表达式1 代码块1 #elif 常量表达式2 代码块2 #else 代码块3 #endif示例#define VERSION 2 int main() { #if VERSION 1 printf(使用版本1功能\n); #elif VERSION 2 printf(使用版本2功能\n); #else printf(使用默认功能\n); #endif return 0; }3.2.2 #ifdef / #endif判断某个宏是否已经被定义如果已定义则编译中间的代码。语法#ifdef 宏名 代码段 #endif3.2.3 #ifndef / #endif判断某个宏是否没有被定义如果未定义则编译中间的代码。 最常用的场景是头文件防重复包含。语法#ifndef 宏名 代码段 #endif3.3 头文件包含核心作用预处理时找到指定的头文件并将头文件的全部内容展开、插入到当前文件中。头文件通常存放的内容构造数据类型的定义结构体、枚举等全局变量、全局函数的声明宏定义两种包含方式的区别#include 文件名查找路径直接在系统指定的标准库目录如/usr/include下查找头文件。适用场景包含系统标准头文件如stdio.h、stdlib.h。#include 文件名查找路径先在当前源文件所在目录下查找找不到再去系统标准库目录查找。适用场景包含自定义的头文件。今日知识点总结函数传参值传递是拷贝副本形参修改不影响实参只能读取、不能修改外部变量。全局变量可跨函数共享数据局部重名时优先使用局部变量不建议滥用。数组传递本质是传递首地址函数内修改会影响外部数组整型数组建议传数组名 长度字符串可依靠\0判断结束。递归函数核心是函数调用自身必须同时具备结束条件和递推公式。斐波那契数列是递归经典案例深层递归存在栈溢出风险。预处理命令宏定义是纯文本替换不做计算、不检查类型带参宏建议给参数和整体加括号避免优先级问题。带参宏执行效率高但会增大代码体积适合短小逻辑函数适合复杂功能逻辑。条件编译可按需编译代码#ifndef最常用于头文件防重复包含。用于包含系统头文件用于包含自定义头文件二者查找路径不同。