以下是经典程序(载自林锐的从c\c++高质量编程),讲解的部分是我个人理解
void GetMemory(char *p,int num)
{
p=(char*)malloc(sizeof(char)*num); //p是形参指向的地址
}
void main()
{
char *str=NULL;
GetMemory(str,100); //str是实参指向的地址,不能通过调用函数来申请内存
strcpy(str,"hello");
}
结构是编译能通过,却不能运行,为什么呢?
先说一下指针作为函数参数的意义:当将指针作为参数时,实参向形参传递的是地址,在函数执行过程中,既可以对该参数指针进行处理,也可以对该参数指针所指向的数据进行处理,(以上程序段来说就是可以对p或*p进行处理)。由于此时形参和实参都是指向同一个存储单元,因此当形参指针所指向的数据改变时,实参指针所指向的数据也作相应的改变,因此这时的形参可以作为输出参数使用。
按照上面的说法,这个程序应该没有问题的啊,实参str和形参p指向同一个存储单元,给形参分配的内存单元应该也给实参分配了才对啊,问题就是在这里
实参和形参是指向同一个地址,它们只是指向相同(实参与形参是值传递),但它们自身的地址不是同时申请的,就是说p在申请内存时,str并不可以通过调用Getmemory同时申请内存,所以尽管str调用了GetMemory,但它仍然是个空指针,所以进行strcpy是就不能运行。
要使程序可以运行,只要小小的改动就行了(用指向指针的指针):
void GetMemory(char **p,int num)
{
*p=(char*)malloc(sizeof(char)*num); //此时*p就变成了是形参本身的地址
}
void main()
{
char *str=NULL;
GetMemory(&str,100); //&str是实参的地址,所以实参和形参之间就可以直接调用
strcpy(str,"hello");
cout<<>free(str);
就是说调用函数后,&str赋值给char**p了,这个时候对*p分配堆内存,就相当于给str分配堆内存 (str本身也是一个指针)
p---->
第一次传递的
str-->
p--->
第二次传递*p(相当于第一次的p)----真正的值
str-->
光标题就够让人胆怯的了,又是指针?还指向指针?还指向数组?
要弄清这个问题,还得从指针的本质说起。
每次提到指针,我总是要反复提起“指针也是一个变量”,当你对指针本身进行操作时,就是对一个变量进行操作。指针唯一的特点是:它的值是一个内存地址,通过它可以间接寻找到其它变量(或常量)。
如果光通过它寻找其它变量常量,那倒也不必这么复杂。关键是,找来的数据要进行处理的话,必须知道那个数据的类型。大家都知道int和long是不一样的,那么,指向这两种类型变量的指针,它们的区别在哪儿呢?
应该说,在本质上它们没有区别,它们都是指向内存地址。内存除了地址区别以外,没有其它区别。同一个内存地址,可以放int,可以放long,还可以放char。为了保证对每一个变量的操作都符合要求,C++惟一的对策就是在声明指针的时候就规定了这个指针所指的类型。如“int *p”,int只是表示了p所指对象的类型,而不表示p的类型。
C++这样做还有另一个好处,就是方便直观地移动指针。对于加、减运算来说,许多人都早已烂熟于心,但是对于指针的加减运算,于其说加减,还不如说前移和后移。++p就是让p指向后面一个对象。至于p的值究竟加了多少?这可不一定。这要看p指向的对象占内存大小了。
更直观地说,如果p是指向char的,那么++p就是让p的值增1,如果p是指向long的,可就是增4了(某些系统用8字节表示long,那就增8)。
下面该提到“指向指针的指针”了,对于int **p来说,p所指的对象是指针,那么++p就是让p的值增一个指针的大小,虽然指针所指对像可大可小,但是指针自己的大小却是一定的,目前的系统上一般是4字节(64位系统我没用过,可能是8字节)。也就是说,对char *p和long *q来说,p和q本身占内存容量是一样大的(虽然char和long占内存不一样大)。
很多人以为“指向数组的指针”就是“指向指针的指针”,于是有人写这样的代码:
int a[3][4];
int **p = a;//错误
这个代码的错误之处在于a是一个数组,它的成员也是数组,所以a叫做“数组的数组”——C++中严格说来没有二维数组。那么,你要用一个指针来记录a,就要用一个能表示“数组的数组”的指针,以下代码是正确的:
int (*p)[4] = a;//正确
只有这样才能保证++p使p指向a的下一对像(该对像是一个数组)。
顺便提一句:不要写成“int *p[4];”

没有评论:
发表评论