news 2026/7/31 5:03:58

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【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 / #endif
      • 3.2.2 #ifdef / #endif
      • 3.2.3 #ifndef / #endif
    • 3.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_context

2.读入数据

代码如下(示例):

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 递归的两个必要条件

  1. 递归出口(结束条件):必须有明确的终止条件,否则会无限递归,最终导致栈溢出、程序崩溃。
  2. 递归递推公式:问题可以拆解为更小的同类子问题,逐步向结束条件靠近。

2.3 注意事项

  • 必须设置合理的结束条件,避免死递归。
  • 避免使用深层次的递归,每一层递归都会占用栈空间,层级过深会导致栈溢出。

2.4 经典示例:递归实现斐波那契数列

斐波那契数列规则:第 1、2 项为 1,从第 3 项开始,每一项等于前两项之和:1 1 2 3 5 8 13 21 ...

递归公式:

  • n=1 或 n=2 时,返回 1
  • n>2 时,返回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 宏名 替换文本

作用:

  1. 提高代码可读性,用有意义的名字替代魔法数字。
  2. 统一管理常量参数,修改一处、全局生效,实现快速调整。
  3. 可作为功能开关,控制代码是否生效。

示例:

#define MAX_SIZE 100 // 数组最大长度 #define PI 3.14159 // 圆周率
3.1.2 带参宏(宏函数)

带参宏看起来像函数,但本质依然是文本替换,没有参数类型、返回值的概念。语法:

#define 宏名(参数列表) 替换文本

注意事项:由于是纯文本替换,容易出现运算符优先级问题,建议给参数和整体都加上括号。

错误示例与正确示例:

// 错误写法:优先级问题 #define SQUARE(x) x * x // SQUARE(3+2) 会被替换成 3+2 * 3+2 = 3+6+2 = 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 宏名 代码段 #endif
3.2.3 #ifndef / #endif

判断某个宏是否没有被定义,如果未定义,则编译中间的代码。 最常用的场景是头文件防重复包含语法:

#ifndef 宏名 代码段 #endif

3.3 头文件包含

核心作用

预处理时找到指定的头文件,并将头文件的全部内容展开、插入到当前文件中。

头文件通常存放的内容
  • 构造数据类型的定义(结构体、枚举等)
  • 全局变量、全局函数的声明
  • 宏定义
两种包含方式的区别
  1. #include <文件名>

    • 查找路径:直接在系统指定的标准库目录(如/usr/include)下查找头文件。
    • 适用场景:包含系统标准头文件,如stdio.hstdlib.h
  2. #include "文件名"

    • 查找路径:先在当前源文件所在目录下查找,找不到再去系统标准库目录查找。
    • 适用场景:包含自定义的头文件。

今日知识点总结

  1. 函数传参

    • 值传递是拷贝副本,形参修改不影响实参,只能读取、不能修改外部变量。
    • 全局变量可跨函数共享数据,局部重名时优先使用局部变量,不建议滥用。
    • 数组传递本质是传递首地址,函数内修改会影响外部数组;整型数组建议传数组名 + 长度,字符串可依靠\0判断结束。
  2. 递归函数

    • 核心是函数调用自身,必须同时具备结束条件和递推公式。
    • 斐波那契数列是递归经典案例,深层递归存在栈溢出风险。
  3. 预处理命令

    • 宏定义是纯文本替换,不做计算、不检查类型;带参宏建议给参数和整体加括号,避免优先级问题。
    • 带参宏执行效率高但会增大代码体积,适合短小逻辑;函数适合复杂功能逻辑。
    • 条件编译可按需编译代码,#ifndef最常用于头文件防重复包含。
    • <>用于包含系统头文件,""用于包含自定义头文件,二者查找路径不同。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/31 5:02:56

锁相环(PLL)原理深度解析:从基础模块到工程实践

1. 从“对不上拍子”到“精准同步”&#xff1a;一个工程师眼中的锁相环在数字电路、通信系统乃至我们日常用的收音机、手机里&#xff0c;有一个默默无闻但至关重要的“节奏大师”——锁相环。我第一次深刻理解它的重要性&#xff0c;是在调试一个高速串行通信接口时。发送端和…

作者头像 李华
网站建设 2026/7/31 5:01:14

DDD 第三天实战:交叉验证、决策树与样本平衡全攻略

在处理分类问题时&#xff0c;你是否遇到过模型在训练集上表现完美&#xff0c;一到测试集就“崩盘”的情况&#xff1f;或者面对一份数据&#xff0c;其中某一类样本寥寥无几&#xff0c;导致模型直接“忽略”了少数类&#xff0c;只预测多数类&#xff1f;这往往是数据失衡惹…

作者头像 李华
网站建设 2026/7/31 4:58:03

Type-C接口引脚全解析:从6P到24P,如何选择与避坑

1. 从“一根线”到“一个生态”&#xff1a;TYPE-C接口的物理层演进如果你最近几年买过新手机、新笔记本&#xff0c;甚至是一些小家电&#xff0c;大概率会接触到那个正反都能插、看起来都一样的接口——USB Type-C。但你可能不知道&#xff0c;这个小小的接口&#xff0c;内部…

作者头像 李华
网站建设 2026/7/31 4:56:59

NVIDIA Quadro显卡全史:从FX到RTX,专业图形工作站20年演进与选购指南

1. 项目概述&#xff1a;一份面向专业用户的“显卡族谱”如果你是一名从事三维动画、工业设计、科学计算或者视频后期工作的专业人士&#xff0c;那么“Quadro”这个名字对你来说一定不陌生。它不像GeForce那样家喻户晓&#xff0c;但在专业图形工作站领域&#xff0c;它曾是绝…

作者头像 李华
网站建设 2026/7/31 4:56:33

龍魂·数字根计算引擎 v1.0

这是专门为龙魂系统定制的数字根计算引擎&#xff0c;CNSH五行计算器和流场压缩核的底层组件。 #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 龍魂数字根计算引擎 v1.0 DNA: #龍芯⚡️丙午乙未甲辰离为火-数字根引擎-v1.0 CONFIRM: #CONFIRM&#x1f30…

作者头像 李华
网站建设 2026/7/31 4:56:21

华为nova16系列全景声技术解析:如何做到手机也能听出影院感

作为常年深耕华为机型的花粉&#xff0c;最近才关注到nova16系列一个极易被忽略的影音黑科技——移动全景声。很多人入手nova系列却不知道这套声学方案&#xff0c;能直接重塑日常观影听歌体验。日常用手机追剧&#xff0c;声音集中在底部扬声器&#xff0c;缺少空间层次感&…

作者头像 李华