C 语言

关注公众号 jb51net

关闭
首页 > 软件编程 > C 语言 > C语言 形参和实参

C语言中形参和实参的区别小结

作者:kaf_1

本文介绍了C语言中形参和实参的区别小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

本文介绍了C语言中形参和实参的核心概念。形参是函数定义中的参数,在调用时分配内存并接收实参的值;实参则是函数调用时传入的具体值或表达式。C语言默认采用传值调用机制,通过指针可实现传址调用。文章还详细讲解了数组、结构体、函数指针等不同类型的参数使用方法,以及const参数保护、可变参数等高级特性,并通过示例代码展示了各种参数传递方式的实际效果。

C语言中的形参和实参

一、核心定义

形参 (Formal Parameter)

实参 (Actual Argument)

二、基础示例

#include <stdio.h>

// 函数定义 - a, b 是形参
int add(int a, int b) {
    return a + b;
}

int main() {
    int x = 5, y = 3;
    
    // 函数调用 - x, y 是实参
    int result = add(x, y);
    printf("结果: %d\n", result);  // 输出: 8
    
    // 直接使用常量或表达式作为实参
    result = add(10, 20);          // 常量实参
    printf("结果: %d\n", result);  // 输出: 30
    
    result = add(x, y * 2);        // 表达式作为实参
    printf("结果: %d\n", result);  // 输出: 11
    
    return 0;
}

三、C语言的参数传递机制

1.传值调用 (Call by Value)- C语言的默认方式

#include <stdio.h>

void swap_by_value(int a, int b) {
    int temp = a;
    a = b;
    b = temp;
    printf("函数内: a=%d, b=%d\n", a, b);
}

int main() {
    int x = 10, y = 20;
    printf("交换前: x=%d, y=%d\n", x, y);
    
    swap_by_value(x, y);  // 只传递值的副本
    
    printf("交换后: x=%d, y=%d\n", x, y);  // 原值未改变!
    return 0;
}

输出:

交换前: x=10, y=20
函数内: a=20, b=10
交换后: x=10, y=20  // 原值未变!

2.传址调用 (Call by Address)- 使用指针

#include <stdio.h>

void swap_by_address(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

int main() {
    int x = 10, y = 20;
    printf("交换前: x=%d, y=%d\n", x, y);
    
    swap_by_address(&x, &y);  // 传递地址
    
    printf("交换后: x=%d, y=%d\n", x, y);  // 原值改变了!
    return 0;
}

3.数组作为参数- 实际传递的是指针

#include <stdio.h>

// 数组作为参数(实际是指针)
void print_array(int arr[], int size) {  // int arr[] 等价于 int *arr
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

void modify_array(int arr[], int size) {
    for (int i = 0; i < size; i++) {
        arr[i] *= 2;  // 会修改原数组
    }
}

int main() {
    int nums[] = {1, 2, 3, 4, 5};
    int size = sizeof(nums) / sizeof(nums[0]);
    
    printf("原数组: ");
    print_array(nums, size);
    
    modify_array(nums, size);
    
    printf("修改后: ");
    print_array(nums, size);  // 数组内容已改变
    
    return 0;
}

四、不同类型的参数

1. 基本数据类型参数

#include <stdio.h>

// 各种基本类型参数
void process_basic_types(int i, float f, double d, char c) {
    printf("整型: %d\n", i);
    printf("浮点: %.2f\n", f);
    printf("双精度: %.4lf\n", d);
    printf("字符: %c\n", c);
}

2. 指针参数

#include <stdio.h>
#include <string.h>

// 字符串处理(使用字符指针)
void string_operations(char *str) {
    printf("字符串: %s\n", str);
    printf("长度: %zu\n", strlen(str));
    
    // 修改字符串内容
    str[0] = toupper(str[0]);
}

// 多级指针
void allocate_memory(int **ptr, int size) {
    *ptr = (int *)malloc(size * sizeof(int));
    if (*ptr != NULL) {
        for (int i = 0; i < size; i++) {
            (*ptr)[i] = i * 10;
        }
    }
}

3. 结构体参数

#include <stdio.h>

// 结构体定义
typedef struct {
    int x;
    int y;
} Point;

// 传值 - 传递整个结构体的副本
void print_point_by_value(Point p) {
    printf("点坐标: (%d, %d)\n", p.x, p.y);
    p.x = 100;  // 只修改副本
}

// 传址 - 传递结构体指针
void modify_point_by_address(Point *p) {
    p->x += 10;
    p->y += 10;
}

// 返回结构体
Point create_point(int x, int y) {
    Point p = {x, y};
    return p;
}

4. 函数指针参数

#include <stdio.h>

// 回调函数类型定义
typedef int (*CompareFunc)(int, int);

int max(int a, int b) {
    return (a > b) ? a : b;
}

int min(int a, int b) {
    return (a < b) ? a : b;
}

// 使用函数指针作为参数
int compare_and_process(int a, int b, CompareFunc func) {
    return func(a, b);
}

int main() {
    int result;
    
    result = compare_and_process(10, 20, max);
    printf("较大值: %d\n", result);  // 20
    
    result = compare_and_process(10, 20, min);
    printf("较小值: %d\n", result);  // 10
    
    return 0;
}

五、参数的高级特性

1. const参数(保护数据不被修改)

#include <stdio.h>

// const保护指针指向的数据
void print_string(const char *str) {
    // str[0] = 'A';  // 错误!不能修改const数据
    while (*str) {
        putchar(*str++);
    }
}

// const保护指针本身
void process_array(int *const arr, int size) {
    // arr = NULL;  // 错误!不能修改指针本身
    for (int i = 0; i < size; i++) {
        arr[i] += 1;  // 可以修改指向的数据
    }
}

2. 可变参数(stdarg.h)

#include <stdio.h>
#include <stdarg.h>

// 可变参数函数
int sum_variable_args(int count, ...) {
    int total = 0;
    va_list args;          // 参数列表
    va_start(args, count); // 初始化
    
    for (int i = 0; i < count; i++) {
        total += va_arg(args, int);  // 获取下一个int参数
    }
    
    va_end(args);          // 清理
    return total;
}

int main() {
    printf("总和: %d\n", sum_variable_args(3, 10, 20, 30));  // 60
    printf("总和: %d\n", sum_variable_args(5, 1, 2, 3, 4, 5));  // 15
    return 0;
}

3. 参数默认值(C语言不支持,但可用宏模拟)

// 使用宏模拟默认参数
#define PRINT_MESSAGE(msg, count) print_message_impl(msg, count)

void print_message_impl(const char *msg, int count) {
    for (int i = 0; i < count; i++) {
        printf("%s\n", msg);
    }
}

// 使用重载宏
#define PRINT_MESSAGE_1(msg) PRINT_MESSAGE(msg, 1)
#define PRINT_MESSAGE_2(msg, count) PRINT_MESSAGE(msg, count)

六、重要注意事项

1. 数组参数的大小信息丢失

void process(int arr[]) {  // 等价于 int *arr
    // sizeof(arr) 返回的是指针大小,不是数组大小!
    // 必须额外传递数组大小参数
}

2. 参数的求值顺序

int i = 0;
printf("%d, %d, %d\n", i, ++i, i++);  // 未定义行为!不同编译器结果不同

3. 寄存器变量参数(register关键字)

// 建议编译器将参数放入寄存器(C17起已废弃,但可了解)
void fast_function(register int x, register int y) {
    // 这些参数可能存储在寄存器中,访问更快
}

七、完整示例程序

#include <stdio.h>
#include <stdlib.h>

// 结构体定义
typedef struct {
    char name[50];
    int age;
    float score;
} Student;

// 函数声明
void print_student(const Student *s);
void update_score(Student *s, float new_score);
Student create_student(const char *name, int age, float score);

int main() {
    // 创建学生结构体
    Student stu = create_student("张三", 20, 85.5);
    
    // 传址调用,可以修改原结构体
    printf("修改前:\n");
    print_student(&stu);
    
    update_score(&stu, 90.0);
    
    printf("\n修改后:\n");
    print_student(&stu);
    
    return 0;
}

// 函数定义
Student create_student(const char *name, int age, float score) {
    Student s;
    snprintf(s.name, sizeof(s.name), "%s", name);
    s.age = age;
    s.score = score;
    return s;  // 返回结构体(C语言支持结构体返回)
}

void print_student(const Student *s) {
    printf("姓名: %s\n", s->name);
    printf("年龄: %d\n", s->age);
    printf("分数: %.1f\n", s->score);
}

void update_score(Student *s, float new_score) {
    s->score = new_score;  // 直接修改原结构体
}

八、最佳实践总结

  1. 明确传递意图

    • 不需要修改的参数:使用const修饰
    • 需要修改的参数:使用指针传递
  2. 数组参数

    • 总是同时传递数组和大小
    • 使用const保护不想被修改的数组
  3. 结构体参数

    • 小结构体:传值(简单安全)
    • 大结构体:传址(高效)
    • 需要修改的结构体:必须传址
  4. 参数验证

    void safe_divide(int numerator, int denominator) {
        if (denominator == 0) {
            fprintf(stderr, "错误:分母不能为零\n");
            return;
        }
        // 安全操作...
    }
    
  5. 文档说明

    /*
     * 函数:calculate_average
     * 参数:scores - 分数数组(不能为NULL)
     *        count - 数组元素个数(必须大于0)
     * 返回:平均分,如果出错返回-1
     */
    float calculate_average(const float *scores, int count);
    

理解形参和实参是掌握C语言函数编程的基础,特别是理解C语言严格的传值机制和通过指针实现的传址效果,这是与许多其他语言的重要区别。

对于数组与函数调用来说,实参与形参格式不同,实参只需要给出定义名称即可

到此这篇关于C语言中形参和实参的区别小结的文章就介绍到这了,更多相关C语言 形参和实参内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望

您可能感兴趣的文章:
阅读全文