Mobile wallpaper 1Mobile wallpaper 2Mobile wallpaper 3Mobile wallpaper 4Mobile wallpaper 5Mobile wallpaper 6Mobile wallpaper 7Mobile wallpaper 8
1507 字
8 分钟
【C语言】(指针篇)指针的运算和遍历
2025-12-21

一、指针的运算#

(一)指针+(-)整数#

指针加减整数代表的是指针在内存上的移动,结果也是指针。这个时候我们可以观察p+1*(p+1)的变化。
我们以数组举例,只需要知道其中一个元素的地址,就可以顺藤摸瓜找到所有元素的地址。

#include<stdio.h>
int main() {
int a[10] = { 1,2,3,4,5,6,7 };
int sz = sizeof(a) / sizeof(a[0]);
int* p = a;//这里的a指的是a的首地址的地址,等价于&a[0]
for (int i = 0; i < sz - 1; i++) {
p++;
printf("%p %d\n", p, *p);
}
return 0;
}
>>>0000001F5C4FF86C 2
0000001F5C4FF870 3
0000001F5C4FF874 4
0000001F5C4FF878 5
0000001F5C4FF87C 6
0000001F5C4FF880 7
0000001F5C4FF884 0
0000001F5C4FF888 0
0000001F5C4FF88C 0

需要标注的是,数组名(如:a)单独出现的时候,代表的是数组首元素的地址,也就是&a[0],对于二维数组也是如此,之后会细说,这里不做展开。
可以发现,p++一次,地址往后移动了4个字节,这是因为int类型的变量占用4个字节,因此地址往后四个字节才会找到下一个元素。换成char*的指针的话,因为char类型的变量只占用一个字节,因此地址只会往后一个字节。
这就是为什么明明在内存上都占用4/8个字节,但是却要分出指针类型的原因。

(二)指针-指针#

前面我们知道指针是可以加减整数的,并且结果也是指针,用表达式来看的话就是p1 + n = p2,那么自然的,这样的运算是否可逆?
结论是可以的,我们可以反向得到p2 - p1 = n,其中nint类型变量。

#include<stdio.h>
int main() {
int a[10] = { 1,2,3,4,5,6,7 };
printf("%d\n", &a[9] - &a[0]);
return 0;
}
>>> 9

得到的9就是从a[0]到a[9],指针扫过的元素个数。下面我用图像辅助解释一下:指针的运算

p在&a[0]时,p + 1的时候,指针停在&a[1]上,经过了a[0]这一个元素,因此(p + 1) - 1 = p,同理,p+9时到达&a[9],(p + 9) - p = 9

(三)指针的关系运算#

加减法存在那么自然也存在大小比较,比如我们可以对前面的代码进行改编:

#include<stdio.h>
int main() {
int a[10] = { 1,2,3,4,5,6,7 };
int sz = sizeof(a) / sizeof(a[0]);
for (int* p = a; p < a + sz ; p++) {
printf("%p %d\n", p, *p);
}
return 0;
}
>>>00000028C70FFBC8 1
00000028C70FFBCC 2
00000028C70FFBD0 3
00000028C70FFBD4 4
00000028C70FFBD8 5
00000028C70FFBDC 6
00000028C70FFBE0 7
00000028C70FFBE4 0
00000028C70FFBE8 0
00000028C70FFBEC 0

其中就涉及了关系运算p < arr + sz

(四)指针+-整数的等价形式#

如果我们对尝试这样的代码:

#include<stdio.h>
int main() {
int a[10] = { 1,2,3,4,5,6,7 };
int sz = sizeof(a) / sizeof(a[0]);
for (int i = 0; i < sz ; i++) {
printf("%d %d\n", *(a + i), a[i]);
}
return 0;
}
>>>
>1 1
2 2
3 3
4 4
5 5
6 6
7 7
0 0
0 0
0 0

可以发现*(a + i)a[i]是等价的,也就是说,借用解引用操作符*和指针的运算,我们可以用指针代替数组的下标访问!
再进一步,我们甚至可以得出这样的等价式:*(p + i) = p[i] = *(i + p) = i[p]
再用取地址操作符我们还可以得到一组恒等式:p + i = &p[i] = i + p = &i[p]
i[p]可读性太差了之后不会出现,这里只是在说明下标访问和地址访问的等价性。

二、指针的遍历:#

有了基础的运算,现在我们就可以用指针访问数组了。

一维数组#

#include<stdio.h>
int main() {
int arr[10];
int sz = sizeof(arr) / sizeof(arr[0]);
int* p = arr;
for (int i = 0; i < sz; i++) {
scanf("%d", p + i);//也可以写成arr + i
printf("%d ", *(p + i) + 1);
}
return 0;
}
>>>1 2 3 4 5 6 7 8 9 10
2 3 4 5 6 7 8 9 10 11

二维数组#

二维数组也是一样的,但是我们要注意一下拆解的顺序。

#include<stdio.h>
#include<stdio.h>
int main() {
int arr[3][3];
int r = sizeof(arr) / sizeof(arr[0]);
int c = sizeof(arr[0]) / sizeof(arr[0][0]);
int (*p)[3] = arr;//&arr[0],也就是arr的第一行的地址
for (int i = 0; i < r; i++) {
for (int j = 0; j < c; j++) {
scanf("%d", *(p + i) + j);//p + i为第i行的地址,p + i + j代表第i行第j列
printf("%d ", arr[i][j]);
}
}
return 0;
}
>>>
>1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 9

在解释代码之前补充几个知识点:
1.此时p是数组指针,类型已经不再是int*,而是int (*)[3],表示的是指针指向的数组有三个元素
2.&arrarr虽然都是数组首元素的地址,但是&arr表示整个数组的地址,因此&arr + 1就会跳过整个数组,对于一维数组arr[9],&arr + 1就会跳过9个元素直接到数组外面,arr + 1则是指向第二个元素。
我们对*(*(p + i)+j)进行分解,p代表第一行的地址,也就等价于&a[0],a[0]是一维数组,因此*(p + 1)就会跳过a[0]整个数组,得到&a[1]的地址并解引用得到a[1],a[1]就是&a[1][0],同理,*(p + i) 得到的就是第i行的数组的首元素地址,*(*(p + i)+j)就是在第i行数组中访问第j列。
倒退的话,arr[i][j] = *(arr[i] + j) = *(*(&arr[0] + i) + j) = *(*(p + i)+j)

数组指针转换#

我们也可以用一些歪门邪道(划掉)来把二维数组拍扁成一维数组。
由于数组是连续存放的,我们可以通过强制类型转换,把类型为int (*)[3]的p强行转换成int*。于是我们就可以用地址*(p + i * r + j)对第i行第j列的元素进行访问和修改。二维数组的存放

【C语言】(指针篇)指针的运算和遍历
https://blog.csdn.net/2501_93882415/article/details/156115036?spm=1001.2014.3001.5502
作者
Mem0rin
发布于
2025-12-21
许可协议
MIT

部分信息可能已经过时

封面
Sample Song
Sample Artist
封面
Sample Song
Sample Artist
0:00 / 0:00