指针 + 1 到底加了多少?——指针运算的本质,Day12 学习记录

发布时间:2026/7/28 23:12:12

指针 + 1 到底加了多少?——指针运算的本质,Day12 学习记录 1. 指针运算的本质p1 不是地址加 1学指针的时候我一直以为p1就是地址值直接加 1。直到有一天写代码发现int *p和char *p做p1的结果完全不一样——同一个操作打印出来的地址偏移居然差了 4 倍。于是我明白了一件事指针加 1加的不是 1 个字节而是 1 个元素的大小。提出问题既然指针本身存的是一个地址值那p1到底加了多少为什么int*和char*的表现不同示例#includestdio.hintmain(){inta10;charcA;int*p_inta;char*p_charc;printf(p_int %p\n,p_int);printf(p_int 1 %p\n,p_int1);printf(p_char %p\n,p_char);printf(p_char 1 %p\n,p_char1);return0;}在我的机器上输出类似这样具体值每次运行不同但偏移规律固定p_int 0x7ffe1a3b5c2c p_int 1 0x7ffe1a3b5c30 ← 差 4 字节 p_char 0x7ffe1a3b5c2b p_char 1 0x7ffe1a3b5c2c ← 差 1 字节为什么这样p N的实际偏移字节数等于N × sizeof(基类型)。编译器在编译时就知道指针指向的类型占多大空间所以自动帮你算好偏移量。int在我的机器上占 4 个字节所以p_int 1地址向后跳 4。char占 1 个字节所以p_char 1地址向后跳 1。这个设计的好处是你不用自己算偏移字节数编译器帮你保证p1刚好指向「下一个元素」的起始位置。指针相减也是类似逻辑两个同类型指针相减结果是它们之间相差的元素个数不是字节数。比如(p 5) - p的结果是 5。一句总结指针运算的本质是元素级别的地址偏移步长取决于指针指向的类型大小不是机械的地址值加 1。2. 指针操作一维数组三种写法背后是同一条路学过指针运算的本质之后再回头看数组很多曾经模糊的地方突然通了。提出问题明明数组和指针是两个东西为什么a[i]和*(p i)能访问到同一个元素它们底层到底有什么关系示例#includestdio.hintmain(){intarr[]{10,20,30,40,50};int*parr;// arr 就是首元素地址 arr[0]// 三种等价写法for(inti0;i5;i){printf(%d ,arr[i]);// 下标法printf(%d ,*(pi));// 指针偏移法printf(%d ,p[i]);// 指针也可以像数组一样用下标}return0;}三种写法输出的结果完全相同10 20 30 40 50。为什么这样数组名arr在表达式中会被隐式转换成指向首元素的指针所以arr和p存的是同一个地址。编译器处理arr[i]时内部就是按*(arr i)来计算的——下标法只是指针偏移的语法糖。反过来指针p也能用p[i]这种写法因为p[i]同样等价于*(p i)。指针遍历数组的几种实际写法// 写法一下标法最直观for(inti0;i5;i){printf(%d ,arr[i]);}// 写法二指针偏移法逐步后移int*parr;for(inti0;i5;i){printf(%d ,*(pi));}// 写法三指针自增法最贴近“遍历”语义int*parr;for(inti0;i5;i){printf(%d ,*p);p;// 每次后移一个 int 元素}第三种写法最常见于实际项目每轮循环打印当前元素然后p让指针指向下一个元素直到遍历完整个数组。一句总结数组名就是首元素地址arr[i]和*(pi)和p[i]底层完全一致选择哪种写法只是习惯问题。3. 指针操作字符串字符指针遍历与 char* 和 char[] 的区别字符串本质上是一个以\0结尾的字符数组用指针操作字符串是 C 语言里非常高频的用法。提出问题给一个字符串怎么用指针遍历打印每个字符char *p和char s[]看起来都能存字符串它们到底有什么区别示例#includestdio.hintmain(){chars[]hello;char*ps;// p 指向 s 的首字符 hwhile(*p!\0){printf(%c,*p);p;// 指针后移一个 char即 1 个字节}printf(\n);return0;}输出hello。每次循环打印当前字符然后p让指针指向下一个字符直到遇见\0终止。为什么这样C 语言字符串以\0ASCII 码 0结尾所以while (*p ! \0)可以自然控制循环结束。p每次偏移sizeof(char) 1字节刚好指向下一个字符。char *p与char s[]的关键区别char *p hello;char s[] hello;本质指针指向字符串常量区数组在栈上开辟空间并拷贝内容可否修改内容❌ 不可改常量区只读修改会崩溃✅ 可改如s[0] H;指针本身可重新指向别的字符串数组名不可重新赋值踩坑实录我曾经写过char *p hello; p[0] H;想改成大写结果程序直接崩溃。原因就是p指向的是字符串常量区那片内存只读不可写。如果你需要可修改的字符串请用char s[] hello;。一句总结字符指针遍历字符串用*p ! \0判断结束char*指向常量区不可修改char[]在栈上可自由修改。4. const 修饰指针离谁近就限定谁const 修饰指针是初学者最容易混淆的点之一四种写法只差一个 const 的位置含义就完全不同。提出问题const char *s、char const *s、char *const s、const char *const s到底怎么区分有没有简单的记忆方法示例与对比写法含义通俗理解const char *s指向的内容不可修改不能通过*s改字符但s可以指向别处char const *s同上const 在*左边就行——const char *s是更常见的写法char const *s也合法但不常用和上一条完全等价char *const s指针本身不可修改s不能指向别处但可以通过*s改内容const char *const s两者都不可修改既不能改指向也不能改内容为什么这样记忆原则只有一句话const 离谁近就限定谁。const挨着char限定的是*s内容不可改。const挨着*s即*const s限定的是s本身指针不可改。实际应用建议函数形参如果只读不写一律用const char *。比如int puts(const char *s);调用者传字符串常量、数组名、const char*变量都能接住函数内部还不会误修改字符串代码更严谨。一句总结const 修饰指针看相对位置挨着类型限定内容挨着指针变量限定指向。5. 今日练习题解用指针思维实战两道题到做练习的时候我才发现光听懂原理不够真正写代码才能暴露理解漏洞。1单词倒置——「how are you」→「you are how」我当时的第一反应拆分单词存数组、反向拼接。但老师说有更简洁的两遍逆序法。正确思路先把整个字符串逆序how are you→uoy era woh再把每个单词单独逆序uoy→youera→arewoh→how整个过程用指针控制区间边界逆序函数只交换首尾字符。注意第一步逆序整个字符串时空格也被当作普通字符参与逆序会从中间被移到两端。不过没关系——后续对每个单词单独逆序时空格会自然归位完整代码#includestdio.h#includestring.h// reverse: 将字符串区间 [start, end) 内的字符逆序// 注意end 指向最后一个字符的下一位置即空格或\0不包含在交换中voidreverse(char*start,char*end){--end;// 让 end 指向最后一个有效字符while(startend){chartmp*start;// 交换首尾字符*start*end;*endtmp;start;end--;}}intmain(){charstr[]how are you;printf(原字符串: %s\n,str);intlenstrlen(str);// 第一步整个字符串逆序不包含 \0reverse(str,strlen);printf(整体逆序: %s\n,str);// 第二步逐个单词逆序char*pstr;while(*p!\0){// 跳过空格while(*p )p;if(*p\0)break;// 已到字符串末尾char*word_startp;// 单词起始位置// 找到单词结束下一个空格或字符串结尾while(*p! *p!\0)p;char*word_endp;// 单词结束位置指向空格或\0// 逆序当前单词reverse(word_start,word_end);}printf(单词逆序后: %s\n,str);return0;}编译运行结果原字符串: how are you 整体逆序: uoy era woh 单词逆序后: you are how最终结果正确。2间隔输出——「Computer Science」→「Cmue cec」思路从首字符开始每次跳过 1 个字符步长 2相当于只取偶数下标的字符。用指针实现就是p初始指向首字符每次p 2完整代码#includestdio.hintmain(){charstr[]Computer Science;char*pstr;printf(原字符串: %s\n,str);printf(间隔输出: );while(*p!\0){putchar(*p);// 打印当前字符p2;// 步长 2跳过下一个字符}printf(\n);return0;}编译运行结果原字符串: Computer Science 间隔输出: Cmue cec直到超出字符串末尾。一句总结指针解决字符串问题的关键是「用指针控制区间」而不是「用下标数位置」代码会更紧凑也更贴近内存思维。今日避坑指南坑现象正确理解p1 以为是地址加 1打印地址发现偏移数值不对偏移量 1 × sizeof(基类型)char *p abc; p[0]A;程序崩溃字面串在常量区不可写用char s[]用比较两个字符串永远不成立或结果不对比的是地址应使用strcmp()const 位置一头雾水四种写法混淆const 离谁近限定谁数组名和指针划等号以为sizeof(s)能拿到字符串长度数组名在多数表达式中会退化为指针但在sizeof和运算中不会退化因此sizeof(数组名)返回整个数组的字节数今日学习感悟今天才真正搞明白指针运算不是在玩地址数字游戏而是编译器在默默帮我按元素大小算偏移。以前记不住const的那几种写法现在用「离谁近就限定谁」这条口诀基本不会搞混了。写单词倒置的时候第一次没考虑\0逆序完打印多了一堆乱码找半天才发现是字符串结束符也被反转了——如果 reverse 函数把\0也纳入交换范围\0就会被移到开头导致字符串被截断。实际实现时reverse 的区间应排除\0只处理有效字符部分。这种低级错误真得亲自动手写过才会长记性。Day12 最大的收获指针不是玄学它是一套有规律可循的内存操作逻辑理解 sizeof 和元素粒度的概念就通了八成。

相关新闻