指针收官篇:sizeof/strlen + 指针运算笔试考点全梳理

🏠个人主页:黎雁

🎬作者简介:C/C++/JAVA后端开发学习者

❄️个人专栏:C语言数据结构(C语言)EasyX游戏规划

✨ 从来绝巘须孤往,万里同尘即玉京

文章目录

    • [前景回顾:前五篇指针核心速记 📝](#前景回顾:前五篇指针核心速记 📝)
    • [一、sizeof vs strlen:最易混淆的两个关键字 🆚](#一、sizeof vs strlen:最易混淆的两个关键字 🆚)
      • [1. sizeof:计算"类型/变量/数组"的字节大小 📏](#1. sizeof:计算“类型/变量/数组”的字节大小 📏)
      • [2. strlen:计算字符串的有效长度 📜](#2. strlen:计算字符串的有效长度 📜)
      • [3. sizeof与strlen核心区别表](#3. sizeof与strlen核心区别表)
    • [二、数组与指针笔试题:逐行拆解核心考点 🔍](#二、数组与指针笔试题:逐行拆解核心考点 🔍)
      • [1. 一维整型数组](#1. 一维整型数组)
      • [2. 字符数组(无\0)](#2. 字符数组(无\0))
      • [3. 字符串数组(带\0)](#3. 字符串数组(带\0))
      • [4. 字符指针(指向常量字符串)](#4. 字符指针(指向常量字符串))
      • [5. 二维数组](#5. 二维数组)
    • [三、指针运算笔试题:实战拆解核心逻辑 ⚙️](#三、指针运算笔试题:实战拆解核心逻辑 ⚙️)
      • [1. 数组指针运算](#1. 数组指针运算)
      • [2. 结构体指针运算(x86环境)](#2. 结构体指针运算(x86环境))
      • [3. 二维数组指针运算](#3. 二维数组指针运算)
      • [4. 多级指针与指针数组(高频难题)](#4. 多级指针与指针数组(高频难题))
    • [写在最后 📝](#写在最后 📝)

指针系列的收官之作来啦!这一篇我们直击指针类笔试面试的核心考点------sizeofstrlen的区别、数组/指针笔试题拆解、指针运算实战解析,帮你理清所有易混淆的细节,轻松应对各类指针考题!

前景回顾:前五篇指针核心速记 📝

指针第一讲:从内存到运算,吃透指针核心逻辑
指针第二讲:const 修饰、野指针规避与传址调用
指针第三讲:数组与指针深度绑定 + 二级指针 + 指针数组全解析
指针第四讲:字符指针、数组指针、函数指针及转移表应用
指针第五讲:回调函数与 qsort 的使用和模拟

想要吃透本篇的实战内容,先回顾前四篇的关键知识点:

  1. 指针本质是地址,不同类型的指针指向不同的目标对象,包括变量、数组、函数。
  2. 数组与指针深度绑定,数组传参本质传递首元素地址;函数指针可存储函数地址,实现对函数的间接调用。
  3. 函数指针数组可以构建转移表,简化多分支逻辑;typedef可重命名复杂的指针类型,提升代码可读性。
    想要吃透本篇的笔面试题,先回顾前五篇的关键知识点:
  4. 指针本质是地址,不同类型指针的运算规则不同(+1跳过的字节数由指向类型决定)。
  5. 数组名默认是首元素地址,仅在sizeof(数组名)&数组名时代表整个数组。
  6. 回调函数是通过函数指针调用的函数,qsort借助回调函数实现通用排序。
  7. 指针的解引用、地址运算需严格匹配类型,否则会导致非法访问或逻辑错误。

一、sizeof vs strlen:最易混淆的两个关键字 🆚

sizeofstrlen是笔面试中必考的对比考点,二者的核心区别在于计算目标、终止条件、返回值意义完全不同。

1. sizeof:计算"类型/变量/数组"的字节大小 📏

sizeof是C语言的操作符,不是函数,用于计算数据类型或变量占用的字节数,不关心内存中的实际内容。

  • 语法特点:变量名可省略括号,类型必须加括号;计算时不执行表达式(仅推导类型)。
  • 核心规则:sizeof(数组名)计算整个数组的字节大小,其余场景数组名均视为首元素地址。
c 复制代码
#include <stdio.h>
int main()
{
    int a = 10;
    printf("%zd\n", sizeof(a));    // 4(int类型大小)
    printf("%zd\n", sizeof a);    // 4(变量名可省略括号)
    printf("%zd\n", sizeof(a+2)); // 4(表达式仅推导类型,a+2仍为int)
    printf("%zd\n", sizeof(int)); // 4(类型必须加括号)
    return 0;
}

2. strlen:计算字符串的有效长度 📜

strlen是C语言标准库函数(需包含<string.h>),用于计算以\0为终止符的字符串长度(不包含\0)。

  • 核心规则:必须从有效地址开始,向后查找\0,无\0则返回随机值;参数必须是字符指针(地址)。
c 复制代码
#include <stdio.h>
#include <string.h>
int main()
{
    char arr1[] = "abc"; // 内存:a b c \0
    char arr2[] = {'a','b','c'}; // 无\0
    printf("%zd\n", strlen(arr1)); // 3(找到\0,统计前3个字符)
    printf("%zd\n", strlen(arr2)); // 随机值(无\0,越界查找)
    return 0;
}

3. sizeof与strlen核心区别表

特性 sizeof strlen
本质 操作符 库函数
计算目标 字节大小 字符串有效长度
终止条件 无(仅看类型/数组) \0为终止符
参数类型 任意类型/变量/数组 仅字符指针(地址)
无终止符时 正常计算字节数 返回随机值

二、数组与指针笔试题:逐行拆解核心考点 🔍

数组与指针的笔试题核心是数组名的含义指针类型的运算规则,下面按数组类型分类拆解高频考题。

1. 一维整型数组

c 复制代码
int main()
{
    int a[] = {1,2,3,4};
    printf("%zd\n", sizeof(a));     // ① 16(sizeof(数组名),4*4)
    printf("%zd\n", sizeof(a+0));   // ② 4/8(a+0是首元素地址,地址大小)
    printf("%zd\n", sizeof(*a));    // ③ 4(*a是首元素,int类型)
    printf("%zd\n", sizeof(a+1));   // ④ 4/8(a+1是第二个元素地址)
    printf("%zd\n", sizeof(a[1]));  // ⑤ 4(第二个元素,int类型)
    printf("%zd\n", sizeof(&a));    // ⑥ 4/8(&a是数组地址,地址大小)
    printf("%zd\n", sizeof(*&a));   // ⑦ 16(*&抵消,等价于sizeof(a))
    printf("%zd\n", sizeof(&a+1));  // ⑧ 4/8(&a+1跳过整个数组,仍为地址)
    printf("%zd\n", sizeof(&a[0])); // ⑨ 4/8(首元素地址)
    printf("%zd\n", sizeof(&a[0]+1));// ⑩ 4/8(第二个元素地址)
    return 0;
}

2. 字符数组(无\0)

c 复制代码
int main()
{
    char arr[] = {'a','b','c','d','e','f'};
    printf("%zd\n", sizeof(arr));     // ① 6(sizeof(数组名),1*6)
    printf("%zd\n", sizeof(arr+0));   // ② 4/8(首元素地址)
    printf("%zd\n", sizeof(*arr));    // ③ 1(首元素,char类型)
    printf("%zd\n", sizeof(arr[1]));  // ④ 1(第二个元素,char类型)
    printf("%zd\n", sizeof(&arr));    // ⑤ 4/8(数组地址)
    printf("%zd\n", sizeof(&arr+1));  // ⑥ 4/8(跳过整个数组,地址)
    printf("%zd\n", sizeof(&arr[0]+1));// ⑦ 4/8(第二个元素地址)
    
    // strlen易错点
    printf("%zd\n", strlen(arr));     // 随机值(无\0,越界查找)
    printf("%zd\n", strlen(*arr));    // 程序崩溃(*arr是'a',ASCII97当作地址,非法访问)
    printf("%zd\n", strlen(&arr));    // 随机值(&arr是数组地址,值同首元素地址,无\0)
    return 0;
}

3. 字符串数组(带\0)

c 复制代码
int main()
{
    char arr[] = "abcdef"; // 内存:a b c d e f \0
    printf("%zd\n", sizeof(arr));     // ① 7(sizeof(数组名),1*7,包含\0)
    printf("%zd\n", strlen(arr));     // ② 6(strlen统计到\0前,不含\0)
    printf("%zd\n", sizeof(arr+0));   // ③ 4/8(首元素地址)
    printf("%zd\n", strlen(arr+0));   // ④ 6(首元素地址开始找\0)
    printf("%zd\n", sizeof(*arr));    // ⑤ 1(首元素,char类型)
    printf("%zd\n", strlen(*arr));    // ⑥ 程序崩溃(*arr是'a',非法地址)
    return 0;
}

4. 字符指针(指向常量字符串)

c 复制代码
int main()
{
    char* p = "abcdef"; // p指向常量字符串首地址
    printf("%zd\n", sizeof(p));       // ① 4/8(指针变量大小)
    printf("%zd\n", sizeof(p+1));     // ② 4/8(p+1指向'b',地址大小)
    printf("%zd\n", sizeof(*p));      // ③ 1(*p是'a',char类型)
    printf("%zd\n", strlen(p));       // ④ 6(从'a'开始找\0)
    printf("%zd\n", strlen(p+1));     // ⑤ 5(从'b'开始找\0)
    printf("%zd\n", strlen(*p));      // ⑥ 程序崩溃(*p是'a',非法地址)
    return 0;
}

5. 二维数组

c 复制代码
int main()
{
    int a[3][4] = {0};
    printf("%zd\n", sizeof(a));       // ① 48(sizeof(数组名),3*4*4)
    printf("%zd\n", sizeof(a[0][0])); // ② 4(第一行第一个元素,int类型)
    printf("%zd\n", sizeof(a[0]));    // ③ 16(a[0]是第一行数组名,4*4)
    printf("%zd\n", sizeof(a[0]+1));  // ④ 4/8(a[0]+1是第一行第二个元素地址)
    printf("%zd\n", sizeof(*(a+1)));  // ⑤ 16(a+1是第二行地址,解引用为第二行)
    printf("%zd\n", sizeof(a[3]));    // ⑥ 16(sizeof不执行表达式,推导a[3]为int[4]类型)
    return 0;
}

💡 关键:二维数组的数组名a是第一行的地址(int(*)[4]类型),a[i]是第i行的数组名,sizeof(a[i])计算第i行的字节数。

三、指针运算笔试题:实战拆解核心逻辑 ⚙️

指针运算的核心是类型决定步长 ------指针+1跳过的字节数 = 指向类型的大小,下面拆解高频运算考题。

1. 数组指针运算

c 复制代码
#include <stdio.h>
int main()
{
    int arr[5] = {1,2,3,4,5};
    int (*p)[5] = &arr+1; // &arr是数组地址,+1跳过整个数组
    printf("%d %d", *(arr+1), *(p-1)); // 输出:2 5
    return 0;
}

📝 解析:

  • *(arr+1):arr是首元素地址,+1指向第二个元素,解引用得2。
  • p-1:p是int(*)[5]类型,-1回退整个数组(20字节),指向原数组,*(p-1)等价于arr,解引用后取最后一个元素5。

2. 结构体指针运算(x86环境)

c 复制代码
#include <stdio.h>
struct Test
{
    int Num;
    char* pcName;
    short sDate;
    char cha[2];
}; // 结构体大小20字节(内存对齐)

int main()
{
    struct Test* p = NULL;
    printf("%p\n", p+0x1);        // 输出:00000014(NULL+20字节)
    printf("%p\n", (unsigned long)p+0x1); // 输出:00000001(转为数值+1)
    printf("%p\n", (unsigned int*)p+0x1); // 输出:00000004(转为int*,+1跳过4字节)
    return 0;
}

📝 解析:指针运算步长由类型决定,数值运算直接+1,指针类型不同步长不同。

3. 二维数组指针运算

c 复制代码
#include <stdio.h>
int main()
{
    int aa[2][5] = {1,2,3,4,5,6,7,8,9,10};
    int *p1 = (int*)(&aa+1); // &aa+1跳过整个二维数组,转为int*
    int *p2 = (int*)(*(aa+1)); // aa+1是第二行地址,解引用为第二行数组名,转为int*
    printf("%d %d", *(p1-1), *(p2-1)); // 输出:10 5
    return 0;
}

📝 解析:

  • p1-1:回退4字节,指向二维数组最后一个元素10。
  • p2-1:回退4字节,指向第一行最后一个元素5。

4. 多级指针与指针数组(高频难题)

c 复制代码
#include <stdio.h>
int main()
{
    char* c[]={"ENTER","NEW","POINT","FIRST"};
    char** cp[]={c+3,c+2,c+1,c};
    char*** cpp=cp;
    
    printf("%s\n", **++cpp);        // 输出:POINT
    printf("%s\n", *--*++cpp+3);    // 输出:ER
    printf("%s\n", *cpp[-2]+3);     // 输出:IST
    printf("%s\n", cpp[-1][-1]+1);  // 输出:NTER
    return 0;
}

📝 核心解析:

  • cpp是三级指针,初始指向cp[0]++cpp指向cp[1]c+2),**cppc[2]指向的"POINT"
  • ++cpp指向cp[2]c+1),*cppc+1--*cppc*--*cpp"ENTER"+3"ER"
  • cpp[-2]等价于*(cpp-2),指向cp[0]c+3),*cpp[-2]"FIRST"+3"IST"
  • cpp[-1]指向cp[1]c+2),cpp[-1][-1]等价于*(*(cpp-1)-1),是"NEW"+1"NTER"

写在最后 📝

到这里,C语言指针系列的所有核心知识点就全部讲解完毕了!从基础的指针变量,到高阶的函数指针、回调函数,再到笔面试高频的sizeof/strlen、指针运算,整个知识体系可以总结为:

  1. 指针的本质是地址,类型决定运算规则(+1步长、解引用范围)。
  2. 数组名的含义分场景,sizeof(数组名)&数组名是特殊情况。
  3. sizeof计算字节大小,strlen\0统计长度,二者不可混淆。
  4. 指针运算笔试题的核心是"类型推导+地址偏移",逐行拆解即可理清逻辑。

指针是C语言的灵魂,也是笔面试的重点难点。想要真正掌握,一定要多敲代码、多调试、多分析内存地址的变化。至此,指针系列完结,希望这些内容能帮你彻底攻克指针难关!

要不要我帮你整理一份指针笔试面试核心考点速记表,把所有易考点、易错点汇总起来,方便考前冲刺?

相关推荐
APIshop2 小时前
Java 爬虫 1688 评论 API 接口实战解析
java·开发语言·爬虫
凯子坚持 c2 小时前
Qt 5.14.0 入门框架开发全流程深度解析
开发语言·qt
lingran__2 小时前
数据在内存中的存储详解(C语言拓展版)
c语言·开发语言
编程乐学(Arfan开发工程师)2 小时前
信息收集与分析详解:渗透测试的侦察兵 (CISP-PTE 核心技能)
java·开发语言·javascript·python
bugcome_com2 小时前
深入解析 C# 中 int? 与 int 的核心区别:可空值类型的本质与最佳实践
开发语言·c#
superman超哥2 小时前
仓颉语言中异常处理入门的深度剖析与工程实践
c语言·开发语言·c++·python·仓颉
深蓝海拓2 小时前
PySide6从0开始学习的笔记(十四)创建一个简单的实用UI项目
开发语言·笔记·python·qt·学习·ui·pyqt
祁思妙想2 小时前
Python中CORS 跨域中间件的配置和作用原理
开发语言·python·中间件
与遨游于天地2 小时前
深入了解 Java `synchronized`:从对象头到锁升级、线程竞争感知
java·开发语言·c#