1 二重指针
二重指针的C语言表示:int **p
,它表示指针的指针。具体什么用途呢?先看一个例子
以下是经典程序(载自林锐的从c/c++高质量编程)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #include <stdio.h> #include <stdlib.h> #include <string.h>
void GetMemory(char *p,int num) { p=(char*)malloc(sizeof(char)*num); } void main() { char *str=NULL; GetMemory(str,100); strcpy(str,"hello");
printf("str=%s\n",str); }
|
结构是编译能通过,却不能运行,如下
1 2 3 4 5 6
| zmurder@zmurder:~/WorkSpace/zyd/test/C C++$ gcc -o testPoint testPoint.c zmurder@zmurder:~/WorkSpace/zyd/test/C C++$ ls testPoint testPoint.c zmurder@zmurder:~/WorkSpace/zyd/test/C C++$ ./testPoint Segmentation fault
|
解释一下为什么上面程序不能运行:首先 str
是一个指针,指向一个地址,假如 str
指向的地址是 0x0001
,那么 p
指向的地址也是 0x0001
。但是 str
和 p
自身的地址是不一致的,因为函数是值传递,拷贝了一个副本。因此采用上面的内存分配方式,当分配好新的内存之后,p
指向的已经是新内存的首地址,而 str
依然是指向 0x0001
。
对上面的程序进行修改如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #include <stdio.h> #include <stdlib.h> #include <string.h>
void GetMemory(char **p,int num) { *p=(char*)malloc(sizeof(char)*num); } void main() { char *str=NULL; GetMemory(&str,100); strcpy(str,"hello"); printf("str=%s\n",str); free(str); }
|
相当于p是指向 str
的地址的指针,因此 *p = str
,给 *p
分配内存,相当于让 str
指向新内存的首地址。
编译运行如下
1 2 3 4
| zmurder@zmurder:~/WorkSpace/zyd/test/C C++$ gcc -o testPoint2 testPoint2.c zmurder@zmurder:~/WorkSpace/zyd/test/C C++$ ./testPoint2 str=hello zmurder@zmurder:~/WorkSpace/zyd/test/C C++$
|
一般指针的指针用作参数,大多用在需要函数改变指针(重新引用变量)而又不能通过返回值传递(例如返回值用于传递其他结果)时。
例如:
void * malloc(size_t size)
函数返回的就是一个指针。调用后有返回值,直接修改了原变量(内容是地址)。char * p ; p=malloc(size);
- 自己写一个函数
void GetMemory(void ** p ,size_t size)
。返回值是空的,但是想修改一个变量的地址(注意修改的是地址)。只能将变量的地址的地址传进去 char ** p ; GetMemory((void**)(&p),size);
。
再看一个例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
|
#include <stdio.h> #include<stdlib.h> #include <string.h> void P1Malloc(char* p,int num) { p=malloc(num); printf("P1Malloc,current malloc address p:%p\n",p); } void P2Malloc(char** p,int num) { *p=malloc(num); printf("P2Malloc,current malloc address *p:%p\n",*p); }
char* P3Malloc(char* p,int num) { p=malloc(num); printf("P3Malloc,current malloc address p:%p\n",p); return p; }
int main() { int Num=10; char* a=NULL;
printf("initial pointer a:%p\n",a);
P1Malloc(a,Num); printf("after using *,ponter a:%p\n",a);
P2Malloc(&a,Num); printf("after using **,ponter a:%p\n",a); strcpy(a,"hello"); free(a);
a = P3Malloc(a,Num); printf("after P3Malloc,ponter a:%p\n",a); strcpy(a,"hello"); free(a);
return 0; }
|
编译运行如下
1 2 3 4 5 6 7 8 9
| zmurder@zmurder:~/WorkSpace/zyd/test/C C++$ gcc -o testPoint3 testPoint3.c zmurder@zmurder:~/WorkSpace/zyd/test/C C++$ ./testPoint3 initial pointer a:(nil) P1Malloc,current malloc address p:0x555b5a5c06b0 after using *,ponter a:(nil) P2Malloc,current malloc address *p:0x555b5a5c06d0 after using **,ponter a:0x555b5a5c06d0 P3Malloc,current malloc address p:0x555b5a5c06d0 after P3Malloc,ponter a:0x555b5a5c06d0
|
分析:
- 原始的a的是nil(注意
a
是一个指针变量,也就是一个变量,存的是地址)
- P1Malloc函数中
p
的地址是0x555b5a5c06b0,并没有改变 a
。
- P2Malloc函数中
*p
的地址是0x555b5a5c06d0,改变了 a
,也是0x555b5a5c06d0
- P3Malloc函数中
p
的地址是0x555b5a5c06d0,函数返回后赋值给 a
,因次 a
改变为0x555b5a5c06d0
2 指针数组与数组指针
- 指针数组:指针数组可以说成是”指针的数组”,首先这个变量是一个数组,其次,”指针”修饰这个数组,意思是说这个数组的所有元素都是指针类型,在32位系统中,指针占四个字节。
- 数组指针:数组指针可以说成是”数组的指针”,首先这个变量是一个指针,其次,”数组”修饰这个指针,意思是说这个指针存放着一个数组的首地址,或者说这个指针指向一个数组的首地址。
2.1指针数组
首先先定义一个指针数组,既然是数组,名字就叫arr。(注意优先级:()>[]> *)
1 2 3
| char *arr[4] = {"hello", "world", "shannxi", "xian"};
|
这个指针数组是16个字节,因为它是一个指针数组。
相当与定义 char *p1 = “hello”,char *p1 = “world”,char *p3 = “shannxi”, char *p4 = “xian”
,这是四个指针,每个指针存放一个字符串首地址,然后用 arr[4]
这个数组分别存放这四个指针,就形成了指针数组。
2.2数组指针
首先来定义一个数组指针,既然是指针,名字就叫pa
既然pa是一个指针,存放一个数组的地址,那么在我们定义一个数组时,数组名称就是这个数组的首地址,那么这二者有什么区别和联系呢?
- a是数组首元素首地址,a是char 类型,a+1,地址加1
- pa存放的却是数组首地址,而pa是char[4]类型的,pa+1,地址加4
2.3 指针数组使用
指针数组常用在 函数传参
1
| int main(int argc, char *argv[])
|
1 2
| void fun(char **pp); fun(char *p[]);
|
下面是一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include <stdio.h> #include <stdlib.h> #include <string.h>
void change(char **ptr) { ptr[2] = "student"; } void main() { char *buf[4] = { "hello","world","welcome","profect" }; char **ptr = buf; int i = 0; change(buf); for (i = 0; i < 4; i++) { printf("%s\n", *(ptr +i)); }
}
|
运行结果如下
1 2 3 4 5
| $ ./testPointArray hello world student profect
|
2.4 数组指针的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include<stdio.h> #include<stdlib.h> void main() { int a[3][4]={{1,2,3,4},{11,12,13,14},{21,22,23,24}}; int (*p)[4]; p=a; int i=0; while(i<3) {
printf("%d\t",(*p++)[0]); i++; } }
|
附录:
指针的指针作用(申请空间)之一
【CUDA】分配内存使用void**
CUDA 中的 cudaMalloc使用二重指针(void**)的一些理解
数组指针的用法,用处。
6.指针数组做函数参数