You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言二维数组传参时int**与变长数组形参结果差异原因

核心原因:两种形参的指针类型完全不同,寻址逻辑天差地别

首先要明确一个基础规则:二维数组int ar[3][2]本质是数组的数组,它的每个元素是int[2]类型的一维数组。当数组名作为函数参数传递时,会退化为指向首个元素的指针,也就是int (*)[2]类型(指向长度为2的int数组的指针),和int**类型完全不兼容。


1. 变长数组形参的运行逻辑

当你声明形参为int ar[rows][cols]时,C语法会自动把这种数组类型的形参调整为指向其首元素的指针,也就是等价于int (*ar)[cols],刚好和你传入的二维数组退化后的指针类型完全匹配。
此时ar[rows - 1][cols - 1]的寻址逻辑是:

  • 以ar存储的基地址为起点,偏移(rows-1) * sizeof(int[cols])字节,得到第rows-1行的首地址
  • 再以该行首地址为起点,偏移(cols-1) * sizeof(int)字节,读取目标元素的值
    整个过程是直接对基地址做两次偏移计算,完全适配二维数组连续内存的布局,所以能正确拿到结果。

2. int**形参的运行逻辑

int**是指向int指针的指针,它的寻址逻辑和数组指针完全不同:

  • 第一步执行ar[rows-1]时,会先把ar存储的地址作为起点,偏移(rows-1) * sizeof(int*)字节,读取该地址的内容,把读出来的数值当成一个int*类型的行指针
  • 第二步再以这个读出来的行指针为起点,偏移(cols-1)*sizeof(int)字节读取目标元素
    你传入的是int (*)[2]类型的数组指针,相当于把普通指针强转成了指向指针的指针给函数。函数执行第一步寻址时,会把二维数组里存储的实际数值(比如你示例里的第一个元素1)当成内存地址去访问,直接触发非法内存访问的段错误,程序自然终止。

关于汇编寻址一致的疑问

你大概率是看错了汇编的操作属性,或者是开启高等级优化时编译器对未定义行为做了非常规处理。正常无优化场景下,两者的汇编逻辑完全不同:

  • 变长数组版本的两次地址计算后,只有第二次是读取数组元素值
  • int**版本第一次计算后就读取了数组值作为地址,还没走到后续计算就已经触发段错误
    如果确实看到汇编计算出了正确地址,也是C标准允许的:未定义行为的执行结果没有任何保障,编译器生成任何逻辑的汇编都符合规则。

如果你一定要用int**作为形参接收二维数组,需要先构造指针数组存储每一行的首地址,再传入函数:

int main()
{
  int ar[3][2] = {{1, 2}, {3, 4}, {5, 6}};
  int *p_ar[3] = {ar[0], ar[1], ar[2]};
  foo(3, 2, p_ar);
}

这种情况下int**版本的函数也能正常运行。

内容的提问来源于stack exchange,提问作者milanHrabos

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 03:15:05