目录
指针的加减运算
普通整数加减:x + 1,数值直接 + 1。 指针加法不是!
c
int *p;
p = p + 1;
并不是地址值 + 1 字节。
真实规则: 新地址 = 原地址 + 1 × sizeof(*p)
int占 4 字节:p+1,内存地址实际向后偏移4 字节,跳到下一个 int 元素。 char*占 1 字节:p+1地址只 + 1 字节。 double*占 8 字节:p+1地址向后偏移 8 字节。
重点:指针的步长,完全由它指向的数据类型决定,不是指针本身大小。
示例:
c
int arr[] = {0x1111,0x2222,0x3333};
int *p = arr;
假设数组首地址:0x1000
p→0x1000,指向第一个元素p+1→0x1004,指向第二个元素p+2→0x1008,指向第三个元素
合法、非法指针运算汇总
✅允许
- 指针 ± 整数(向前 / 向后移动若干个元素)
- 两个同类型指针相减:得到中间相隔元素个数:
p2 - p1,结果 =(地址差) / sizeof(*p)
❌绝对不允许
- 指针 + 指针:没有意义,编译器报错
- 指针 * / 指针:无意义
c
#include <cstdio>
int main(void)
{
int arr[] = {10,20,30};
int *p = arr;
printf("p =%p\n", p);
printf("p+1 =%p\n", p+1);
printf("p+2 =%p\n", p+2);
char *cp = (char*)arr;
printf("cp =%p\n", cp);
printf("cp+1 =%p\n", cp+1);
return 0;
}
观察:int 指针 + 1 地址跳 4;char 指针 + 1 地址只跳 1。

思考
int* p,p+3,地址实际增加多少字节?- 两个同类型指针相减,得到的是字节差吗?
- 指针可以做指针 + 指针运算吗?
:
1.12
2.不是
3.不可以
:
✅批改:
- int 占 4 字节,
p+3:\(3×4=\boldsymbol{12}\)字节 ✔ - 不是字节差,是元素个数差(地址差 ÷ sizeof (*p))✔
- 指针 + 指针属于非法运算 ✔
指针相减,只限定两个指针指向同一块连续数组内存才有意义。如果两个指针指向完全无关两块内存,相减结果无定义。
指针和数组的核心混淆点
很多人以为数组和指针完全一样,并不是,只是语法可以互相替代,内存本质完全不同。
c
int arr[5] = {1,2,3,4,5};
int *p = arr;
arr 数组名,在大多数表达式里,会隐式转换为首元素的地址。
所以 int *p = arr;
等价于 int *p = &arr[0];。
核心等价公式
arr[i] ≡ ∗(arr+i) ≡ ∗(i+arr) ≡p[i]
arr[2] 完全等价 *(arr+2)
所以 C 语言下标语法本质就是指针运算。
⚠️数组名不是指针变量
-
数组 arr:是一块连续内存的别名,本身不是变量,不能修改
arr = arr + 1;❌ 编译报错!数组名不能被赋值、不能自增arr++。 数组大小编译期确定:sizeof(arr)得到整个数组总字节。 -
指针 p:是独立变量,存地址,可以修改
p = p +1;✅合法,可以改变 p 存的地址;sizeof(p):64 位环境永远等于 8 字节,和数组多大没关系。
表格对比
表格
| 表达式 | int arr[5]数组 | int *p = arr指针 |
|---|---|---|
sizeof() | 20(5×4,整个数组大小) | 8(64 位指针本身大小) |
能否 arr++ / p++ | ❌ arr++ 报错 | ✅ p++ 允许 |
| 存储内容 | 直接存 5 个 int 数据 | 存放数组首元素地址 |
现象↓:
c
#include <cstdio>
int main(void)
{
int arr[5] = {10,20,30,40,50};
int *p = arr;
printf("arr[2] = %d\n", arr[2]);
printf("*(arr+2) = %d\n", *(arr+2));
printf("p[2] = %d\n", p[2]);
printf("sizeof(arr)=%zu\n", sizeof(arr));
printf("sizeof(p)=%zu\n", sizeof(p));
// arr = arr +1; //打开这行直接编译报错
p = p +1;
printf("p移动后 p[0]=%d\n", p[0]);
return 0;
}
运行观察:sizeof(arr)输出 20;sizeof(p)输出 8;arr++语法报错。
函数参数
void func(int arr[]),函数内部的arr不是数组!退化成指针,此时sizeof(arr)得到指针大小,得不到数组长度。

思考
arr[3]使用指针写法等价写成什么?sizeof(arr)和sizeof(p)结果为什么不一样?arr++是否可以编译通过?
:
1.*(arr + 3)
2.一个是整个数组的大小 一个是指针的大小
3.不可以
:
✅
arr[3]等价*(arr+3),甚至*(3+arr)语法同样合法。sizeof(arr)求整个数组占用字节;sizeof(p)求指针变量本身大小,64 位固定 8 字节,和指向数组大小无关。arr++❌编译报错:数组名不是变量,不能被修改。
🔴 当数组作为函数形参:
void test(int arr[]),这里
arr会被编译器直接退化为int* arr。 函数内部sizeof(arr)得到指针大小,拿不到数组真实长度。
字符数组与字符指针
同样写"hello",两种写法内存分布天差地别。
c
char str1[] = "hello";
char *str2 = "hello";
char str1[] = "hello"; 字符数组
"hello"拷贝到栈内存数组里面。- 内存:
h e l l o \0,一共 6 字节。 - ✅内存可读写,可以修改字符:
str1[0]='X';合法。 sizeof(str1)得到 6,数组总字节。
char *str2 = "hello"; 字符指针
"hello"是字符串字面量(常量),存放在只读常量段,不在栈。str2只是一个指针变量(64 位占 8 字节),仅仅保存字符串常量的首地址。- ❌禁止修改:
str2[0]='X';,未定义行为,程序直接崩溃!不能写只读内存。 sizeof(str2)得到 8,指针本身大小,和字符串长度无关。
内存对比表格
表格
| 写法 | 内存位置 | 能否修改内容 | sizeof 结果 |
|---|---|---|---|
char str1[]="hello" | 栈空间 | ✅可修改 | 6 |
char *str2="hello" | 只读常量段 | ❌禁止修改 | 8 |
补充小现象:
c
char *a = "abcd";
char *b = "abcd";
编译器会优化,a和b指向同一块只读常量地址,节约内存。
实操代码,可以 VS 运行:
c
#include <cstdio>
int main(void)
{
char str1[] = "hello";
char *str2 = "hello";
str1[0] = 'M';
printf("%s\n", str1);
// str2[0] = 'M'; //打开这行,程序运行直接崩溃!
printf("sizeof(str1)=%zu\n", sizeof(str1));
printf("sizeof(str2)=%zu\n", sizeof(str2));
return 0;
}
函数传参,如果返回局部字符数组的地址,数组函数结束栈销毁,返回野指针; 但是返回字符串字面量
return "ok";是安全的,因为常量区生命周期是整个程序。

思考
char str[] = "test",sizeof(str)等于多少?char *s = "test";,能否执行s[1]='A'?- 返回局部函数内
char buf[]数组的地址,会产生什么问题?
:
1.5
2.不能
3.出现野指针
:
✅
-
✔
char str[] = "test"字符串末尾自带\0结束符,实际内存:t e s t \0,一共 5 字节。你的答案数值是对。 -
✔不能。
s指向只读字符串常量区,s[1]='A'运行崩溃,未定义行为。 -
✔函数内部局部
char buf[]属于栈上局部变量。函数执行结束,栈内存释放销毁。返回它的地址,得到野指针。后续访问这块内存,内容已经乱掉。
返回局部数组地址是野指针问题。

365

被折叠的 条评论
为什么被折叠?



