当前位置: C语言 -- 专题 -- C语言的参数传递机制

C语言的参数传递机制

关于C语言中参数是如何传递的,ISO/IEC 9899:2018标准第6.5.2.2 Function calls节有这样两段话,原文如下:

In preparing for the call to a function, the arguments are evaluated, and each parameter is assigned the value of the corresponding argument.

A function can change the values of its parameters, but these changes cannot affect the values of the arguments. On the other hand, it is possible to pass a pointer to an object, and the function can then change the value of the object pointed to. A parameter declared to have array or function type is adjusted to have a pointer type as described in 6.9.1.

意思是:

函数调用时,将对实际参数进行评估,每个形式参数将被赋予对应实际参数的值。

函数能够改变形式参数的值,但不会影响实际参数的值。通过指针,函数可以改变指针指向对象的值。声明为数组类型或者函数类型的形式参数将被调整为ISO/IEC 9899:2018标准第6.9.1节描述的指针类型。

这也就是说C语言中参数是按值传递(pass by value)的。


下面通过具体例子讨论一下C语言中参数是如何按值传递的。

1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
/*
** C语言按值传递机制
** 非指针类型
*/

#include <stdio.h>

/* 交换函数-交换两个参数的值。*/
void swap(int A, int B)
{
    /* 交换前形式参数的值。*/
    printf("Before swap in swap function: A=%d\n", A);
    printf("Before swap in swap function: B=%d\n\n", B);
    
    /* 交换形式参数的值。*/
    int temp = A;
    A = B;
    B = temp;
    
    /* 交换后形式参数的值。*/
    printf("After swap in swap function: A=%d\n", A);
    printf("After swap in swap function: B=%d\n", B);
}

int main(void)
{
    int a = 3;
    int b = 7;

    /* 交换前实际参数的值。*/
    printf("Before swap in main function: a=%d\n", a);
    printf("Before swap in main function: b=%d\n", b);
    
    swap(a, b);
    
    /* 交换后实际参数的值。*/
    printf("After swap in main function: a=%d\n", a);
    printf("After swap in main function: b=%d\n", b);

    return 0;
}

将输出:

Before swap in main function: a=3

Before swap in main function: b=7

Before swap in swap function: A=3

Before swap in swap function: B=7

 

After swap in swap function: A=7

After swap in swap function: B=3

After swap in main function: a=3

After swap in main function: b=7


下面按程序执行的先后顺序分析一下实际参数、形式参数的存在状态。

程序
执行顺序
执行main函数,
初始化实参a、b。
调用swap函数。 执行swap函数,
交换形参值。
swap函数
调用结束。
main函数
执行结束。
参数 a(3)
b(7)
a(3)
b(7)
A(3)
B(7)
a(3)
b(7)
A(7)
B(3)
a(3)
b(7)
A(7)
B(3)
a(3)
b(7)

宿主环境中C程序的执行是从main函数开始按顺序执行的;执行实际参数ab的初始化后,ab的值分别为37;调用swap函数,形式参数AB值通过对应的实际参数赋值获得;在swap函数中交换形式参数值,AB中存储的值发生改变;swap函数调用结束,形式参数AB将不可见;main函数执行结束,实际参数ab将不可见;所以在这个过程中形式参数值的改变不会影响实际参数值。


1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
43 
44 
45 
46 
47 
48 
/*
** C语言按值传递机制
** 指针类型
*/

#include <stdio.h>

/* 交换函数-交换两个指针参数的值,并修改指针指向对象的值。*/
void swap(int *ptrA, int *ptrB)
{
    /* 交换前形式参数的值。*/
    printf("Before swap in swap function: ptrA=%p\n", ptrA);
    printf("Before swap in swap function: ptrB=%p\n\n", ptrB);
    
    /* 修改指针指向对象的值,并交换形式参数的值。*/
    *ptrA = 7;
    *ptrB = 3;
    int *temp = ptrA;
    ptrA = ptrB;
    ptrB = temp;
    
    /* 交换后形式参数的值。*/
    printf("After swap in swap function: ptrA=%p\n", ptrA);
    printf("After swap in swap function: ptrB=%p\n", ptrB);
}

int main(void)
{
    int a = 3, b = 7;
    int *ptra = &a, *ptrb = &b;

    /* 交换前实际参数及其指向对象的值。*/
    printf("Before swap in main function: ptra=%p\n", ptra);
    printf("Before swap in main function: ptrb=%p\n", ptrb);
    printf("Before swap in main function: a=%d\n", a);
    printf("Before swap in main function: b=%d\n", b);
    
    swap(ptra, ptrb);
    
    /* 交换后实际参数及其指向对象的值。*/
    printf("After swap in main function: ptra=%p\n", ptra);
    printf("After swap in main function: ptrb=%p\n", ptrb);
    printf("After swap in main function: a=%d\n", a);
    printf("After swap in main function: b=%d\n", b);
    
    return 0;
}

将输出:

Before swap in main function: ptra=0028ff14

Before swap in main function: ptrb=0028ff10

Before swap in main function: a=3

Before swap in main function: b=7

Before swap in swap function: ptrA=0028ff14

Before swap in swap function: ptrB=0028ff10

 

After swap in swap function: ptrA=0028ff10

After swap in swap function: ptrB=0028ff14

After swap in main function: ptra=0028ff14

After swap in main function: ptrb=0028ff10

After swap in main function: a=7

After swap in main function: b=3


下面按程序执行的先后顺序分析一下实际参数、形式参数及其指向对象的存在状态。

程序
执行顺序
执行main函数,初始化实参ptra、ptrb及其指向对象a、b。 调用swap函数。 执行swap函数,
修改指向对象值。
执行swap函数,
交换形参值。
swap函数
调用结束。
main函数
执行结束。
参数
及其指向对象
a(3)
b(7)
ptra(&a)
ptrb(&b)
a(3)
b(7)
ptra(&a)
ptrb(&b)
ptrA(&a)
ptrB(&b)
a(7)
b(3)
ptra(&a)
ptrb(&b)
ptrA(&a)
ptrB(&b)
a(7)
b(3)
ptra(&a)
ptrb(&b)
ptrA(&b)
ptrB(&a)
a(7)
b(3)
ptra(&a)
ptrb(&b)
ptrA(&b)
ptrB(&a)
a(7)
b(3)
ptra(&a)
ptrb(&b)

调用swap函数,形式参数ptrAptrB值通过对应的实际参数ptraptrb赋值获得,所以ptrAptrB的值就是变量ab的地址;对表达式*ptrA*ptrB赋值就是对变量ab赋值;swap函数调用结束后形式参数ptrAptrB不可见,但变量ab仍然存在;通过这种方式函数可以修改指针指向对象的值。


为防止指向对象被意外修改,可以使用const类型限定符对指向对象进行限定。

按值传递对于小型数据特别有效;对于一些大型数据(例如:数组、结构等)可以通过指针的按值传递来提高效率。