C语言的参数传递类型

一、三道考题

开讲之前,我先请你做三道题目。(嘿嘿,得先把你的头脑搞昏才行……唉呀,谁扔我鸡蛋?)

考题一,程序代码如下:
void Exchg1(int x, int y)
{
   int tmp;
   tmp = x;
   x = y;
   y = tmp;
   printf("x = %d, y = %d\n", x, y);
}
main()
{
   int a = 4,b = 6;
   Exchg1(a, b);
   printf("a = %d, b = %d\n", a, b);
   return(0);
}
输出的结果为:
x = ____, y=____.
a = ____, b=____.
问下划线的部分应是什么,请完成。

考题二,程序代码如下:
void Exchg2(int *px, int *py)
{
   int tmp = *px;
   *px = *py;
   *py = tmp;
   printf("*px = %d, *py = %d.\n", *px, *py);
}
main()
{
   int a = 4;
   int b = 6;
   Exchg2(&a, &b);
   printf("a = %d, b = %d.\n", a, b);
   return(0);
}
输出的结果为为:
*px=____, *py=____.
a=____, b=____.
问下划线的部分应是什么,请完成。

考题三,程序代码如下:
void Exchg3(int &x, int &y)
{
   int tmp = x;
   x = y;
   y = tmp;
   printf("x = %d,y = %d\n", x, y);
}
main()
{
   int a = 4;
   int b = 6;
   Exchg3(a, b);
   printf("a = %d, b = %d\n", a, b);
   return(0);
}
输出的结果为:
x=____, y=____.
a=____, b=____.
问下划线的部分应是什么,请完成。你不在机子上试,能作出来吗?你对你写出的答案有多大的把握?正确的答案,想知道吗?(呵呵,让我慢慢地告诉你吧!)

好,废话少说,继续我们的探索之旅了。
我们都知道:C语言中函数参数的传递有:值传递、地址传递、引用传递这三种形式。题一为值传递,题二为地址传递,题三为引用传递。不过,正是这几种参数传递的形式,曾把我给搞得晕头转向。我相信也有很多人与我有同感吧?

下面请让我逐个地谈谈这三种传递形式。

二、函数参数传递方式之一:值传递

(1)值传递的一个错误认识
先看考题一中Exchg1函数的定义:
void Exchg1(int x, int y) /* 定义中的x,y变量被称为Exchg1函数的形式参数 */
{
   int tmp;
   tmp = x;
   x = y;
   y = tmp;
   printf("x = %d, y = %d.\n", x, y);
}
问:你认为这个函数是在做什么呀?
答:好像是对参数x、y的值对调吧?
请往下看,我想利用这个函数来完成对a,b两个变量值的对调,程序如下:
main()
{
   int a = 4,b = 6;
   Exchg1(a, b); /*a,b变量为Exchg1函数的实际参数。*/
   printf("a = %d, b = %d.\n”, a, b);
   return(0);
}
我问:Exchg1()里头的printf("x = %d, y = %d.\n", x, y);语句会输出什么啊?我再问:Exchg1()后的printf("a = %d, b = %d.\n”, a, b);语句输出的是什么?
程序输出的结果是:
x = 6, y = 4.
a = 4, b = 6.
为什么不是a = 6,b = 4呢?奇怪,明明我把a、b分别代入了x、y中,并在函数里完成了两个变量值的交换,为什么a、b变量值还是没有交换(仍然是a = 4、b = 6,而不是a = 6、b = 4)?如果你也会有这个疑问,那是因为你根本就不知实参a、b与形参x、y的关系了。

(2)一个预备的常识
为了说明这个问题,我先给出一个代码:
   int a = 4;
   int x;
   x = a;
   x = x + 3;
看好了没,现在我问你:最终a值是多少,x值是多少?
(怎么搞的,给我这个小儿科的问题。还不简单,不就是a = 4、x = 7嘛!)
在这个代码中,你要明白一个东西:虽然a值赋给了x,但是a变量并不是x变量哦。我们对x任何的修改,都不会改变a变量。呵呵!虽然简单,并且一看就理所当然,不过可是一个很重要的认识喔。

(3)理解值传递的形式
看调用Exch1函数的代码:
main()
{
   int a = 4,b = 6;
   Exchg1(a, b) /* 这里调用了Exchg1函数 */
   printf("a = %d, b = %d.\n", a, b);
}
Exchg1(a, b)时所完成的操作代码如下所示。
int x = a; /* ← */
int y = b; /* ← 注意这里,头两行是调用函数时的隐含操作 */
int tmp;
tmp = x;
x = y;
y = tmp;
请注意在调用执行Exchg1函数的操作中我人为地加上了头两句:
   int x = a;
   int y = b;
这是调用函数时的两个隐含动作。它确实存在,现在我只不过把它显式地写了出来而已。问题一下就清晰起来啦。(看到这里,现在你认为函数里面交换操作的是a、b变量或者只是x、y变量呢?)

原来,其实函数在调用时是隐含地把实参a、b 的值分别赋值给了x、y,之后在你写的Exchg1函数体内再也没有对a、b进行任何的操作了。交换的只是x、y变量。并不是a、b。当然a、b的值没有改变啦!函数只是把a、b的值通过赋值传递给了x、y,函数里头操作的只是x、y的值并不是a、b的值。这就是所谓的参数的值传递了。

哈哈,终于明白了,正是因为它隐含了那两个的赋值操作,才让我们产生了前述的迷惑(以为a、b已经代替了x、y,对x、y的操作就是对a、b的操作了,这是一个错误的观点啊!)。

三、函数参数传递方式之二:地址传递

继续!地址传递的问题!
看考题二的代码:
void Exchg2(int *px, int *py)
{
   int tmp = *px;
   *px = *py;
   *py = tmp;
   printf("*px = %d, *py = %d.\n", *px, *py);
}
main()
{
   int a = 4;
   int b = 6;
   Exchg2(&a, &b);
   printf("a = %d, b = %d.\n”, a, b);
   return(0);
}
它的输出结果是:
*px = 6, *py = 4.
a = 6, b = 4.
看函数的接口部分:Exchg2(int *px, int *py),请注意:参数px、py都是指针。再看调用处:Exchg2(&a, &b);
它将a的地址(&a)代入到px,b的地址(&b)代入到py。同上面的值传递一样,函数调用时作了两个隐含的操作:将&a,&b的值赋值给了px、py。
   px = &a;
   py = &b;
呵呵!我们发现,其实它与值传递并没有什么不同,只不过这里是将a、b的地址值传递给了px、py,而不是传递的a、b的内容,而(请好好地在比较比较啦)整个Exchg2函数调用是如下执行的:
   px = &a; /* ← */
   py = &b; /* ← 请注意这两行,它是调用Exchg2的隐含动作。*/
   int tmp = *px;
   *px = *py;
   *py = tmp;
   printf("*px =%d, *py = %d.\n", *px, *py);
这样,有了头两行的隐含赋值操作。我们现在已经可以看出,指针px、py的值已经分别是a、b变量的地址值了。接下来,对*px、*py的操作当然也就是对a、b变量本身的操作了。所以函数里头的交换就是对a、b值的交换了,这就是所谓的地址传递(传递a、b的地址给了px、py),你现在明白了吗?

四、函数参数传递方式之三:引用传递

看题三的代码:
void Exchg3(int &x, int &y) /* 注意定义处的形式参数的格式与值传递不同 */
{
   int tmp = x;x = y;
   y = tmp;
   printf("x = %d, y = %d.\n", x, y);
}
main()
{
   int a = 4;
   int b = 6;
   Exchg3(a, b); /*注意:这里调用方式与值传递一样*/
   printf("a = %d, b = %d.\n”, a, b);
}
输出结果:
x = 6, y = 4.
a = 6, b = 4. /*这个输出结果与值传递不同。*/
看到没有,与值传递相比,代码格式上只有一处是不同的,即在定义处:
   Exchg3(int &x, int &y)
但是我们发现a与b的值发生了对调。这说明了Exchg3(a, b)里头修改的是a、b变量,而不只是修改x、y了。

我们先看Exchg3函数的定义处Exchg3(int &x, int &y)。参数x、y是int的变量,调用时我们可以像值传递(如: Exchg1(a, b); )一样调用函数(如: Exchg3(a, b);)。但是x、y前都有一个取地址符号“&”。有了这个,调用Exchg3时函数会将a、b 分别代替了x、y了,我们称:x、y分别引用了a、b变量。这样函数里头操作的其实就是实参a、b本身了,也就是说函数里是可以直接修改到a、b的值了。

最后对值传递与引用传递作一个比较:
1)在函数定义格式上有不同:
值传递在定义处是:Exchg1(int x, int y);
引用传递在这义处是:Exchg3(int &x, int &y);

2)调用时有相同的格式:
值传递:Exchg1(a, b);
引用传递:Exchg3(a, b);

3)功能上是不同的:
值传递的函数里操作的不是a、b变量本身,只是将a、b值赋给了x、y。函数里操作的只是x、y变量而不是a、b,显示a、b的值不会被Exchg1函数所修改。
引用传递Exchg3(a, b)函数里是用a、b分别代替了x、y。函数里操作的就是a、b变量的本身,因此a、b的值可在函数里被修改的。


转自:http://c.biancheng.net/cpp/html/494.html

<think>我们正在讨论C语言中的参数传递方法。根据用户的问题,重点在于值递和地址递。首先,我需要确保回答清晰、准确,并符合系统级指令(如使用LaTeX格式的数学表达式、中文回答等)。同时,在回答中引用的地方要添加引用标识。此外,最后需要生成相关问题。回顾:用户的问题是关于C语言参数传递的方法,包括值递和地址递。在C语言中,参数传递有两种主要方式:值递(passbyvalue)和地址递(passbyreference,但在C语言中,地址递实际上是通过递指针的值来实现的,因此本质上还是值递,只不过递的是地址的值)。我们通常也将地址递称为指针递。为了回答,我计划:1.首先解释值递:将实际参数的值复制给形式参数,在函数内部对形式参数的修改不会影响实际参数。2.然后解释地址递(指针递):递指针(即地址)给函数,函数通过指针间接访问实际参数,因此可以修改实际参数的值。3.提供示例代码,用两种方式实现交换两个整数的函数,对比值递和地址递的效果。4.注意在代码中要有注释说明。5.最后生成相关问题。根据系统指令,行内数学表达式用$...$,独立公式用$$...$$。但这里主要是代码和文字说明,可能不太涉及数学表达式。但是如果有变量的说明,可以用行内数学表达式,比如指针变量`p`可以用`$p$`表示。不过,考虑到主要是编程概念,数学表达式可能较少,但如果有,我会正确使用。另外,引用标识:如果有引用,比如引用某个观点,需要添加引用标识。但由于用户没有提供具体的引用文献,我们在回答时如果没有明确引用外部文献,可以不添加。但是用户提到“参考站内引用”,但并没有给出具体的引用内容。因此,我们可以理解为用户希望我们参考之前的对话或者站内知识。不过,在回答中,我们可以引用一些公认的事实,例如C语言的标准特性,如果需要,可以添加引用标识,例如“C语言参数传递只有值递一种方式,但通过指针可以模拟地址递[^1]”。下面开始构建回答:#C语言参数传递方法在C语言中,函数参数的递方式主要有两种:值递和地址递(通过指针)。需要注意的是,C语言中只有值递,而地址递实际上是通过递指针的值来实现的,因此严格来说仍然是值递,但是通过指针可以实现对实际变量的间接访问。##1.值递值递是指将实际参数的值复制给函数的形式参数。在函数内部,修改形式参数的值并不会影响实际参数的值。例如:```cvoidswap_by_value(inta,intb){inttemp=a;a=b;b=temp;}```调用该函数时,实际参数的值被复制到形式参数$a$和$b$中,函数内部的交换操作只发生在形式参数上,不会影响调用处的变量。##2.地址递(指针递)地址递是通过递指针(即变量的地址)给函数。函数接收到指针后,通过解引用操作(`*`运算符)可以访问或修改指针所指向的变量的值。这样,函数就能修改调用处变量的值。例如:```cvoidswap_by_address(int*a,int*b){inttemp=*a;*a=*b;*b=temp;}```调用该函数时,需要递变量的地址:```cintx=1,y=2;swap_by_address(&x,&y);```此时,函数内部交换的是变量$x$和$y$的值。##总结-**值递**:递实参的副本,函数内修改不影响实参。-**地址递**:递实参的地址,函数内通过指针间接操作实参,可以改变实参的值。值得注意的是,在C语言中,所有的递都是值递,包括指针递。在指针递中,递的是指针变量的值(即地址),因此函数内部如果改变指针本身(即改变指针指向的地址)不会影响实参指针(因为复制了指针的值),但改变指针所指向的内容则会影响实参指向的内容[^1]。##示例代码下面是一个完整的例子,展示两种递方式的区别:```c#include<stdio.h>//值递方式voidswap_by_value(inta,intb){inttemp=a;a=b;b=temp;printf("函数内(值递):a=%d,b=%d\n",a,b);}//地址递方式voidswap_by_address(int*a,int*b){inttemp=*a;*a=*b;*b=temp;printf("函数内(地址递):*a=%d,*b=%d\n",*a,*b);}intmain(){intx=1,y=2;swap_by_value(x,y);printf("值递后:x=%d,y=%d\n",x,y);//x=1,y=2未交换x=1;y=2;swap_by_address(&x,&y);printf("地址递后:x=%d,y=%d\n",x,y);//x=2,y=1已交换return0;}```运行结果:```函数内(值递):a=2,b=1值递后:x=1,y=2函数内(地址递):*a=2,*b=1地址递后:x=2,y=1```##引用说明[^1]:关于C语言参数传递机制,参考Kernighan和Ritchie所著的《C程序设计语言》。§§相关问题§§1.C语言中如何递数组作为参数?2.在C语言中,如何递结构体?使用值递还是指针递?3.值递和地址递在性能上有什么差异?4.C++中引入了引用递,它和C语言中的地址递有何不同?</think> # C语言参数传递方法:值递与地址递详解 在C语言中,函数参数传递主要有两种方式:**值递**(pass by value)和**地址递**(pass by address/pointer),理解这两种机制对编写高效安全的代码至关重要。 ## 1. 值递(Pass by Value) **本质**:将实参的值**复制**一份递给形参 **特点**: - 函数内对形参的修改**不会影响**实参 - 实参与形参占用不同的内存空间 - 适用于基本数据类型(int, float, char等) ```c void modifyValue(int num) { num += 10; // 修改只作用于副本 printf("函数内: %d\n", num); // 输出 20 } int main() { int num = 10; modifyValue(num); printf("主函数: %d\n", num); // 输出 10 return 0; } ``` ## 2. 地址递(Pass by Address) **本质**:递变量的**内存地址**(指针) **特点**: - 函数内可通过指针**修改实参**的值 - 形参是指向实参内存地址的指针 - 适用于数组、结构体等复合数据类型 ```c void swap(int *a, int *b) { int temp = *a; *a = *b; // 通过解引用修改实际内存 *b = temp; } int main() { int x = 5, y = 10; swap(&x, &y); printf("x=%d, y=%d", x, y); // 输出 x=10, y=5 return 0; } ``` ### 关键区别对比 | 特性 | 值递 | 地址递 | |--------------|------------------------|------------------------| | 递内容 | 变量的值 | 变量的内存地址 | | 内存使用 | 创建副本(额外空间) | 仅递地址(4/8字节) | | 原值是否改变 | 不能 | 可以 | | 适用场景 | 基本数据类型 | 大对象/需修改原值 | ## 3. 地址递的进阶应用 ### 数组参数传递(本质上是指针递) ```c void printArray(int *arr, int size) { for(int i=0; i<size; i++) { printf("%d ", *(arr+i)); // 等价于 arr[i] } } int main() { int nums[] = {1, 2, 3, 4}; printArray(nums, 4); // nums自动退化为指针 return 0; } ``` ### 结构体递优化 ```c typedef struct { int id; char name[30]; } Student; // 值递(效率低,产生副本) void printStudent(Student s) {...} // 地址递(高效) void printStudentPtr(const Student *s) { printf("ID: %d, Name: %s", s->id, s->name); } // 当需要修改结构体时 void updateId(Student *s, int newId) { s->id = newId; // 直接修改原结构体 } ``` ## 4. 注意事项 1. **指针的安全性**:地址递时需确保指针有效性,避免野指针 2. **const修饰符**:对不希望修改的参数添加`const`约束 ```c void display(const int *ptr); // 保证不修改指针指向的值 ``` 3. **返回局部变量**:禁止返回指向局部变量的指针 4. **多维数组**:需要明确第二维度大小 ```c void processMatrix(int mat[][4], int rows); ``` ## 引用说明 C语言严格遵循值递机制,所谓"地址递"实际上是递**指针的值**(一个内存地址),通过对指针解引用来操作原变量[^1]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值