[C]二级指针
二级指针即“指向指针的指针”;
下面的实例代码创建了一个二级指针c
int a = 5; int* b = &a; int** c = &b;
你不能这样
int a = 5; int** c = &a;
这样你会得到一个类型不兼容的警告
c5.c:16:18: warning: initialization from incompatible pointer type [enabled by default] const int** c = &a; ^
你也可以像这样创建一个二级指针变量,并且分配一些空间给它
char **Pstr_1 = malloc( sizeof( char* ) * 3 );
这里我分配了3个指针长度给Pstr_1,Pstr_1目前指向一个可以存放3个char*类型变量的空间,也就是说它指向的空间可以存放3个指向char的指针
for (; i < 3; ++i) { *( Pstr_1 + i ) = malloc( sizeof( char ) * 3 ); }
你可以这样把每个指针都分配特定的内存区域,现在这3个指针都指向了一个可以存放3个char的内存空间了哦
但是你不能这样做,因为Pstr_1分配的这3个空间只能存放char指针,这是类型问题
for (; i < ARR_LEN; ++i) { *( Pstr_1 + i ) = i; }
关于const修饰符
首先我们来看看指向常量的二级指针
int a = 5; const int* b = &a; const int** c = &a; **c = 6;
上面代码会出现警告,说我们试图更改了一个只读的值
c5.c:17:2: error: assignment of read-only location ¡®**c¡¯ **c = 6; ^
或许你会认为因为b是指向常量的指针,所以导致了这次的警告
int a = 5; int* b = &a; const int** c = &b; **c = 6;
c5.c:16:18: warning: initialization from incompatible pointer type [enabled by default] const int** c = &b; ^
:-),二级指针是不能指向不同类型的指针的哦,一级是const类型,二级也要是const类型,相反二级是const类型,也只能指向是const类型的一级指针
由于这个特性,const类型的二级指针,是修改不了最终指向的值的哦!
int a = 5; int* b = &a; int** c = &b; **c = 6; printf("%d\n", a);
这样就可以完美运行了,**c指向了a,并且修改了a的值
高能时刻
int a = 5; const int* b = &a; const int** c = &b; int d = 6; const int* e = &d; c = &e; printf("%d\n", **c);
它输出了6,全程没有任何警告。
是的,尽管是const的二级指针,它也是可以随时改变指针指向的。
最终回想一下“指向常量的指针”这句话,它适用于1级指针,同样适用于2级指针,你根本不用想太多,因为它最终指向的始终是常量。
同理,这个也适合分配空间的二级指针
const char **Pstr_1 = malloc( sizeof( char* ) * 3 ); int i = 0; for (; i < 3; ++i) { *( Pstr_1 + i ) = malloc( sizeof( char ) * 3 ); *(*( Pstr_1 + i )) = i; }
你得到了一个错误,因为Pstr_1指向的指针*( Pstr_1 + i ),里面的内存区域已经不允许修改了
c5.c:45:3: error: assignment of read-only location ¡®**(Pstr_1 + (sizetype)((long unsigned int)i * 8ul))¡¯ *(*( Pstr_1 + i )) = i; ^
二维数组的指针
我们知道,要在函数中处理数组,一般要给函数传递指针。那么,对于一维int数组a[10],我们可以定义一个int *类型的指针变量p指向该数组。为什么这样定义?(按照我下面的理解方式有利于理解二维数组指针的定义)
首先我们可以把这个一维数组中的10个元素当作10个数组,每个数组都只有一个元素,即指针在每一次移动,都只需指向一个int类型的变量(通过指针对数组进行操作),故定义指向一个int变量的指针。
那么对于二维数组a[3][2],我们要定义一个 int (*p) [2]的指针。首先我们来分析一下这个指针的类型。它也等价于这种形式:int [2] *p。意思是定义一个指向两个int类型变量的指针。当你学了结构体,你就对这种数据类型的定义方式不陌生了。但没有学过结构体,就有点抽象了。我先举个简单的例子:例如我定义一个数组int a[10],其实它也可以表示成另一种形式:int [10] a。意思是定义一个变量a,它是int [10]类型的,即它是一个拥有10个int变量长度的变量,即是数组。那对于二维数组a[3][2],为什么要定义一个指向两个int变量的指针呢?
按照前面对一维数组的分析,我们可以把这个二维数组看作是三个数组,每个数组有两个元素。指向该二维数组的指针在进行移动时,它指向的是一整个数组,即两个int类型,所以需要定义int [2] 类型的指针变量。
由此我们可以总结规律,指向二维数组a[i][j]的指针类型必然是 int [j] *类型。
# include <stdio.h> int main(void) { int a[4][3] = { {1, 2, 12}, {3, 4, 34}, {5, 6, 56}, {7, 8, 78} }; int (*pArr) [3]; //一定要加上括号,因为[]的优先级高于* pArr = a; for (int i = 0; i < 4; i++) for (int j = 0; j < 3; j++) printf("%d ", pArr[i][j]); return 0; }
有人会说难道不能定义一个int**的指针类型吗?那你可能是被动态数组影响到了。其实我前面讲的都是针对于静态数组。对于静态数组,它在内存中所占的空间是连续的,只需要用到地址,所以只要一颗;而动态数组是先建立一个一维数组,然后分别在一维数组的元素内再开辟一段连续的空间,它就需要地址的地址,所以需要两个*。这很重要很重要噢!!!
更多维度也是一样的
char str_2[2][2][3] = { { "ap\0", "br\0", }, { "ca\0", "dr\0", } }; char (*Pstr_3) [2][3] = str_2; i = 0; int j = 0; for (; i < 2; ++i) { j = 0; for (; j < 2; ++j) { printf( "%s\n", *(*( Pstr_3 + i ) + j) ); } }