C语言传递数组/指针时如何让函数修改不影响main原数组
实现方案
你当前的示例代码属于C标准定义的未定义行为:将const限定的数组地址隐式转换为非const指针后修改,不同编译器、编译选项下的运行结果不可控——可能出现修改同步到原数组的情况,也可能直接触发运行时崩溃。
要实现「main内数组不可直接修改、传参给其他函数后修改不影响原数组、无需手动拷贝数组」的需求,有两种成熟实现方式:
方案1:结构体包装数组 + 值传递(跨平台通用,无依赖)
C语言语法不支持数组直接按值传参,但结构体默认按值传递。把数组包装在结构体内部,给main中的结构体实例加const修饰即可满足全部要求:
- main内受
const限定,直接修改数组内容会触发编译报错,无法直接修改 - 传参时编译器会自动生成结构体(含内部数组)的完整副本传递给被调函数,被调函数的所有修改都作用在副本上,不会影响原数组
- 拷贝动作由编译器自动完成,不需要手动编写
memcpy或循环拷贝的逻辑,全平台兼容。
实现代码:
#include <stdio.h> // 用结构体包装目标数组 typedef struct { int arr[4]; } IntArray; void fun(IntArray arr_copy); int main() { const IntArray a = {.arr = {1,2,10,20}}; // a.arr[3] = 42; // 取消注释会直接编译报错,main内无法修改 fun(a); // 值传递,编译器自动拷贝数组 printf("main中a.arr[3] = %d\n", a.arr[3]); // 固定输出20,原值不受影响 return 0; } void fun(IntArray arr_copy) { arr_copy.arr[3] = 42; // 仅修改自动生成的副本 printf("fun内部arr_copy.arr[3] = %d\n", arr_copy.arr[3]); // 输出42 }
运行输出:
fun内部arr_copy.arr[3] = 42 main中a.arr[3] = 20
这个方案是最通用的实现,适合绝大多数常规场景。
方案2:操作系统级写时复制(COW,适合超大数组场景)
如果数组体量极大,每次传参全量拷贝会带来明显的性能开销,可以借助操作系统的内存管理特性实现写时复制:将原数组所在内存页标记为只读私有映射,初始状态下不会拷贝任何内存数据,只有当被调函数尝试写入数组内存时,操作系统才会自动复制被写入的内存页供修改使用,原数组所在内存页完全不会被改动。
这个方案同样不需要手动拷贝数组,且只有实际发生写入时才会复制对应内存页,大数组场景下性能远高于全量拷贝,但缺点是依赖操作系统的内存API,跨平台兼容性差。以下是Linux环境下的参考实现:
#include <stdio.h> #include <sys/mman.h> #include <unistd.h> #include <string.h> void fun(int *arr) { arr[3] = 42; printf("fun内部a[3] = %d\n", arr[3]); // 输出42 } int main() { long page_size = sysconf(_SC_PAGESIZE); // 分配页对齐的私有匿名内存映射 int *a = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); // 初始化数组内容 memcpy(a, (int[]){1,2,10,20}, sizeof(int)*4); // 将内存页设置为只读,触发写时复制机制 mprotect(a, page_size, PROT_READ); // main内直接执行a[3] = 42会触发SIGSEGV段错误,满足只读要求 fun(a); printf("main中a[3] = %d\n", a[3]); // 固定输出20 munmap(a, page_size); return 0; }
不建议继续使用最初的写法:通过强转const指针修改const限定的内存,属于未定义行为,代码的运行结果没有任何保障。
内容的提问来源于stack exchange,提问作者John Doe
相关产品推荐
相关产品推荐

