C语言parameters,传值、传址与指针如何区分?

99ANYc3cd6
预计阅读时长 21 分钟
位置: 首页 C语言 正文
  1. 参数的定义:什么是参数?
  2. 形参与实参:两者之间的区别与联系。
  3. 参数传递方式:C 语言如何传递参数?
  4. 不同类型的参数:值传递、指针传递、数组名传递。
  5. 可变参数:如何处理参数数量不固定的函数?
  6. 最佳实践与注意事项

参数的定义

在 C 语言中,参数是函数定义和函数调用之间的信息桥梁,它们允许你向函数传递数据,使函数能够根据这些数据执行不同的操作。

c语言parameters
(图片来源网络,侵删)
  • 函数定义:在函数定义时,参数被称为形式参数(Formal Parameters),简称形参,它们是函数括号内声明的变量,用于接收传递进来的值。
  • 函数调用:在调用函数时,传递给函数的实际值被称为实际参数(Actual Arguments),简称实参,实参可以是常量、变量、表达式或函数调用。

简单比喻: 想象一下你点外卖:

  • 函数:就是那个“做菜的厨房”。
  • 函数定义:厨房的菜单上写着“需要一份肉和一份菜才能做这道菜”,这里的“肉”和“菜”就是形参,它们是厨房接收食材的“容器”或“要求”。
  • 函数调用:你打电话给厨房说“我要一份牛肉和一份西兰花”,这里的“牛肉”和“西兰花”就是实参,是你实际传递给厨房的食材。

形参与实参

特性 形参 实参
位置 函数定义的函数头中声明 函数调用的函数名后括号内
作用 接收实参传递过来的值 将值传递给形参
本质 是一个变量,有类型和名称 是一个,可以是常量、变量、表达式
内存 在函数被调用时创建,函数结束时销毁 在函数调用时,将其值复制给形参

示例代码:

#include <stdio.h>
// 函数定义
// a 和 b 是形参
int add(int a, int b) {
    printf("Inside function: a = %d, b = %d\n", a, b);
    return a + b;
}
int main() {
    int x = 5;
    int y = 10;
    // 函数调用
    // x 和 y 是实参
    int sum = add(x, y);
    printf("In main: sum = %d\n", sum);
    return 0;
}

输出:

Inside function: a = 5, b = 10
In main: sum = 15

在这个例子中,main 函数中的变量 xy(实参)的值被复制给了 add 函数中的变量 ab(形参)。

c语言parameters
(图片来源网络,侵删)

参数传递方式:值传递

C 语言中,所有参数传递都是通过“值传递”(Pass-by-Value)实现的。

这意味着:

  1. 当函数被调用时,会为形参创建新的、独立的内存空间。
  2. 将实参的复制到这些新的内存空间中。
  3. 函数内部对形参的任何修改,都只影响它自己的那份副本,不会影响原始实参的值

示例:证明值传递

#include <stdio.h>
void modifyValue(int num) { // num 是形参
    printf("Inside modifyValue (before change): num = %d\n", num);
    num = 100; // 修改的是 num 这个副本
    printf("Inside modifyValue (after change): num = %d\n", num);
}
int main() {
    int myNum = 10;
    printf("In main (before calling function): myNum = %d\n", myNum);
    modifyValue(myNum); // myNum 是实参
    printf("In main (after calling function): myNum = %d\n", myNum);
    return 0;
}

输出:

c语言parameters
(图片来源网络,侵删)
In main (before calling function): myNum = 10
Inside modifyValue (before change): num = 10
Inside modifyValue (after change): num = 100
In main (after calling function): myNum = 10

可以看到,modifyValue 函数内部将 num 修改为 100,但 main 函数中的 myNum 仍然是 10,这证明了传递的是一份副本。


不同类型的参数(高级应用)

既然是值传递,那如何才能在函数内部修改外部变量的值呢?答案是传递变量的地址,这就是指针的用武之地。

1 传递指针(模拟引用传递)

通过传递变量的地址,我们可以在函数内部通过指针间接访问和修改原始变量。

示例:通过指针修改外部变量

#include <stdio.h>
void modifyValueByPointer(int *ptr) { // ptr 是一个指向整形的指针(形参)
    printf("Inside function (before change): *ptr = %d\n", *ptr);
    *ptr = 100; // 通过解引用指针来修改 ptr 所指向的内存中的值
    printf("Inside function (after change): *ptr = %d\n", *ptr);
}
int main() {
    int myNum = 10;
    printf("In main (before calling function): myNum = %d\n", myNum);
    // 注意这里传递的是变量的地址 &myNum
    modifyValueByPointer(&myNum);
    printf("In main (after calling function): myNum = %d\n", myNum);
    return 0;
}

输出:

In main (before calling function): myNum = 10
Inside function (before change): *ptr = 10
Inside function (after change): *ptr = 100
In main (after calling function): myNum = 100

这次,main 函数中的 myNum 被成功修改了,因为我们传递的是 myNum 的地址,函数内部的 *ptr 操作直接作用于 myNum 所在的内存。

2 传递数组名

数组名作为函数参数时,有一个特殊的性质:数组名会“退化”为其首元素的地址

传递数组名本质上是传递一个指针。

示例:传递数组

#include <stdio.h>
// 接收一个整型数组,arr 是一个指向 int 的指针
void printArray(int arr[], int size) {
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}
int main() {
    int numbers[] = {1, 2, 3, 4, 5};
    int size = sizeof(numbers) / sizeof(numbers[0]);
    // 传递数组名 numbers
    printArray(numbers, size);
    return 0;
}

printArray 函数中,int arr[]int *arr 在功能上是等价的,函数内部对 arr[i] 的操作,实际上是通过指针算术运算访问原始数组 numbers 的元素,因此可以修改数组内容。


可变参数

C 语言提供了一种机制,允许函数接受数量可变的参数,最经典的例子就是 printf 函数。

要创建可变参数函数,需要包含 <stdarg.h> 头文件,并使用以下宏:

  • va_list: 一个类型,用于存储可变参数列表。
  • va_start(ap, last_fixed_arg): 初始化 va_list 变量 aplast_fixed_arg 是最后一个固定的参数。
  • va_arg(ap, type): 从 ap 中获取下一个参数,type 是该参数的类型。
  • va_end(ap): 清理 va_list

示例:计算可变参数的平均值

#include <stdio.h>
#include <stdarg.h> // 必须包含此头文件
// last_fixed_arg 是 count
double average(int count, ...) {
    va_list args;      // 声明一个 va_list 变量
    double sum = 0.0;
    // 初始化 args,count 是最后一个固定参数
    va_start(args, count);
    // 循环 count 次,从 args 中获取每个参数
    for (int i = 0; i < count; i++) {
        // va_arg 的第二个参数是你要获取的参数的类型
        sum += va_arg(args, int);
    }
    // 清理 args
    va_end(args);
    return sum / count;
}
int main() {
    printf("Average of 2, 3, 4, 5 is: %.2f\n", average(4, 2, 3, 4, 5));
    printf("Average of 10, 20 is: %.2f\n", average(2, 10, 20));
    return 0;
}

输出:

Average of 2, 3, 4, 5 is: 3.50
Average of 10, 20 is: 15.00

注意: 使用可变参数函数时,你必须非常清楚参数的类型和数量,否则会导致未定义行为(比如程序崩溃)。


最佳实践与注意事项

  1. 使用 const 保护数据:如果函数只需要读取参数的值,而不需要修改它,最好在形参前加上 const 关键字,这可以防止函数内部意外修改数据,同时也能让代码意图更清晰。

    // 好的实践
    int calculateLength(const char *str); // 函数承诺不会修改 str 指向的内容
  2. 指针参数要明确:如果参数是指针,一定要在注释或函数名中明确说明该指针是用于输入、输出,还是两者兼具。

    • 输入指针void processData(const Data* data);
    • 输出指针void getResult(int* result);
  3. 传递大型结构体时使用指针:如果函数需要接收一个很大的结构体作为参数,直接传递结构体(值传递)会导致整个结构体被复制,造成性能开销,这时应该传递指向该结构体的指针。

    // 低效
    void processBigStruct(BigStruct bs);
    // 高效
    void processBigStruct(const BigStruct* bs_ptr);
  4. 注意数组长度:当传递数组时,最好同时传递数组的长度,以防止数组越界访问。

概念 关键点
参数 函数间传递数据的桥梁。
形参 vs 实参 形参是函数定义中的变量,实参是调用时传递的具体值。
传递方式 C 语言只有值传递
如何修改外部变量 传递变量的地址(即使用指针)。
数组作为参数 数组名会退化为指针,传递的是首元素地址。
可变参数 使用 <stdarg.h> 中的宏实现,需谨慎使用。
最佳实践 对只读数据使用 const,大型结构体传指针,明确指针用途。
-- 展开阅读全文 --
头像
织梦安装配置文件怎么获取?
« 上一篇 03-02
织梦系统如何更改标题图片?
下一篇 » 03-02

相关文章

取消
微信二维码
支付宝二维码

目录[+]