Chinaunix首页 | 论坛 | 博客
  • 博客访问: 276499
  • 博文数量: 28
  • 博客积分: 290
  • 博客等级: 二等列兵
  • 技术积分: 326
  • 用 户 组: 普通用户
  • 注册时间: 2006-05-10 12:12
文章分类

全部博文(28)

文章存档

2020年(1)

2018年(1)

2017年(3)

2015年(7)

2014年(9)

2010年(3)

2006年(4)

我的朋友

分类: C/C++

2015-06-04 19:49:26

人们最难意会的其中一件事情就是指针,尤其在C语言中。如果你想要有效率地进行编程,那你就必须彻底地知道如何使用指针,一旦理解了指针你就理解了C语言。在C语言中,对象(比如整型int x)储存在内存中,它们存放的位置称作地址。计算机拥有足够的能力识别保存你的银行帐户余额的和你玩WOW的天数的内存,它是通过把值存放在不同的地址来完成这种区分。

 

这里先让我们花点时间来哲学性地探讨一下“什么是地址?”考虑你的家庭住址 123 Main St(这是一个假地址,它要是与你家的地址雷同的话,那纯属巧合。如果确实是你的真实地址,那么好吧,我知道你住在哪里了)

 

我问你住在哪里?你说 123 Main St。我就知道了能如何找到你的房子,但事实上我并不在你的房子里。我知道的仅仅是它在什么地方,你并没有把你的房子给我,你只是把房子的指针给了我,你给我的是房子的地址。

 

这与你说int xx的指针的情况是一样的。你并不拥有x,你获得的只是x的指针,你拥有的是x的地址。有了这个指针你就可以使用x。就是说你拥有一个对象的指针,那你就可以对它进行读或写。

 

这样的主要好处就是如果你把一个指针传递给某个函数,这个函数就可以在内部操作指针所指向的对象。

 

当你在C语言中调用函数时,所有函数的实参会被复制到函数形参列表中的形参变量。因为这些形参是函数的局部变量,修改它们只会影响到函数内部的局部拷贝,不能影响到它的调用者的原始实参。

 

为了绕开这个限制,你可以把变量的指针作为函数的实参进行传递。这并不是传递变量本身,而是传递变量的指针。函数通过变量的指针来对这个变量进行操作。

 

从现在开始我们将会呈现一大串C语言语法。很遗憾,由于在设计C语言时所做的选择导致在"*"的用法上产生了些混淆。但也不算太坏,只要你理解了它,就能掌握它。

 

我们可以做以下两件事情:

1.从对象到对象的指针。当拥有一个对象,我们需要得到这个对象的指针。

2.相反的过程:从对象的指针到对象本身。当拥一个对象指针时,我们需要得到这个对象以便能操作它。

 

先来处理第一种情况。先声明一个变量,然后打印出它的指针。记住,指针是对象的地址。为了取得对象的地址,我们要使用取址符&

#include

int main(void)

{

    int x = 10;

    printf("%p\n", &x); // address-of x, aka "a pointer to x"

    return 0;

}

在我的机器上运行以上代码时,它显示:"0x7fffd944f17c",它表示x的地址。对你来说可能会是别的数字,实际上每次运行时的值都不一样。准确的数字并不重要。

 

现在有趣的事情马上就要开始了。我们将把x的地址存入到一个变量中(稍后它会被传递给一个函数)。但问题是,这个变量的类型会是什么呢?其它一些东西如12,它是一个int型;而34.9当然也能够存放到一个float型变量中。如果有"int x",那么x的类型是int。但是x的指针,它的类型又是什么?

 

事实是它的类型是"指向int的指针",或"int指针"。确实有这样一种类型它的存在的目的就是用来指向其它类型。

 

如何声明这样的类型呢?很简单,在变量名之前类型之后放置一个*

{

    int x;   // declare an int called "x"

    int *y;  // declare a pointer to an int (or "int-pointer"), called "y"

...

 

在上面的例子中,变量x未初始化。它是一个整型,但由于未被赋值,它也可能是任何值。同样,变量y也未初始化。它是一个int指针,但由于未被赋值,它可能指向任何地方。(就像是一张未写上地址的邮寄地址标签)

 

所以应该给它们赋上值,为了完整性和乐趣。下面是比较稳健的写法:

{

    int x;   // declare an int called "x"

    int *y;  // declare a pointer to an int (or "int-pointer"), called "y"

 

    x = 3490;   // assign 3490 to x

    y = &x;     // assign "the address of x" to y

 

    // at this point "y points to x".

    ...

正如你所看到的,我们使用取址符来获得x的地址,并且把它存放在指针类型变量y中,y现在指向x

到目前为止,我们完成了列表中的第一个目标:通过使用&符号实现了从对象到对象的指针。

 

第二,通过一个已有的指向对象的指针,我们可以得到这个对象以便能够处理它吗?有个神奇的操作叫作解指针。它表示,我所说的不是指针而是指针所指向的东西。

C语言中使用间接操作符*表示这样的操作。不错,又是这个星号。但是我们并不是把它用在变量声明中,所以使用环境是不一样的。(在变量声明中,星号表示你将声明的是一个指针,而在用在表达式中时,除了表示乘法外则意味着解指针操作。)

 

看下面的代码:

{

    int x;   // declare an int called "x"

    int *y;  // declare a pointer to an int (or "int-pointer"), called "y"

    int z;   // declare another int called "z"

 

    x = 3490;   // assign 3490 to x

    y = &x;     // assign "the address of x" to y

 

    // at this point "y points to x".

 

    // here comes the dereference, which says "z is assigned the value

    // of the thing y is pointing at" (namely 3490 in this case):

 

    z = *y;

 

    printf("%d\n", z); // prints "3490"

    ...

你会看到如何把一个与变量x相同的值赋给变量z。我们并不直接对z赋值,而是通过指向x的指针变量y来间接完成。

 

不仅可以通过指针y来读取x的值,实际上还能够通过指针y来对x进行间接赋值。如下所示:

    int x;

    int *y;

 

    x = 3490;           // x holds 3490

    y = &x;             // y points to x

 

    printf("%d\n", x);  // prints "3490"

    *y = 3491;          // assign 3491 into "x" via the pointer "y"

    printf("%d\n", x);  // prints "3491"

在上面的赋值语句"*y = 3491"中,我们说“y指向的东西被赋给值3491”。这就是在赋值语句后x的值改变的原因。

 

我们创建一个函数通过x的指针在远处对x的值进行操作。

void make24more(int *a)

{

    *a += 24;

}

 

int main(void)

{

    int x;   // declare an int called "x"

    int *y;  // declare a pointer to an int (or "int-pointer"), called "y"

 

    x = 3490;   // assign 3490 to x

    y = &x;     // assign "the address of x" to y

 

    printf("%d\n", x); // prints "3490"

 

    make24more(y);     // add 24 to whatever y points at, namely x

 

    printf("%d\n", x); // prints "3514" (3490+24)

 

    make24more(&x);    // add 24 to whatever is at the address of x, namely x

 

    printf("%d\n", x); // prints "3538" (3490+24+24)

    ...

看到它是如何工作的了吗?当你把变量的指针传递给函数make24more(),函数进行解指针后把值24添加到指针所指向的任何东西。

你会注意到我们以两种不同的方式调用make24more():传递指针类型的变量y以及传递x的地址&x。这两种方法都是完全可接受的。

当我们调用函数make24more()后会发生什么事情呢?那就是一个指针的复本会被创建,在这个例子中就是函数内部的局部变量a。函数并不关心形参只是指针的一个复本,因为它不对指针进行操作;通过使用*操作符,它所处理的是指针指向的东西。x的指针保存在指向x的指针变量y中,而x的指针的副本保存在形参变量a里,它也指向x。对其中任何一个解指针你都会得到x。不管你是得到我家的地址还是我家地址的副本,这都不重要,它们两个都可以让找到我的家。

 

在实际使用上这是很强大的。如果你拥有了一个对象的指针,你可以把它分发传递给不同的函数,这些函数都能够操作指针所指向的对象。实际上可以这样说,我不拥有对象,但我知道它在哪里,因为我有这个对象的指针,而且通过解指针我就能够改变它的值。

 

C语言中使用符号“NULL”来表示那些不指向任何对象的指针。

int *p;  // p is uninitialized at this point

p = NULL;  // now p points to nothing, explicitly

它可以用来作为指针数组结尾标记有时也作为标记错误条件,malloc()分配内存失败时会返回NULL通知你。

char *p;

 

p = malloc(1024); // allocate 1K

if (p == NULL) {

printf("Memory allocation failed!\n");

} else {

    // go ahead and use the pointer:

strcpy(p, "Goats detected!");

}

C语言中的指针可以指向变量,数组中的元素,列表或树的结点,显式内存地址甚至函数。

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