
C语言学习笔记20260615-指针与数组进阶本笔记基于以下代码片段分析指针、数组、结构体、多级指针等核心知识点并总结常见陷阱。一、32位小端环境二、逐段分析与知识点讲解1数组名与取地址数组名inta[5]{1,2,3,4,5};int*ptr(int*)(a1);printf(%d,%d,*(a1),*(ptr-1));输出 2,5分析a 是数组首元素地址a1 指向第二个元素 → 2a 是整个数组的地址a1 跳过整个数组20字节指向数组末尾ptr 指向末尾后的 intptr-1 回退到最后一个元素 → 5关键点a1 的步长是 sizeof(a)而 a1 的步长是 sizeof(int)。数组名在 作用下类型变为“指向整个数组的指针。2结构体指针、整数、不同类型指针的加法structTest{intNum;char*pcName;shortsDate;charcha[2];shortsBa[4];}*p0x100000;// 结构体大小20字节printf(%p\n,p0x1);// 0x100014printf(%p\n,(unsignedlong)p0x1);// 0x100001printf(%p\n,(unsignedint*)p0x1);// 0x100004输出0x1000140x1000010x100004分析p 0x1指针加法移动 1 * sizeof(Test) 20 字节 → 0x100000 20 0x100014(unsigned long)p 0x1将地址转为整数直接加1 → 0x100001(unsigned int*)p 0x1转为 unsigned int* 类型步长4字节 → 0x100000 4 0x100004关键点指针加法依赖于指针指向的类型大小。整数加法没有类型缩放。不同类型的指针步长不同。3指针与整数的相互转换 字节序inta[4]{1,2,3,4};int*ptr1(int*)(a1);int*ptr2(int*)((int)a1);printf(%x,%x,ptr1[-1],*ptr2);输出小端int4字节 4,2000000分析ptr1[-1] *(ptr1-1)指向最后一个元素 → 4(int)a 1将数组地址转成整数加1字节再转回 int*内存布局小端a[0]1 → 01 00 00 00a[1]2 → 02 00 00 00地址 a1 处读4字节00 00 00 02 → 0x02000000 → 2000000关键点跨字节边界读取 int 会得到“错位”的值结果依赖字节序。大端环境下*ptr2 会是 0x01000000 或其他值不可移植。4逗号表达式与数组初始化inta[3][2]{(0,1),(2,3),(4,5)};int*pa[0];printf(%d,p[0]);输出 1分析逗号表达式 (x,y) 的值是 y。初始化列表实际为{1, 3, 5}但二维数组每行需要2个元素不足的补0。最终数组内容{1, 0}{3, 0}{5, 0}p[0] a[0][0] 1关键点逗号表达式在初始化列表中常被误用应避免。数组初始化不足时剩余元素自动初始化为0。5不同长度数组指针的减法inta[5][5];int(*p)[4]a;// p指向长度为4的int数组printf(%p,%d\n,p[4][2]-a[4][2],p[4][2]-a[4][2]);输出32位 0xFFFFFFFC,-4分析p 指向 int[4]p1 移动 4*sizeof(int)16 字节。a 指向 int[5]a1 移动 5*sizeof(int)20 字节。p[4][2] 与 a[4][2] 的地址差(416 24) - (420 24) 64 - 80 -16 字节。指针相减得到元素个数差-16 / sizeof(int) -4。%p 打印 -4 的补码表示32位下 0xFFFFFFFC。关键点不同长度的数组指针赋值是合法的但后续计算容易出错。指针相减结果为 ptrdiff_t单位是元素个数不是字节数。6二维数组的取地址与数组名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));输出 10,5分析aa 是整个二维数组的地址aa1 指向数组末尾即 aa 后面。ptr1-1 指向最后一个元素 aa[1][4] 10。*(aa1) aa[1]第二行首地址ptr2 指向 aa[1][0] 6。ptr2-1 指向 aa[0][4] 5。关键点aa 类型为 int(*)[2][5]步长是整个数组大小。aa1 类型为 int(*)[5]步长是一行5个int。7指针数组与二级指针char*a[]{work,at,alibaba};char**paa;pa;printf(%s\n,*pa);输出 at分析a 是指针数组每个元素指向一个字符串常量。pa 指向 a[0]pa 指向 a[1]。*pa 取出 a[1] 中存储的地址即字符串 “at” 的首地址。关键点指针数组 二级指针 是实现字符串数组的常用方式。8三级指针的复杂运算char*c[]{ENTER,NEW,POINT,FIRST};char**cp[]{c3,c2,c1,c};char***cppcp;printf(%s\n,**cpp);// POINTprintf(%s\n,*--*cpp3);// ERprintf(%s\n,*cpp[-2]3);// STprintf(%s\n,cpp[-1][-1]1);// EW初始状态c[0]“ENTER”, c[1]“NEW”, c[2]“POINT”, c[3]“FIRST”cp[0]c3, cp[1]c2, cp[2]c1, cp[3]ccpp cp // 指向 cp[0]① **cppcpp → 指向 cp[1]*cpp → c2**cpp → *(c2) c[2] “POINT”②–cpp 3cpp → 指向 cp[2]此时 cpp 已指向 cp[2]*cpp → c1–*cpp → (c1)-1 c–cpp → c[0] “ENTER”3 → 指向第4个字符 ‘E’ → “ER”③ *cpp[-2] 3cpp 仍指向 cp[2]cpp[-2] *(cpp-2) cp[0] c3*cpp[-2] c[3] “FIRST”3 → 指向第4个字符 ‘S’ → “ST”④ cpp[-1][-1] 1cpp[-1] *(cpp-1) cp[1] c2cpp[-1][-1] *(c2 -1) *(c1) c[1] “NEW”1 → 指向第二个字符 ‘E’ → “EW”关键点多级指针的 、-- 和 * 组合时从右向左逐层分析。[] 运算符等价于 * 和 的组合p[n] *(pn)。三、常见错误与避坑指南1. a1 vs a1a1 跳过整个数组常用于取数组末尾指针a1 仅跳过一个元素2.(int)a1依赖字节序不可移植应避免直接地址算术后再解引用3.不同长度数组指针赋值合法但容易引起计算错误尽量保持类型一致4.多级指针的 与 * 结合从右向左理解–cpp 先运算 cpp再 *再 --再 *5.指针减法结果类型为 ptrdiff_t单位是元素个数不是字节数