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

C语言指针访问二维数组元素的代码执行逻辑逐行解析

C语言指针与二维数组操作逐行解析

前置核心规则

  • 数组退化规则:除了作为sizeof、&操作的操作数时,数组名在表达式中会自动退化为指向其首元素的指针。一维数组int arr[2]的数组名arr等价于&arr[0],类型为int*;二维数组int arr[2][2]的数组名arr等价于&arr[0],类型为int (*)[2](指向长度为2的int数组的指针)。二维数组元素按行优先连续存储,行与行之间不存在内存空隙。
  • 下标运算本质:a[b] 完全等价于 *(a + b),地址偏移时会自动按照指针指向的类型长度计算步长:比如int* p指向地址0x100,p+1的实际地址是0x104(int占4字节),指向相邻的下一个int元素。
  • 多重解引用规则:多层*操作需要逐层跳转:**p首先取p中存储的地址,解引用得到第一层指针值,再对该指针值做解引用,才能拿到最终的int类型值。
  • 恒等运算规则:*&x完全等价于x,取地址和解引用操作直接抵消,不会产生额外运算。
  • 指针数组识别:int* values[2]是长度为2的数组,每个元素的类型是int*(int型指针),本质是存指针的数组,不是二维数组。

代码逐行执行拆解

先给出示例原始代码:

#include "stdio.h"

int main()
{
    int *pI, *values[2];
    int i1[] = {1, 2}, i2[][2] = {{5, 10}, {20, 30}};
    *values = i2[*i1];
    values[1] = i1;
    pI = &i2[0][0];
    printf(" %d %d\n", **values, *&values[1][1]);
    
    *pI += pI[i1[1]];
    *values = pI;
    *(i1 + 1) *= *(values[1] + 1);
    printf(" %d %d\n", **values, *&values[1][1]);

    return 0;
}

初始状态(变量定义完成后)

为方便推导,先明确内存布局(int占4字节,逻辑地址连续排列):

  • i1是长度为2的一维int数组:i1[0]=1、i1[1]=2
  • i2是2行2列的二维int数组,内存连续排列:i2[0][0]=5、i2[0][1]=10、i2[1][0]=20、i2[1][1]=30
  • values是长度为2的int指针数组,两个元素初始为未赋值的野指针
  • pI是单个int指针,初始为未赋值的野指针

逐句执行逻辑

  1. *values = i2[*i1];
    • 按优先级先计算*i1:i1退化为&i1[0],解引用拿到i1[0]的值1
    • 再计算i2[1]:i2退化为指向首行的指针,偏移1个行长度拿到第二行子数组,子数组退化为指向第二行首元素的指针,即&i2[1][0](对应值20)
    • 最后计算*values:values退化为指向自身首元素values[0]的指针,解引用就是values[0]本身
    • 赋值结果:values[0] = &i2[1][0],即values[0]指向i2第二行第一个元素20
  2. values[1] = i1;
    • i1退化为&i1[0],直接赋值给values数组第二个元素,values[1] = &i1[0],指向i1数组首元素1
  3. pI = &i2[0][0];
    • 取i2第一行第一个元素的地址赋值给pI,pI指向值为5的i2[0][0]
  4. 第一个printf输出:
    • 第一个参数**values:第一次解引用values拿到values[0](即&i2[1][0]),第二次解引用拿到i2[1][0]的值20
    • 第二个参数*&values[1][1]:*&直接抵消,等价于values[1][1];values[1]是&i1[0],偏移1个int长度到&i1[1],解引用拿到i1[1]的值2
    • 输出结果:20 2
  5. *pI += pI[i1[1]];
    • 先计算右值:i1[1]的值为2,pI[2]等价于*(pI + 2),pI指向i2[0][0],偏移2个int长度到i2[1][0],拿到值20
    • 左值*pI是pI指向的i2[0][0],原值为5,执行+=20后,i2[0][0]被修改为25
    • 此时i2内存更新为:i2[0][0]=25、i2[0][1]=10、i2[1][0]=20、i2[1][1]=30
  6. *values = pI;
    • *values等价于values[0],pI当前指向修改后的i2[0][0](值25),因此values[0] = &i2[0][0],不再指向之前的i2[1][0]
  7. *(i1 + 1) *= *(values[1] + 1);
    • 左值*(i1 + 1)等价于i1[1],当前值为2
    • 右值*(values[1] + 1):values[1]仍指向i1[0],偏移1个int长度到i1[1],解引用拿到值2
    • 执行*=运算:i1[1] = 2 * 2 = 4,i1数组更新为i1[0]=1、i1[1]=4
  8. 第二个printf输出:
    • 第一个参数**values:第一次解引用拿到values[0](即&i2[0][0]),第二次解引用拿到值25
    • 第二个参数*&values[1][1]:等价于values[1][1],values[1]指向i1[0],偏移1位到i1[1],拿到修改后的值4
    • 输出结果:25 4

同类问题通用分析步骤

  • 第一步先列初始内存表:把所有数组的元素、初始值、指针的初始指向写下来,不要靠记忆硬推
  • 第二步拆解复杂表达式:按运算优先级从高到低计算,遇到数组名先判断是否退化为指针,遇到下标先转换成*(地址+偏移)的形式,偏移量永远按指针指向的基类型计算步长
  • 第三步逐句更新状态:每执行完一句赋值,立刻更新表中指针的指向、对应内存的存储值,不要沿用上一步的旧状态推导
  • 遇到*&连续操作直接划掉抵消,遇到多重指针逐层解引用,每解一次就替换成对应地址存储的值,直到拿到最终的基础类型值

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 08:12:32