指针可以指向一份普通类型的数据,例如 int、double、char 等,也可以指向一份指针类型的数据,例如 int *
、double *
、char *
等。
如果一个指针指向的是另外一个指针,我们就称它为二级指针,或者指向指针的指针。
假设有一个 int 类型的变量 a,p1
是指向 a 的指针变量,p2
又是指向 p1
的指针变量,它们的关系如下图所示:
将这种关系转换为C语言代码:
cpp
int a =100;
int *p1 = &a;
int **p2 = &p1;
指针变量也是一种变量,也会占用存储空间,也可以使用&
获取它的地址。C语言不限制指针的级数,每增加一级指针,在定义指针变量时就得增加一个星号*
。p1
是一级指针,指向普通类型的数据,定义时有一个*
;p2
是二级指针,指向一级指针 p1
,定义时有两个*
。
如果我们希望再定义一个三级指针 p3
,让它指向 p2
,那么可以这样写:
cpp
int ***p3 = &p2;
四级指针也是类似的道理:
cpp
int ****p4 = &p3;
想要获取指针指向的数据时,一级指针加一个*
,二级指针加两个*
,三级指针加三个*
,以此类推,请看代码:
cpp
#include <stdio.h>
int main(){
int a =100;
int *p1 = &a;
int **p2 = &p1;
int ***p3 = &p2;
printf("%d, %d, %d, %d\n", a, *p1, **p2, ***p3);
printf("&p2 = %#X, p3 = %#X\n", &p2, p3);
printf("&p1 = %#X, p2 = %#X, *p3 = %#X\n", &p1, p2, *p3);
printf(" &a = %#X, p1 = %#X, *p2 = %#X, **p3 = %#X\n", &a, p1, *p2, **p3);
return 0;
}
运行结果:
cpp
100, 100, 100, 100
&p2 = 0X28FF3C, p3 = 0X28FF3C
&p1 = 0X28FF40, p2 = 0X28FF40, *p3 = 0X28FF40
&a = 0X28FF44, p1 = 0X28FF44, *p2 = 0X28FF44, **p3 = 0X28FF44
*p3
得到的是 p2
的值,也即 p1
的地址;*(*p3)
得到的是 p1
的值,也即 a
的地址;经过三次"取值"操作后,*(*(*p3))
得到的才是 a
的值。
指针数组
如果一个数组中的所有元素保存的都是指针,那么我们就称它为指针数组。指针数组的定义形式一般为:
cpp
dataType *arrayName[length];
[ ]
的优先级高于*
,该定义形式应该理解为:
dataType *(arrayName[length])
;
括号里面说明 arrayName
是一个数组,包含了 length
个元素,括号外面说明每个元素的类型为 dataType *
。
除了每个元素的数据类型不同,指针数组和普通数组在其他方面都是一样的,下面是一个简单的例子:
cpp
#include <stdio.h>
int main(){
int a = 16, b = 932, c = 100;
//定义一个指针数组
int *arr[3] = {&a, &b, &c};//也可以不指定长度,直接写作 int *arr[]
//定义一个指向指针数组的指针
int **parr = arr;
printf("%d, %d, %d\n", *arr[0], *arr[1], *arr[2]);
printf("%d, %d, %d\n", **(parr+0), **(parr+1), **(parr+2));
return 0;
}
运行结果:
cpp
16, 932, 100
16, 932, 100
arr
是一个指针数组,它包含了 3 个元素,每个元素都是一个指针,在定义 arr
的同时,我们使用变量 a、b、c 的地址对它进行了初始化,这和普通数组是多么地类似。
parr
是指向数组 arr
的指针,确切地说是指向 arr
第 0 个元素的指针,它的定义形式应该理解为 int *(*parr)
,括号中的 *
表示 parr
是一个指针,括号外面的 int *
表示 parr
指向的数据的类型。arr
第 0 个元素的类型为 int *
,所以在定义 parr
时要加两个 *
。
第一个 printf()
语句中,arr[i]
表示获取第 i
个元素的值,该元素是一个指针,还需要在前面增加一个 *
才能取得它指向的数据,也即 *arr[i]
的形式。
第二个printf()
语句中,parr+i
表示第 i
个元素的地址,*(parr+i)
表示获取第 i
个元素的值(该元素是一个指针),**(parr+i)
表示获取第 i
个元素指向的数据。
指针数组还可以和字符串数组结合使用,请看下面的例子:
cpp
#include <stdio.h>
int main(){
char *str[3] = {
"c.biancheng.net",
"C语言中文网",
"C Language"
};
printf("%s\n%s\n%s\n", str[0], str[1], str[2]);
return 0;
}
运行结果:
cpp
c.biancheng.net
C语言中文网
C Language
需要注意的是,字符数组 str
中存放的是字符串的首地址,不是字符串本身,字符串本身位于其他的内存区域,和字符数组是分开的。
也只有当指针数组中每个元素的类型都是 char *
时,才能像上面那样给指针数组赋值,其他类型不行。
为了便于理解,可以将上面的字符串数组改成下面的形式,它们都是等价的。
cpp
#include <stdio.h>
int main(){
char *str0 = "c.biancheng.net";
char *str1 = "C语言中文网";
char *str2 = "C Language";
char *str[3] = {str0, str1, str2};
printf("%s\n%s\n%s\n", str[0], str[1], str[2]);
return 0;
}
当涉及到多维数据结构时,C语言提供了一维、二维和三维指针来处理这些数据。指针是一种特殊类型的变量,它存储了内存地址,可以用于访问和操作内存中的数据。本文将详细介绍C语言中一维指针、二维指针和三维指针的内存结构,并提供相应的C语言示例来加深理解。
一维指针
一维指针是处理一维数组的重要工具。它存储数组的首个元素的内存地址,并可用于遍历整个数组。一维数组在内存中是连续存储的,因此一维指针可以按顺序访问数组的各个元素。
cpp
int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr; // 定义一维指针并指向数组的首个元素
for (int i = 0; i < 5; i++) {
printf("%d ", *(ptr + i));
}
上述示例中,我们定义了一个包含5个元素的整型数组arr
,然后定义了一个指向 arr
的一维指针 ptr
。通过指针ptr
,我们使用指针算术运算来遍历数组,并使用间接引用运算符*
访问每个元素的值。
二维指针
二维指针用于处理二维数组,它存储了二维数组每个元素的内存地址。二维数组在内存中以行优先的方式存储,即每一行的元素是连续存储的。二维指针可以通过指向每个一维数组的指针来访问和操作二维数组的元素。
cpp
int matrix[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
int **ptr = (int **)malloc(3 * sizeof(int *));
for (int i = 0; i < 3; i++) {
ptr[i] = matrix[i];
}
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
printf("%d ", *(*(ptr + i) + j));
}
printf("\n");
}
free(ptr);
在上述示例中,我们定义了一个包含3行3列的二维整型数组matrix
。然后,我们使用二维指针 ptr
动态分配了3个指针的内存空间,并将每一行的首地址赋给二维指针。通过二维指针 ptr
,我们使用指针算术运算来访问和操作二维数组的元素。
三维指针
三维指针用于处理三维数组,它存储了三维数组每个元素的内存地址。三维数组在内存中的存储方式比较复杂,它需要使用多级指针来表示。通过多级指针,我们可以访问和操作三维数组的元素。
cpp
int cube[2][2][2] = {{{1, 2}, {3, 4}}, {{5, 6}, {7, 8}}};
int ***ptr = (int ***)malloc(2 * sizeof(int **));
for (int i = 0; i < 2; i++) {
ptr[i] = (int **)malloc(2 * sizeof(int *));
for (int j = 0; j < 2; j++) {
ptr[i][j] = (int *)malloc(2 * sizeof(int));
for (int k = 0; k < 2; k++) {
ptr[i][j][k] = cube[i][j][k];
}
}
}
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
for (int k = 0; k < 2; k++) {
printf("%d ", *(*(*(ptr + i) + j) + k));
}
printf("\n");
}
printf("\n");
}
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
free(ptr[i][j]);
}
free(ptr[i]);
}
free(ptr);
在上述示例中,我们定义了一个包含2个2行2列的三维整型数组cube
。然后,我们使用三维指针 ptr
动态分配了对应的内存空间,并将每个元素的值赋给三维指针。通过三维指针 ptr
,我们使用指针算术运算来访问和操作三维数组的元素。