1. 指针的指针
在C语言中,指针用于存储变量的地址。如果我们另一个指针去指向这个指针的地址,这样的指针叫做“指针的指针”。我们可以对指针进行取地址运算,看看是否能编译通过。
```
#include <stdio.h>
int main() {
int n = 123;
int *pn = &n; // pn 是指向 n 的指针
printf("pn = %u\n", pn); // 打印 pn 的值,指向 n 的地址
printf("&pn = %u\n", &pn); // 打印 pn 的地址
return 0;
}
```
如上代码可以成功编译并运行。`pn` 是一个指针,指向变量 `n`,而 `&pn` 是 `pn` 的地址,类型是指向指针的指针。
2. 指针的类型
指针的指针应该是什么类型呢?通过在原指针类型前再加一个星号,我们可以得出结论:
```
int **p; // p 是指向 int * 类型的指针
```
需要注意,指针变量的声明中,类型和变量名之间的空格并不是严格的,比如以下声明都是正确的:
```
int *p; // 正确
int*p; // 正确
int* p; // 正确
int **p; // 正确(对于指针的指针)
```
因此,`int **p` 表示 `p` 是一个指向 `int *` 类型的指针。
3. 多级指针
`int **` 是指向 `int *` 类型数据的指针,称为二级指针。我们可以用取值运算符 `*` 来访问指针指向的值。
```
#include <stdio.h>
int main() {
int n = 123;
int *pn = &n; // pn 是指向 n 的指针
int **pnn = &pn; // pnn 是指向 pn 的指针
printf("**pnn = %d\n", **pnn); // 获取 pnn 指向的值,即 n 的值
return 0;
}
```
深入理解:
取地址的过程如下:
- 对 `n` 使用取地址运算符,得到 `pn`,类型为 `int *`。
- 对 `pn` 使用取地址运算符,得到 `pnn`,类型为 `int **`。
取值过程:
- 对 `*pnn` 使用取值运算符,将其还原为 `int *`。
- 再对 `*(*pnn)` 使用取值运算符,还原为 `int`,即变量 `n` 的值。
为了展示多级指针的概念,可以这样扩展:
```
#include <stdio.h>
int main() {
int n = 123;
int *oneStar = &n; // int *
int **twoStar = &oneStar; // int **
int ***threeStar = &twoStar; // int ***
// 继续扩展更多级
printf("n = %d\n", *****fiveStar); // 多次解引用
return 0;
}
```
4. 指针数组
指针数组是一个元素为指针的数组。先来看一个整型数组的例子:
```
int arr1[5] = {1, 2, 3, 4, 5};
int arr2[5] = {11, 22, 33, 44, 55};
int arr3[5] = {111, 222, 333, 444, 555};
```
这些常规数组的名称会被转换为指向其首元素的指针。我们定义一个指针数组来存储这些指针:
```
int *pToArr[3];
pToArr[0] = arr1; // arr1转换为首元素指针
pToArr[1] = arr2; // arr2转换为首元素指针
pToArr[2] = arr3; // arr3转换为首元素指针
```
在这个例子中,`pToArr` 是一个指针数组,类型为 `int *[3]`,包含三个 `int *` 类型的元素,分别指向 `arr1`、`arr2` 和 `arr3`。
遍历指针数组
你可以使用嵌套循环来遍历指针数组中的所有元素:
```
for(int i = 0; i < 3; i++) {
int *p = pToArr[i]; // 指向第 i 个数组的首元素
for(int j = 0; j < 5; j++) {
printf("%d ", *(p + j)); // 打印元素值
}
printf("\n");
}
```
5. 从函数中返回指针
我们可以让函数返回一个指针。示例代码如下:
```
#include <stdio.h>
int* func() {
int n = 100; // 局部变量
return &n; // 返回指向局部变量 n 的指针
}
int main() {
int* p = func(); // p 现在指向 func 内部的 n
printf("%d\n", *p); // 潜在问题:访问失效的内存
return 0;
}
```
这个例子看似没问题,但因为变量 `n` 在函数 `func` 执行完毕后被回收,我们无法安全地访问它。
解决方法:
我们可以使用 `static` 关键字,让变量在函数执行完毕后不会被回收:
```
int* func() {
static int n = 100; // n 不会被回收
n++; // 每次调用时自增
return &n; // 返回 n 的地址
}
```
这样,在每次调用 `func` 后,变量 `n` 会保持其状态并自增。
6. 从函数中返回多个变量
要从函数返回多个值,可以将指针作为参数传递。示例代码如下:
```
#include <stdio.h>
void func(int **a, int **b) {
static int x = 100;
static int y = 200;
*a = &x; // 将 x 的地址赋给指针
*b = &y; // 将 y 的地址赋给指针
}
int main() {
int *a = NULL;
int *b = NULL;
func(&a, &b); // 将指针的指针传入
if (a != NULL && b != NULL) {
printf("a=%d b=%d\n", *a, *b); // 打印 x 和 y 的值
}
return 0;
}
```
在这个代码中,我们通过函数 `func` 来修改主调函数中的指针 `a` 和 `b`。
总之,使用指针,可以有效地管理和共享变量的地址,使函数之间的变量传递更为灵活。希望这些示例能帮你进一步理解多级指针与指针数组的概念。