C语言二维数组地址计算与经典面试题详解(附答案)-持续更新

发布时间:2026/8/9 9:01:24
C语言二维数组地址计算与经典面试题详解(附答案)-持续更新 一、二维数组地址计算公式计算二维数组地址核心只有三步确定基址 → 数清前面元素个数 → 乘以元素字节数。绝大多数语言C/C/Java采用行优先存储先存满第一行再存第二行记住这一个公式就够了Addr 基址 (i × 总列数 j) × 单个元素大小下标从 0 起如A[i][j]直接套用公式。前面的元素个数 i行完整的元素 第i行中j前面的元素。举个极简例子int A[3][4]3行4列基址1000int占4字节求A[1][2]。前面元素数 1×4 2 6 个地址 1000 6×4 1024。二、数组名的三种含义sizeof(数组名)—— 数组名表示整个数组计算整个数组的大小。数组名—— 数组名表示整个数组取出整个数组的地址。其他所有情况 —— 数组名表示首元素的地址。三、经典面试题实战1. 一维数组a与a的区别#includestdio.hintmain(){inta[5]{1,2,3,4,5};int*ptr(int*)(a1);printf(%d,%d,*(a1),*(ptr-1));return0;}输出结果2,5解析a数组名转为int*a1偏移1个int*(a1) a[1] 2。a整个数组的地址类型为int(*)[5]a1跳过整个数组5个int指向数组尾后。ptr强转为int*ptr-1回退1个int指向a[4]取值为5。核心考点地址值可能相同但指针类型不同步长完全不同。2. 指针步长与强制类型转换// x86 32位假设struct Test大小20字节structTest{intNum;char*pcName;shortsDate;charcha[2];shortsBa[4];}*p(structTest*)0x100000;intmain(){printf(%p\n,p0x1);printf(%p\n,(unsignedlong)p0x1);printf(%p\n,(unsignedint*)p0x1);return0;}输出结果0x100014 0x100001 0x100004解析p0x1struct Test*指针1 偏移sizeof(struct Test)20(0x14)→0x10000020 0x100014。(unsigned long)p0x1转为普通整型纯数字加1 →0x1000001 0x100001。(unsigned int*)p0x1int*指针1 偏移4字节 →0x1000004 0x100004。考点指针加法偏移量 个数 ×sizeof(指向类型)整数强转后不能再当指针解引用。3. 逗号运算符 二维数组初始化#includestdio.hintmain(){inta[3][2]{(0,1),(2,3),(4,5)};int*p;pa[0];printf(%d,p[0]);return0;}输出结果1解析(0,1)是逗号表达式取最右侧值所以(0,1)1(2,3)3(4,5)5。数组实际初始化int a[3][2] {1, 3, 5}剩余元素自动置0。p[0]等价于a[0][0]输出1。陷阱()不是{}圆括号触发逗号运算符。正确二维初始化应写成{{0,1},{2,3},{4,5}}。4. 数组指针与指针相减#includestdio.hintmain(){inta[5][5];int(*p)[4];pa;printf(%p,%d\n,p[4][2]-a[4][2],p[4][2]-a[4][2]);return0;}输出结果FFFFFFFC,-432位环境解析int (*p)[4]数组指针指向大小为4的int数组p1偏移4*sizeof(int)16字节。pa将int[5][5]的首行地址赋给p但p的步长是int[4]。p[4][2]等价*(*(p4)2)p4偏移4个int[4]→ 偏移了4×4 16个int。a[4][2]是数组a第4行第2列距起始4×52 22个int。p[4][2]距起始162 18个int。指针相减得到元素个数差18 - 22 -4。%p将-4按无符号地址打印输出FFFFFFFC%d正常输出-4。考点数组指针步长由[]内数字决定同类型指针相减得到的是元素个数不是字节数。5.aa1与aa1的区别二维数组#includestdio.hintmain(){intaa[2][5]{1,2,3,4,5,6,7,8,9,10};int*ptr1(int*)(aa1);int*ptr2(int*)(*(aa1));printf(%d,%d,*(ptr1-1),*(ptr2-1));return0;}输出结果10,5解析aa整个二维数组的地址aa1跳过整个数组10个int指向数组末尾之后。ptr1 (int*)(aa1)ptr1-1回退1个int指向最后一个元素10。aa数组名aa1指向第二行等价aa[1]*(aa1)得到第二行首元素地址元素6的地址。ptr2指向6ptr2-1回退到上一行末尾取值5。考点数组名1跳过整个数组数组名1跳过一行。6. 字符指针数组与二级指针#includestdio.hintmain(){char*a[]{work,at,alibaba};char**paa;pa;printf(%s\n,*pa);return0;}输出结果at解析char* a[]字符指针数组每个元素指向一个字符串常量。pa初始指向a[0]即指向work的指针pa后移一个char*大小指向a[1]。*pa得到a[1]即at的首地址打印at。考点char* a[]是指针数组二级指针加减步长是指针类型的大小。7. 多级指针、指针数组与运算符优先级压轴题#includestdio.hintmain(){char*c[]{ENTER,NEW,POINT,FIRST};char**cp[]{c3,c2,c1,c};char***cppcp;printf(%s\n,**cpp);printf(%s\n,*--*cpp3);printf(%s\n,*cpp[-2]3);printf(%s\n,cpp[-1][-1]1);return0;}输出结果POINT ER ST EW逐步解析初始状态c[]{ENTER, NEW, POINT, FIRST}cp[]{c[3], c[2], c[1], c[0]}即cp[0]指向FIRST的指针以此类推cpp指向cp[0]**cppcpp→cpp指向cp[1]第一次解引用得到c2第二次解引用得到c[2]即POINT。 →POINT*-- * cpp 3cpp→cpp指向cp[2]*cpp得到c1--(*cpp)将cp[2]的值从c1改为c指向ENTER解引用得到ENTER首地址3偏移三个字符指向E后面的R输出ER。 →ER*cpp[-2] 3此时cpp仍指向cp[2]cpp[-2]等价*(cpp-2)得到cp[0]即c3指向FIRST解引用并3从F跳过三个字符得到S输出ST。 →STcpp[-1][-1] 1cpp[-1]得到cp[1]其值仍为c2未被修改(c2)[-1]等价*(c2-1) c[1]即NEW1偏移一个字符得到EW。 →EW考点总结/--优先级高于*[]等价于指针加减。cpp[-2]表示*(cpp-2)负下标完全合法。*-- * cpp中--修改了指针数组里的值会影响后续所有用到该位置的计算。四、总结二维数组地址计算看似简单但一旦结合数组名、数组指针、指针步长、强制类型转换以及运算符优先级就会衍生出各种让人头疼的面试题。核心还是要牢记三点地址公式基址 (i×列数 j)×元素大小指针步长由指针类型严格决定数组名1跳整个数组数组名1跳一行表达式优先级善用括号理解*、、[]的运算顺序。希望这篇整理能帮你彻底拿下 C 语言中的二维数组与指针难题