【C 语言系统入门教程】第 16 讲:深入理解指针 (6) | 零基础学习笔记

张开发
2026/6/3 10:47:31 15 分钟阅读
【C 语言系统入门教程】第 16 讲:深入理解指针 (6) | 零基础学习笔记
【C 语言系统入门教程】第 16 讲深入理解指针 (6) | 零基础学习笔记前言本讲是指针系列收官之作也是笔试面试高频考点的集中解析。通过对比sizeof与strlen、拆解数组与指针经典笔试题、详解指针运算陷阱帮你彻底理清数组与指针的底层逻辑攻克 C 语言最容易丢分的指针笔试题。系列往期笔记第 1 讲C 语言常见概念第 2 讲C 语言数据类型和变量第 3 讲分支和循环上第 4 讲分支和循环下第 5 讲数组第 6 讲函数第 7 讲数组和函数实践扫雷游戏第 8 讲VS 实用调试技巧第 10 讲操作符详解第 11 讲深入理解指针 (1)第 12 讲深入理解指针 (2)第 13 讲深入理解指针 (3)第 14 讲深入理解指针 (4)第 15 讲深入理解指针 (5) 本讲学习目标彻底区分sizeof与strlen的本质区别。掌握数组名的核心规则能快速分析一维 / 二维数组笔试题。理解指针运算的底层逻辑指针 ± 整数、指针 - 指针。能独立解析字符指针、指针数组、二级指针的复杂笔试题。识别并避免指针运算中的常见陷阱。建立 地址 步长 的指针分析思维。 核心学习内容1. sizeof 和 strlen 的对比1.1 核心区别表对比项sizeofstrlen类型操作符库函数需包含string.h功能计算内存空间大小字节计算字符串长度\0之前字符数关注点只看内存大小不关心内容必须找到\0否则越界查找参数变量 / 类型字符串首地址1.2 代码示例#include stdio.h #include string.h int main() { char arr1[3] {a,b,c}; // 无\0 char arr2[] abc; // 自动加\0 ​ printf(strlen(arr1) %d\n, strlen(arr1)); // 随机值越界找\0 printf(strlen(arr2) %d\n, strlen(arr2)); // 3 ​ printf(sizeof(arr1) %d\n, sizeof(arr1)); // 3 printf(sizeof(arr2) %d\n, sizeof(arr2)); // 4含\0 return 0; }【易错点】strlen必须遇到\0才停止无\0会越界访问产生随机值。2. 数组和指针笔试题解析核心规则必背sizeof (数组名)数组名表示整个数组计算整个数组大小。 数组名数组名表示整个数组取出整个数组的地址。其他所有情况数组名都是首元素的地址。2.1 一维数组笔试题int a[] {1,2,3,4}; printf(%d\n, sizeof(a)); // 16整个数组4*4 printf(%d\n, sizeof(a0)); // 4/8首元素地址指针大小 printf(%d\n, sizeof(*a)); // 4首元素int printf(%d\n, sizeof(a1)); // 4/8第二个元素地址 printf(%d\n, sizeof(a[1])); // 4第二个元素 printf(%d\n, sizeof(a)); // 4/8整个数组地址指针大小 printf(%d\n, sizeof(*a)); // 16*a等价于a整个数组 printf(%d\n, sizeof(a1)); // 4/8跳过整个数组后的地址 printf(%d\n, sizeof(a[0])); // 4/8首元素地址 printf(%d\n, sizeof(a[0]1));// 4/8第二个元素地址2.2 字符数组笔试题重点代码 1无 \0 字符数组char arr[] {a,b,c,d,e,f}; // sizeof sizeof(arr) // 6整个数组 sizeof(arr0) // 4/8首元素地址 sizeof(*arr) // 1首元素char sizeof(arr[1]) // 1第二个元素 sizeof(arr) // 4/8整个数组地址 sizeof(arr1) // 4/8跳过整个数组后的地址 sizeof(arr[0]1)// 4/8第二个元素地址 ​ // strlen strlen(arr) // 随机值无\0越界 strlen(arr0) // 随机值同上 strlen(*arr) // 错误*arr是aASCII97非法地址 strlen(arr[1]) // 错误同上 strlen(arr) // 随机值整个数组地址仍无\0 strlen(arr1) // 随机值-6跳过整个数组后找\0 strlen(arr[0]1)// 随机值-1从第二个元素开始找代码 2字符串数组含 \0char arr[] abcdef; // 实际a b c d e f \0 // sizeof sizeof(arr) // 7含\0 sizeof(arr0) // 4/8 sizeof(*arr) // 1 sizeof(arr[1]) // 1 sizeof(arr) // 4/8 sizeof(arr1) // 4/8 sizeof(arr[0]1)// 4/8 ​ // strlen strlen(arr) // 6\0之前6个字符 strlen(arr0) // 6 strlen(*arr) // 错误 strlen(arr[1]) // 错误 strlen(arr) // 6整个数组地址仍指向首元素 strlen(arr1) // 随机值跳过整个数组后找\0 strlen(arr[0]1)// 5从b开始代码 3字符指针指向常量字符串char* p abcdef; // sizeof sizeof(p) // 4/8指针大小 sizeof(p1) // 4/8第二个字符地址 sizeof(*p) // 1首字符a sizeof(p[0]) // 1等价于*p sizeof(p) // 4/8二级指针大小 sizeof(p1) // 4/8跳过p后的地址 sizeof(p[0]1) // 4/8第二个字符地址 ​ // strlen strlen(p) // 6 strlen(p1) // 5 strlen(*p) // 错误 strlen(p[0]) // 错误 strlen(p) // 随机值二级指针不是字符串地址 strlen(p1) // 随机值 strlen(p[0]1) // 52.3 二维数组笔试题int a[3][4] {0}; sizeof(a) // 483*4*4 sizeof(a[0][0]) // 4单个int元素 sizeof(a[0]) // 16第一行数组4*4 sizeof(a[0]1) // 4/8第一行第二个元素地址 sizeof(*(a[0]1)) // 4第一行第二个元素 sizeof(a1) // 4/8第二行地址数组指针 sizeof(*(a1)) // 16第二行数组 sizeof(a[0]1) // 4/8第二行地址 sizeof(*(a[0]1))// 16第二行数组 sizeof(*a) // 16第一行数组a是首元素地址即第一行地址 sizeof(a[3]) // 16不越界只计算类型大小【关键】二维数组名a是第一行一维数组的地址类型为int (*)[4]数组指针。3. 指针运算笔试题解析3.1 题目 1#include stdio.h int main() { int a[5] {1,2,3,4,5}; int *ptr (int *)(a 1); printf(%d,%d, *(a1), *(ptr-1)); return 0; }结果2,5解析a是整个数组地址a1跳过整个数组ptr-1回退一个 int指向最后一个元素 5a1指向第二个元素 23.2 题目 2X86 环境结构体 20 字节struct Test { int Num; char *pcName; short sDate; char cha[2]; short sBa[4]; }*p (struct Test*)0x100000; ​ int main() { printf(%p\n, p 0x1); // 0x10001420字节 printf(%p\n, (unsigned long)p 0x1); // 0x100001整数加法 printf(%p\n, (unsigned int*)p 0x1); // 0x1000044字节 return 0; }关键指针加法的步长由指向的类型大小决定整数加法直接加数值。3.3 题目 3逗号表达式陷阱#include stdio.h int main() { int a[3][2] { (0,1), (2,3), (4,5) }; // 逗号表达式取最后一个值 int *p a[0]; printf(%d, p[0]); return 0; }结果1解析初始化中的(0,1)是逗号表达式结果为 1数组实际为{1,3,5,0,0,0}。3.4 题目 4X86 环境#include stdio.h int main() { int a[5][5]; int(*p)[4]; // 指向含4个int的数组 p a; printf(%p,%d\n, p[4][2] - a[4][2], p[4][2] - a[4][2]); return 0; }结果FFFFFFFC,-4解析p是int(*)[4]p[4][2]*(*(p4)2)p4跳过 4 个int[4]64 字节a4跳过 4 个int[5]80 字节地址差为 - 4补码表示为0xFFFFFFFC3.5 题目 5#include stdio.h int main() { int aa[2][5] {1,2,3,4,5,6,7,8,9,10}; int *ptr1 (int *)(aa 1); int *ptr2 (int *)(*(aa 1)); printf(%d,%d, *(ptr1-1), *(ptr2-1)); return 0; }结果10,5解析aa1跳过整个二维数组ptr1-1指向最后一个元素 10aa1指向第二行*(aa1)是第二行首元素地址ptr2-1指向第一行最后一个元素 53.6 题目 6指针数组 二级指针#include stdio.h int main() { char *a[] {work,at,alibaba}; char**pa a; pa; printf(%s\n, *pa); return 0; }结果at解析a是指针数组每个元素是字符串首地址pa指向a[0]pa指向a[1]*pa是at3.7 题目 7三级指针终极题#include stdio.h int main() { char *c[] {ENTER,NEW,POINT,FIRST}; char**cp[] {c3,c2,c1,c}; char***cpp cp; printf(%s\n, **cpp); // POINT printf(%s\n, *--*cpp3); // ER printf(%s\n, *cpp[-2]3); // ST printf(%s\n, cpp[-1][-1]1); // EW return 0; }分步解析**cppcpp指向cp[1]*cpp是c2**cpp是POINT*--*cpp3cpp指向cp[2]*cpp是c1--*cpp是c*c是ENTER3 后是ER*cpp[-2]3cpp[-2]是cp[0]即c3*(c3)是FIRST3 后是STcpp[-1][-1]1cpp[-1]是cp[1]即c2(c2)[-1]是c[1]即NEW1 后是EW三级指针指向关系 课后习题一、选择题关于 sizeof 和 strlen 说法正确的是A. sizeof 是库函数B. strlen 计算内存大小C. sizeof 不关心内容D. strlen 不会越界int a[10]; sizeof(a)的结果是A. 4B. 10C. 40D. 80char arr[] abc; strlen(arr)的结果是A. 3B. 4C. 随机值D. 错误二维数组名a[3][4]的类型是A.int**B.int*C.int (*)[4]D.int [3][4]指针int* p; p1跳过的字节数是A. 1B. 2C. 4D. 8二、判断题sizeof(数组名)计算的是首元素大小。strlen必须找到\0才停止。二维数组名是首元素的地址即a[0][0]的地址。指针 - 指针的结果是两个地址的字节差。数组名1跳过整个数组。三、编程题不使用库函数自己实现strlen函数。分析以下代码的输出结果并解释原因。int main() { int arr[] {1,2,3,4,5}; int* p arr; printf(%d\n, *(p2)); printf(%d\n, p[3]); return 0; } 参考答案一、选择题CCACC二、判断题×整个数组大小√×第一行一维数组的地址×元素个数差√三、编程题1. 模拟实现 strlenint my_strlen(const char* str) { int count 0; while(*str ! \0) { count; str; } return count; }2. 代码输出3 4解析*(p2)等价于p[2]指向第三个元素 3p[3]指向第四个元素 4。 本讲总结sizeof vs strlensizeof 算内存大小strlen 算字符串长度找\0。数组名规则只有sizeof(数组名)和数组名表示整个数组其他都是首元素地址。指针运算指针 ± 整数步长为指向类型的大小指针 - 指针结果为元素个数差二维数组数组名是第一行一维数组的地址类型为数组指针。多级指针从外到内逐层解引用注意运算符优先级。本讲是指针的终极检验彻底掌握这些笔试题才算真正精通 C 语言指针。 版权说明本文为 C 语言系统学习原创笔记基于标准课件体系整理纯干货零基础友好未经允许禁止转载如有错误欢迎评论区指正

更多文章