数组和指针
1. 一维数组和指针
先回忆一下,数组是由一系列类型相同的元素组成。如:
char ch[4]; /*4个字符的数组*/ int in[4]; /*4个整数的数组*/ float fl[4]; /*4个浮点数的数组*/ ...
那么当一个指针变量被初始化成数组名时。如:
int a[10]; int *p = a;
因为数组名a代表数组首元素a[0]的地址,所以指针p实际指向了数组首元素a[0]。所以下面等式成立:
p == a; p == &a[0];
a和&a[0]都代表首元素的内存地址,两者都是常量。当把它们赋给指针变量时,可以修改指针变量。需要注意的是给指针加上一个数时,它的值发生了什么变化:
#include <stdio.h> #define SIZE 5 int main(void) { char ch[SIZE] = {'h', 'e', 'l', 'l', 'o'}; char *pch; int arr[SIZE] = {1, 2, 3, 4, 5}; int *parr; int i; pch = ch; parr = arr; printf("%28s %14s\n", "char", "int"); for(i = 0; i < SIZE; ++i) { printf("pointers + %d: %10p %10p\n", i, pch + i, parr + i); } return 0; }
输出结果:
对指针加1的结果是对该指针增加1个存储单元。对于数组而言,地址会增加到下一个元素的地址,而不是下一个字节。
此外,数组在作为参数传递给函数形参时,会退化成指针:
#include <stdio.h> #define SIZE 5 void foo(int *a, int len) { printf("pointer's size: %d\n", sizeof(a)); printf("arr's lenght: %d\n", len); } int main(void) { int arr[SIZE] = {1, 2, 3, 4, 5}; int *p = arr; foo(arr, SIZE); return 0; }
输出结果:
2. 多维数组和指针
指针和多维数组有什么关系?为什么需要知道他们之间的关系?函数是通过指针来处理多维数组的,因此使用这样的函数之前,需要更多地了解指针。
为简化讨论,先采用比较小的数组:
int a[4][2]; /*整数数组的数组*/
数组名a是数组首元素的地址,a的首元素本身又是包含两个int的数组,因此a也是包含两个int的数组的地址。下面从指针熟悉进一步分析:
- 因为a是数组首元素的地址,所以a的值和&a[0]相同。另一方面a[0]本身是包含两个整数的数组,因此a[0]的值同其首元素的地址&a[0][0]相同。简单地说,a[0]是一个整数大小对象的地址,而a是两个整数大小对象的地址。因为整数和两个整数组成的数组开始于同一个地址,因为a和a[0]具有相同的数组。
- 对一个指针加1,会对原来的数值加上一个对应类型大小的数值。在这方面a和a[0]是不一样的,a所指向对象的大小是两个int,而a[0]所指向对象的大小是一个int,因此a+1和a[0]+1的结果不同。
- 对于一个指针取值等到的是该指针所指向对象的数值。因为a[0]是其首元素a[0][0]的地址,所以*(a[0])代表存储在a[0][0]中的数值,即一个int数值。同样*a代表其首元素a[0]的值,但是a[0]本身就是int数的地址,即&a[0][0],因为*a是&a[0][0]。对这两个表达式同时应用取值运算符将等到**a等价于*&a[0][0],后者简化后即为一个int数a[0][0]。简言之,a是地址的地址,需要两次取值才可以得到通常的数值。
显然,增加数组维数会增加指针的复杂度。下面举例说明:
include <stdio.h> int main(void) { int a[4][2] = {{1, 5}, {2, 6}, {3, 7}, {4, 8}}; int (*p)[2]; p = a; printf(" p = %p, p + 1 = %p\n", p, p + 1); printf("p[0] = %p, p[0] + 1 = %p\n", p[0], p[0] + 1); printf(" *p = %p, *p + 1 = %p\n", *p, *p + 1); printf(" p[0][0] = %d\n", p[0][0]); printf(" *p[0] = %d\n", *p[0]); printf(" **p = %d\n", **p); printf(" p[3][1] = %d\n", p[3][1]); printf("*(*(p + 3) + 1) = %d\n", *(*(p + 3) + 1)); return 0; }
输出结果:
这里使用指针符号来显示数据的意图并不是为了说明可以用它替代更简单的a[3][1]表达式,而是要说明如果正好有一个指向二维数组的指针并需要取值时,最好不要使用指针符号,而应当使用形式更简单的数组符号。
下面以另一种视图显示了数组地址、数组内容和指针之间的关系:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通