【C语言】连接陷阱探秘(3):形参、实参与返回值

news/2024/11/24 5:18:38/

目录

一、形参的 “迷障”

1.1. 定义与功能

1.2. 类型不匹配

1.3. 数量不一致

1.4. 顺序不一致

1.5. 数组形参退化

二、实参的 “暗礁”

2.1. 定义与功能

2.2. 求值顺序 “谜题”

2.3. 悬空指针 “深渊”

三、返回值的 “陷阱”

3.1. 定义与功能

3.2. 陷阱与缺陷

3.2.1. 未定义返回值

3.2.2. 类型不匹配

3.2.3. 返回局部变量的地址

3.2.4. 函数返回值覆盖问题

3.2.5. 返回动态分配的内存

3.3. 正确的做法

四、破局之策


在 C 语言的编程世界里,函数是构建复杂程序逻辑的基石,而形参、实参与返回值作为函数机制中的关键要素,它们之间的交互在程序连接过程中暗藏诸多容易被忽视的陷阱。正确理解并避开这些陷阱,对于编写稳定、可靠的 C 语言程序至关重要。

一、形参的 “迷障”

1.1. 定义与功能

形参(形式参数):在函数定义中列出的参数,它们作为占位符,用于在函数调用时接收从调用者传递过来的值。形参在函数被调用时分配内存空间,并在函数调用结束后释放。形参的主要作用是提供函数内部对外部传入数据的访问。

1.2. 类型不匹配

形参在函数定义之初便确定了其数据类型,旨在精准匹配对应传入的值。然而,现实编程中常出现实参与形参类型不一致的状况。C 语言编译器有时会自动施展隐式类型转换 “魔法”,可这魔法未必带来理想结局。

intfloat为例,若有函数void print_number(float num)期望接收浮点数形参,调用时却传入int型变量age = 25(即print_number(age)),编译器会将age隐式转换为float,看似顺利衔接,实则丢失了int作为整数的精度特性。反之,从高精度向低精度转换,像把double型数据传入float形参函数,截断小数部分更是悄无声息改变了数据原貌,在对数据精度要求严苛场景(如金融计算、科学模拟),细微偏差足以酿成大错。

以下是一个具体的C语言示例,展示了从intfloat以及从doublefloat的隐式类型转换可能带来的问题。

#include <stdio.h>// 函数期望接收一个float类型的参数
void print_number(float num) {printf("The number is: %f\n", num);
}int main() {int age = 25;                  // 定义一个int类型的变量double salary = 12345.6789;     // 定义一个double类型的变量// 调用函数,传入int类型的变量,编译器会进行隐式类型转换print_number(age);             // 输出: The number is: 25.000000// 虽然这里看起来没有问题,因为25可以精确表示为浮点数25.0,// 但如果考虑更大的整数或负数,且后续在浮点数运算中可能需要更高精度,// 这种隐式转换就可能隐藏问题。// 调用函数,传入double类型的变量,编译器同样会进行隐式类型转换// 但这次是从double到float,会导致精度丢失print_number((float)salary);   // 输出可能是: The number is: 12345.678125// 注意,这里的输出值可能因为浮点数的表示方式而略有不同,// 但关键是原始double值的小数部分被截断了。// 为了更清楚地看到精度丢失,我们可以直接打印转换后的float值float truncated_salary = (float)salary;printf("Truncated salary: %f\n", truncated_salary); // 输出与上面相同或相近// 如果我们关心精度,应该避免这种隐式转换,或者至少应该意识到它的发生。// 一种方法是使用更精确的类型(如double)来定义函数参数,或者确保在转换前了解可能的精度损失。return 0;
}

在这个示例中:

  • print_number(age)被调用时,age(一个int类型的变量)被隐式转换为float类型,并传递给函数。虽然在这个特定的例子中,转换是精确的(因为25可以精确地表示为浮点数25.0),但在其他情况下,特别是当整数很大或包含负数,并且后续操作需要更高的精度时,这种隐式转换可能会隐藏问题。

  • print_number((float)salary)被调用时,salary(一个double类型的变量)被显式(虽然这里用括号写出了转换,但重点在于理解隐式转换也会发生)转换为float类型。这个转换导致了精度丢失,因为double类型通常比float类型能表示更多的小数位数。

应对策略

  • 编写函数调用时,务必仔细核对实参和形参的类型,确保二者严格匹配。
  • 如果确实需要进行类型转换,尽量使用显式类型转换,让代码意图更加清晰,便于排查错误。
  • 例如,若要把 int 类型的变量当作 double 类型传递给函数,可以写成 print_double_value((double)integer_value),这样能明确体现出是有意进行类型转换的操作。

1.3. 数量不一致

如果在函数调用时提供的实参数量少于函数定义中要求的形参数量,未传递的形参将不会被初始化,其值将是未定义的。这可能导致函数内部使用这些未初始化参数时产生不可预测的结果。

如果提供的实参数量多于函数定义中要求的形参数量,额外的实参将被忽略,但这通常不会导致编译错误。然而,这种不一致性可能会使代码难以理解和维护。

示例代码

#include <stdio.h>// 函数定义有两个形参
void add_numbers(int num1, int num2) {printf("The sum is: %d\n", num1 + num2);
}int main() {int a = 3;// 只传递了一个实参,与函数定义的形参数量不符add_numbers(a); return 0;
}

在 add_numbers 函数定义中明确要求有两个 int 类型的形参,用于接收两个数并求和输出。但在 main 函数调用该函数时,只传递了一个实参 a。对于某些宽松的编译器,可能不会直接阻止编译,但函数内部在尝试获取第二个参数时,就会读取到不确定的内存数据,最终导致求和结果完全错误,甚至程序可能因为访问了非法内存而崩溃。

应对策略:仔细检查函数调用语句,保证实参的个数与函数定义的形参个数完全一致。同时,可以通过良好的代码风格,比如在函数声明和定义处详细注释参数的含义及个数要求,方便在编写调用代码时进行核对࿰


http://www.ppmy.cn/news/1549455.html

相关文章

tcpdump抓包 wireShark

TCPdump抓包工具介绍 TCPdump&#xff0c;全称dump the traffic on anetwork&#xff0c;是一个运行在linux平台可以根据使用者需求对网络上传输的数据包进行捕获的抓包工具。 tcpdump可以支持的功能: 1、在Linux平台将网络中传输的数据包全部捕获过来进行分析 2、支持网络层…

移远通信推出全新5G RedCap模组RG255AA系列,以更高性价比加速5G轻量化大规模商用

11月20&#xff0c;全球领先的物联网整体解决方案供应商移远通信宣布&#xff0c;正式推出其全新5G RedCap模组RG255AA系列。该系列模组支持5G NR独立组网&#xff08;SA&#xff09;和LTE Cat 4双模通信&#xff0c;具有高性能高集成度、低功耗、小尺寸、高性价比等优势&#…

从ES的JVM配置起步思考JVM常见参数优化

目录 一、真实查看参数 &#xff08;一&#xff09;-XX:PrintCommandLineFlags &#xff08;二&#xff09;-XX:PrintFlagsFinal 二、堆空间的配置 &#xff08;一&#xff09;默认配置 &#xff08;二&#xff09;配置Elasticsearch堆内存时&#xff0c;将初始大小设置为…

大语言模型---通过数值梯度的方式计算损失值L对模型权重矩阵W的梯度;数值梯度的公式;数值梯度计算过程

文章目录 概要1. 数值梯度的公式2. 数值梯度计算过程3. 数值梯度的特点 概要 前文已经简单介绍梯度&#xff0c;本文主要介绍大语言模型中使用数值梯度的方法实现 损失值 L L L 对模型权重矩阵的梯度计算&#xff0c;而不是传统的链式法则进行梯度计算。如果想要理解整体计算方…

Springboot之登录模块探索(含Token,验证码,网络安全等知识)

简介 登录模块很简单&#xff0c;前端发送账号密码的表单&#xff0c;后端接收验证后即可~ 淦&#xff01;可是我想多了&#xff0c;于是有了以下几个问题&#xff08;里面还包含网络安全问题&#xff09;&#xff1a; 1.登录时的验证码 2.自动登录的实现 3.怎么维护前后端…

Linux第95步_Linux内核中的INPUT子系统

Linux内核对“按键、鼠标、键盘、触摸屏”等这一类输入设备而创建的“框架”被称为“input子系统”&#xff0c;它们在本质上还是字符设备&#xff0c;只是采用input子系统处理输入事件&#xff0c;并上报给用户。前面讲的“pinctrl子系统和gpio子系统”主要用于GPIO驱动开发&a…

《Python制作动态爱心粒子特效》

一、实现思路 粒子效果&#xff1a; – 使用Pygame模拟粒子运动&#xff0c;粒子会以爱心的轨迹分布并运动。爱心公式&#xff1a; 爱心的数学公式&#xff1a; x16sin 3 (t),y13cos(t)−5cos(2t)−2cos(3t)−cos(4t) 参数 t t 的范围决定爱心形状。 动态效果&#xff1a; 粒子…

2024/11/18学习日志

为了更好地记录并反思自己的学习状况&#xff0c;将每日学习的内容、时长、心得等记录于此日志。 于9月3日开始记录&#xff0c;计划每日记录&#xff0c;希望至少能够坚持一个学期。 学习内容&#xff1a; 大物&#xff1a; 尺缩效应m的变化相对论框架下的能量 计数&#…