Chinaunix首页 | 论坛 | 博客
  • 博客访问: 603761
  • 博文数量: 99
  • 博客积分: 5128
  • 博客等级: 大校
  • 技术积分: 1538
  • 用 户 组: 普通用户
  • 注册时间: 2007-10-27 19:40
文章分类

全部博文(99)

文章存档

2012年(3)

2011年(5)

2010年(4)

2009年(31)

2008年(56)

分类: C/C++

2009-04-07 11:45:12

前些日子网上一个朋友问了我一个问题,据说是一个面试题
 
 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void Getmemery(char *p)
{
    p=(char *)malloc(100);
}

void main()
{
    char *str=NULL;

    Getmemery(str);
    strcpy(str,"hello world");
    printf("%s",str);
    free(str);
}

编译没有问题的,但是为什么执行的时候会错误退出呢?根据现象,很明显是指针的非法访问了,但是问题在哪里,我一时也蒙了,或者说被他的思路给引导到了一个错误的方向。

当然,最简单的解决办法是返回一个指针就没有问题,稍微修改一下:

 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char *Getmemery(void)
{
    char *p=(char *)malloc(100);

    return p;
}

void main()
{
    char *str=NULL;

    str = Getmemery();
    strcpy(str,"hello world");
    printf("%s",str);
    free(str);
}

这样代码执行的很正常,这种风格也是很常用的,也就作罢。

 

但是为什么第一个程序不正确?理论上,按照理论上分析是“正确”的,心里一直有个结,解不开这个题目,心里总是有点不舒服。

直到今天在ouravr上面看到有个关于 typedef 用法的帖子,突然引起了我的兴趣,自己关于指针的,特别是二维数组和数组指针并不是理解的很透侧,于是就趁此机会好好的复习一下二维数组。

最后终于将之前的一个问题想明白了。正确的代码如下

 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void Getmemery(void **p)
{
    *p=(void **)malloc(100);
}

void main()
{
    char *str=NULL;

    Getmemery(&str);
    strcpy(str,"hello world");
    printf("%s",str);
    free(str);
}

我们应该使用的是二重指针来完成任务。

分析一下之前的程序为什么会错了。

(1)void Getmemery(char *p)

(2)char *str=NULL;

(3)Getmemery(str);

1中子程序的形参是一个指针,然后很自然会想到2,3中的调用方式,本来的想法是用malloc分配内存,然后修改传入的指针变量,那么最后就根据通过 strcpy(str,"hello world"); 就可以向分配的内存里面写数据了。一切都是那样流畅,对,因为这个用法平时用习惯了,所以根本不会去考虑正确性。

然而,这里就出问题了。首先,Getmemery(str) 传递的是 srt指针的地址,这个没有问题,C不同于C++,参数是通过传递的,而不是通过引用。也就是说,实际参数 srt 先自己copy一份,然后传递给形式参数 *P接收,这个C语言的指针的时候已经强调多次了,但是自己还是错了啊,哈哈。

然后,在子程序里面,如果通过 *P 那么访问到的将是 *str的内容,这是等价的。但是,本程序一个致命的错误,非常隐蔽,那是子程序企图修改 p 的内容,而不是 *p 的内容!!这个错误找了我很久终于给揪出来了。修改了 p 的值是没有意义的,这个值是形式参数,并不会返回任何的东西,而 *p 则是通过p的地址直接访问需要的变量,这是不同的用法。所以说白了,void Getmemery(char *p) 执行之后并没有改变任何的东西,str的值并没有修改过,保持NULL,所以访问 *0 地址会被操作系统禁止,得到一个错误。

解决办法,是用2重指针。目的是要修改指针的地址,但是按照上面的分析,我们并不能去修改,但是我们可以用2重指针,将*str的地址值str,用2重指针来改变。

void Getmemery(void **p)
{
 *p=(void **)malloc(100);
}

子程序修改为这个样子,出入的参数也得修改

char *str=NULL;
Getmemery(&str);

那么可以这样理解,因为形参是2重指针,所以 p 对应 &str ,*P 对应 str,之前说了,我们的目的是要修改 str的值,所以很自然,我们用 *p = xxx 这样的形式去修改了。

这样得到的程序就正确了。2重指针,使用起来还是有点晕,得多加练习。

阅读(1708) | 评论(3) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2010-09-04 15:32:18

这本书上有详细介绍及相应解决方法。和你雷同

chinaunix网友2009-05-01 22:46:00

汗,再发一次。。。 给函数传一个指针,就是为了修改指针指向的对象,而不是修改指针本身。 要有这样直观的印象,看到错的代码就会觉得不对劲了。

chinaunix网友2009-05-01 22:45:03

给参数传一个指针,就是为了修改指针指向的对象,而不是修改指针本身。 要有这样直观的影响,看到错的代码就会觉得不对劲了。