C语言的形参和实参个数不一致问题

本文探讨了在C语言中实参与形参个数不一致时可能产生的问题及原因。介绍了编译器如何在存在前置声明的情况下进行参数检查,并讨论了在缺少前置声明时可能引发的编译与运行时错误。

最近多个分支往基线合并,不同分支上基于不同平台的硬件抽象层提供给应用层的接口有变化,函数参数个数的变化是其中一个大类。

这带来的一个问题,实参和形参个数不一致,能编译通过,且有些情况没有警告,并且能链接通过,但是运行时会产生严重的运行时错误。

事实上,实参和形参个数不一致的问题,编译器是做了检查的,虽然这个检查机制不太完美。

在存在前置声明的情况下,编译器是会做实参和形参个数的检查的,建立两个文件如下图所示:


编译器会报错如下:


现在,让我们注掉对foo函数的前置声明,如下:


编译可以通过


然后,将注掉的代码打开,但是将去掉一个形参,入校所示:


同样可以编译通过。

通过以上的demo,可以推测出编译器只有在能拿到前置声明的时候才会同时也是才有办法进行参数检查,事实也确实如此。

因为在编译期,如果没有前置声明,编译器根本就不知道函数原型是什么,更别提什么检查。

正常来说编译器不检查,那么链接器总该检查吧,但是偏偏C语言对函数符号的判断以函数名作为符号的识别依据(C语言不支持重载)。

对于linker来说,在遍历全局符号表的时候,找到一个同名函数,就会将调用位置重定位到相应位置。

因为在开发工具上没有很好的对此类潜在问题有很好的检查机制,所以在使用C语言的时候,这个地方还是需要特别注意的。


### C语言形参实参的概念及区别 #### 一、形参实参的定义 在C语言中,函数调用涉及两个重要概念——形参(形式参数)实参(实际参数)。 - **形参**是指在函数声明或定义使用的变量名,用于接收外部传入的数据。这些变量仅存在于函数内部,在函数执行完毕后会被销毁[^1]。 - **实参**是在调用函数传递给函数的具体数据值或表达式。它通常是一个已知的变量或者常量。 例如: ```c #include <stdio.h> void func(int x) { printf("Value inside function: %d\n", x); } int main() { int a = 5; func(a); // 调用func函数并传递a作为实参 return 0; } ``` 在此代码片段中,`x` 是形参,而 `a` 则是实参[^2]。 --- #### 二、形参实参的类型及其内存占用 C语言支持多种类型的参数传递,包括但限于基本数据类型、指针类型以及结构体类型等。每种类型都有其特定的字节大小用途: 1. **基本数据类型** 基本数据类型如 `int`, `float`, `char` 等具有固定的存储空间需求。例如,`int` 类型一般占据 4 字节(具体取决于编译器环境),而 `char` 占据 1 字节[^1]。 2. **指针类型** 指针是一种特殊的变量,用来保存另一个变量的地址。无论指针指向何种类型的数据,它的本身大小通常是相同的(比如在大多数现代架构下为 8 字节)。通过指针可以实现对原始数据的操作[^3]。 3. **结构体类型** 结构体允许将同类型的数据组合在一起形成复合数据类型。当作为一个整体传递,整个结构体会被复制到对应的形参位置上[^1]。 --- #### 三、形参实参的传递方式 C语言提供了两种主要的参数传递方法:**值传递** **引用传递**。 1. **值传递** 当采用值传递的方式,会将实参的内容拷贝一份副本交给形参使用。这意味着即使修改了形参会影响原实参的值[^3]。 示例: ```c #include <stdio.h> void modifyValue(int num) { num *= 2; // 修改的是num的一个本地副本 printf("Inside Function: %d\n", num); } int main() { int value = 10; modifyValue(value); printf("After Call: %d\n", value); // 输出仍为原来的value return 0; } ``` 2. **引用传递** 如果希望函数能够直接影响实参,则可以通过指针来间接访问该对象。此传递的是指向实参的指针而非其实质内容。 示例: ```c #include <stdio.h> void modifyByPointer(int* ptrNum) { (*ptrNum) *= 2; // 解引用操作符(*)作用于ptrNum所指向的位置 printf("Modified Inside Func via Pointer: %d\n", *ptrNum); } int main() { int number = 7; modifyByPointer(&number); // 将address-of(number)发送过去 printf("Final Value After Modification Using Pointers: %d\n", number); return 0; } ``` --- #### 四、总结 综上所述,理解区分好形参实参对于编写高效可靠的程序至关重要。需要注意的关键点有以下几个方面: - 形参只在其所属的作用域范围内有效; - 实参决定了最终如何初始化相应的形参; - 同情况下可以选择同的传递策略以满足功能需求。 ---
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值