当前位置: 首页 > news >正文

【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最常用于头文件防重复包含。
    • <>用于包含系统头文件,""用于包含自定义头文件,二者查找路径不同。

http://www.cnnetsun.cn/news/3760388.html

相关文章:

  • 锁相环(PLL)原理深度解析:从基础模块到工程实践
  • DDD 第三天实战:交叉验证、决策树与样本平衡全攻略
  • Type-C接口引脚全解析:从6P到24P,如何选择与避坑
  • NVIDIA Quadro显卡全史:从FX到RTX,专业图形工作站20年演进与选购指南
  • 龍魂·数字根计算引擎 v1.0
  • 华为nova16系列全景声技术解析:如何做到手机也能听出影院感
  • 【Milvus安装】Windows安装Milvus向量数据库保姆级教程|一步一图从零搭建
  • 稳压二极管限流电阻选型详解:从公式到实战避坑指南
  • Mem Reduct深度解析:轻量级内存管理工具的核心机制与实战优化指南
  • RC并联电路实战指南:从滤波到定时,硬件工程师必懂的核心模块
  • 线上采购越来越多!企优托李跃跃解析苏州工厂为何必做百度爱采购
  • 树形DP核心解析:从AcWing 285看状态转移与C++实现
  • 后端接口挂了,前端怎么继续干活?这个 Chrome 扩展让我再也没等过后端
  • Transformer架构解析:从原理到AI实践应用
  • 2、BellMan-Ford算法
  • 濮阳工厂目视化设计5S管理落地完整方案
  • Android面试核心:Handler、RecyclerView与内存泄漏实战解析
  • C++ string类完全指南:从基础使用到底层优化与性能陷阱
  • macbook能玩steam里面的哪些游戏
  • AI搜索中的GEO优化技术:提升转化率的关键
  • ADB命令详解:Android音量控制原理与自动化脚本实践
  • C/C++数组与指针深度解析:从内存模型到多维访问实战
  • Python排序文件按时间?这招绝了,别再傻傻手动翻
  • 数组指针---指向数组的指针
  • 从零部署网站:Nginx手动配置与宝塔面板可视化部署全攻略
  • C++字符编码终极指南:从乱码根源到UTF-8最佳实践
  • UnityHub安装Android模块失败?从网络到环境冲突的完整排查与修复指南
  • 小马宝莉辉月11拆卡攻略:从真伪鉴别到收藏管理的完整指南
  • Unity iOS打包全流程排障指南:从证书配置到上架避坑
  • 基于SpringBoot+Vue的社区医院管理系统设计与实现