理解C语言size_t *a[]形参与指针传参机制及数组读取异常
问题根因与修复方案
核心逻辑偏差
C语言所有函数参数传递均为值拷贝,不存在传引用机制。不管是普通变量还是指针,传入函数的都是实参在栈上的一份临时副本,函数内对副本本身的修改,完全不会影响调用方栈上的原变量。
你现在的代码对rows参数的处理是完全正确的:
- 传入的
&rows是调用方perform()栈上rows变量的内存地址,类型为size_t* - 函数内通过
*rows = n,借助指针存储的地址,直接读写对应栈内存,把行数写回了调用方的rows变量
但对sizes参数的处理完全偏离了逻辑:
- 传入的
&sizes是调用方perform()栈上sizes指针变量的内存地址,类型为size_t** - 函数内本来应该通过
*sizes解引用,拿到调用方sizes变量的内存地址,再把malloc出来的行长度数组的首地址写进去 - 你实际写的是
sizes = malloc(...),相当于直接把函数栈帧上的那个二级指针副本(本来存的是调用方sizes的栈地址),改成了malloc返回的堆地址。这个副本只在函数运行时存在,函数一返回栈帧销毁,调用方的sizes变量从来没被写入过,一直是未初始化的野值,读取时自然得到全0甚至随机值。
形参size_t* sizes[]的本质
C语言规定,函数形参位置书写的
类型 数组名[]格式,会在编译阶段自动退化为等价的指针类型。
也就是说,你写的形参size_t* sizes[],和直接写size_t** sizes没有任何运行时区别,本质就是一个存储了size_t*类型变量地址的二级指针,和普通二级指针完全等价,和数组没有任何运行时关联。
之前memcpy方案触发段错误的原因
你之前写的memcpy(sizes, malloc(n * sizeof(size_t)), (n * sizeof(size_t)))属于完全错位的内存操作:
sizes本身存的是调用方perform()栈上sizes变量的地址,memcpy会从这个地址开始,连续写入n*sizeof(size_t)长度的内存,直接踩穿了perform()的栈空间,把栈上的返回地址、其他变量值全部覆盖,必然触发段错误。- 这个场景根本不需要memcpy,因为我们要写入调用方
sizes变量的只是一个指针值(通常是4或8字节),直接通过解引用赋值就能完成,memcpy在这里不仅多余,还会因为操作长度错误直接破坏内存。
正确实现(无需memcpy)
只需要把两处错位的赋值逻辑修正即可,不需要额外的内存拷贝:
- 把原来给局部二级指针赋值的
sizes = malloc(...),改成解引用后给调用方的sizes变量赋值:*sizes = malloc(n * sizeof(size_t)) - 修正数组元素访问的运算符优先级问题:C语言中
[]的优先级高于*,直接写sizes[i]会访问二级指针指向的后续栈内存,必须加括号写成(*sizes)[i]才能正确访问malloc出来的行长度数组元素。
修正后的完整代码:
int64_t** marray_read( size_t* rows, size_t* sizes[] ) { size_t n = read_size(); *rows = n; // 把行长度数组的首地址写入调用方的sizes变量 *sizes = malloc(n * sizeof(size_t)); int64_t **array = (int64_t **)malloc(n*sizeof(int64_t*)); size_t m = 0; for (size_t i = 0; i < n; i++) { m = read_size(); // 加括号保证先解引用拿到行长度数组首地址,再访问第i个元素 (*sizes)[i] = m; array[i] = (int64_t *)malloc(m*sizeof(int64_t)); for (size_t j = 0; j < m; j++) { array[i][j] = read_int64(); } } for (size_t i = 0; i < n; i++) { printf("%zu ", (*sizes)[i]); } printf("\n"); return array; } void perform() { size_t rows; size_t* sizes; int64_t** marray = marray_read(&rows, &sizes); for (size_t i = 0; i < rows; i++) { printf("%zu ", sizes[i]); } }
C语言指针传参的底层规则总结
- 所有函数参数传递都是值拷贝,不管类型是整数、浮点数还是指针,传入函数的都是实参的一份临时副本,存放在函数专属的栈帧上
- 如果需要在函数内修改调用方的变量,必须传递该变量的内存地址,通过地址间接读写对应内存,才能把修改同步回调用方的栈空间
- 所谓“传指针本质也是传值”,指的是指针本身是一个存储地址的整数值,如果直接修改这个拷贝的指针变量本身,调用方的原指针不会有任何变化;只有通过指针存储的地址去操作对应内存,才是有效修改
- 运算符优先级会直接影响指针解引用的逻辑,访问二级指针指向的动态数组元素时,必须优先处理解引用操作,必要时加括号避免逻辑错位
内容的提问来源于stack exchange,提问作者vasyok03
相关产品推荐
相关产品推荐

