代码改变世界

C指针那点事#1

2011-03-02 01:23  onm  阅读(267)  评论(0编辑  收藏  举报

我在豆瓣日记上更新了《C指针那点事#2》,对此文进行了一些修正。那个对于这个问题的解释更清爽一些,有兴趣的可以过去看看,就不移植过来了。

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

void create_int(int *p)
{
    p = (int *) malloc(sizeof(int));
}

int main()
{
    int *p = NULL;

    create_int(p);

    assert(p != NULL);  /* failed. why? I've allocated memory for it. */

    return 0;
}

这个代码是前天写的,是前天的日记二叉树引发的一些思索的下下文。我虽然同样在StackOverflow上提问并且得到了回答,但是这些回答并没有完全解除我的困惑,反而加深了我的困惑。昨天在看《C和指针》,突然想通了。

其实一看我的注释就知道我是菜鸟。我也是刚发现的,这个问题实在太菜了。你给一个指针指向的地址申请空间和指针本身的值有什么关系啊!指针本身的值还是本身的值NULL,跟指针指向位置分配不分配空间有什么关系,当然这个是昨天看书弄懂的。(这个又理解错了,写着写着,突然又明白了。)一个指针没有初始化的时候(什么是指针初始化,就是给它分配空间或者给指针赋值),指针变量声明后是一通乱指的,如果没有初始化,直接用指针进行操作是危险的,这个内容放到下回说。(所以正解还是StackOverflow上的人的回答)

我这里粘一下StackOverflow的回答,给多人给出了解决方案,当我继续问Why的时候,一些人给出了解释。

Because p in your create_int function is a completely different variable from p in your main function. It just has the same value initially, because you passed the value of the one in main as the parameter. Changing the value of p in create_int has no effect on the value of p in main, which remains a null pointer.

这个解释更加加深了我的迷惑,他居然说p是按值传递的,但是p明明是个指针啊!然而这里我就犯了一个错误,虽然p确实是一个指针,并且指向某个地址,但指针本身是存有值的。就是说指针不过也是个变量,只是变量里存储的内容是个地址,(所以我之前的另一个疑惑也迎刃而解,就是sizeof(p)怎么会是4,并且无论p指向什么类型的变量。答案很简单,因为指针是存储地址的,在32位机器上,一个地址是32bit,所以自然指针的大小就是4字节了。)当我把p做为实参传给create_int()函数的时候,它确实是按值进行传递的,再强调一遍,它传进去的是指针本身,指针本身存储地址,而不是直接传给该函数地址,所以是按值传递。

所以当指针p传递进去后,在函数体内部是复制了一份该指针,也就是指针的内容是NULL,然后对指针进行初始化(动态分配空间),分配完空间之后,指针本身的值就会发生改变(当然排除恰巧就分配在NULL位置),这个值会更新成新分配的空间的首地址。然而当回到主函数时,由于p是按值传递的,不是按址,所以主函数中的p并不会有变化。它还是原来的p。

额外的,指针p在create_int()函数中,是个自动变量,也就说它本身的空间是分配在运行时堆栈上的,当函数结束时,指针p的空间自动被释放,而不是指针p所指向的空间被释放。(所以如果你在函数中不free掉该空间,就会造成内存泄露)还有在create_int()函数中,当调用malloc()函数时,p所指向位置被分配空间。这里又涉及到StackOverflow上一个人的回答,他说:Also, in C you should never cast the return value of malloc(),并且给出了一个相关链接Do I cast the result of malloc?。感兴趣的朋友自己去看看吧,我在这里就不细说明了。