天天看点

C++ 初学者指南 第四篇(10)

必备技能 4.9: 指针运算符

     在使用指针的时候,有两个特殊的运算符要用到:*和&。其中&是一个单目运算符,它返回操作数的内存地址。(单目运算符只需要一个操作数参与运算。)

例如:

ptr = &total;

上面的语句把变量total的地址赋值给ptr。这个地址就是变量total在内存中的位置。它和变量total的取值没有任何关系。取地址运算&可以看做是返回某某变量的地址。因此上面的赋值语句可以表述成“为ptr赋值为total的地址“。为了更好的理解这种赋值运算,我们假设变量total在内存中的地址为100。那么经过这个赋值语句后,ptr的值就是100。

     第二个运算符是*,它是对&运算符的补充。它也是一个单目的运算符。它返回运算数的值对应的内存地址的值。我们继续使用前面的那个例子,如果ptr的值为变量total的内存地址,那么

val = *ptr;

将把total变量的值赋值给val。例如,如果total变量的值为3200,那么val的值就是3200,因为3200就是存储在位置为100的内存中的值,而ptr的值就是100。所以*运算符可以看做是返回某个内存地址上的值。因此,上面的这个语句可以表述为“把ptr指向的内存地址的值赋值给val”。

    下面的程序演示了对上面的操作:

#include <iostream>

using namespace std;

int main()

{

    int total;

    int *ptr;

    int val;

    total = 3200; //给变量total赋值为3200

    ptr = &total; //给ptr赋值为变量total的地址

    //把指针ptr指向的变量的值赋值给val,也就是把ptr的值作为一个内存地址

    //然后把这个内存地址中的值赋值给val

    val = *ptr ;

    cout << "Total is:" << val << '\n';

    return 0;

}

     需要注意的是,乘法的运算符和取某个内存地址的值的运算符是相同的。这点对于C++初学者来说可以会引起混淆。这两者之间没有任何的关系。请记住,&和*运算符的优先级是高于任何算术运算符的,而它们和单目运算符负的(-)的优先级是相同的。

     使用指针的方法通常被称作是间接的方式,因为我们是通过一个变量间接地访问另外一个变量。

练习:

1. 什么是指针?

2. 写出如何声明一个名为valPrt的指向一个long int类型的指针?

3. 和指针相关的两个运算符*和&的作用是什么?

 指针的基本类型很重要

     在前面的讨论中,我们看到,我们可以通过间接的使用指针来把total变量的值赋值给val变量。 你也许会考虑到一个很重要的问题:C++是如何知道需要从ptr所指向的地址中赋值多少个字节的值到val变量中呢?或者,更通用一点的问题:在涉及指针的赋值时,C++编译器是如何决定需要复制的字节的多少呢?针对前面的例子,因为ptr是一个整型数指针,所以需要从ptr执行的地址开始复制4个字节数据到val中(假设整型数的大小为32位)。然而,如果ptr被定义成double 类型的指针,则需要复制8个字节的数据到val中。

 指针变量总是指向相应的正确类型的数据,这一点至关重要。例如,当我们声明一个int类型的指针的时候,C++编译器会认为它所指向的任何数据都是一个int类型的数据。如果它所指向的数据不是int类型,通常很快就会出现问题。例如,下面的写法是错误的:

int * p;

double f;

//…

p=&f;  //这种写法是错误的。

上面的代码片段是无效的,因为我们不能把一个double类型的指针赋值给一个int类型的指针。也就是说,&f产生的是一个double类型的指针,但是p是一个int类型的指针。这两种类型是不兼容的。(实际上,编译器会在这条语句这里报告编译错误,说明是不兼容赋值。)

 尽管在指针相互赋值的时候,两个指针的类型必须是兼容的,但是我们可以使用强制类型转换来突破这个限制。例如,下面的代码种计数上来讲是正确的:

int * p;

double f;

//…

p=(int*)&f;

强制的类型转换把double类型的指针转换成了一个int类型的指针。然而,这种使用强制类型的做法是有争议的。因为指针的基本类型决定了编译器如何对待它所指向的数据。在这种情况下,尽管p实际上是指向了一个double类型的数据,但是编译器会认为p指向的是一个int类型的数据,因为p的基本类型位int类型。为了更好地理解这点,考虑下面的程序:

//下面的程序将不能正确工作。

#include <iostream>

using namespace std;

int main()

{

    double x,y;

    int *p;

    x = 123.33;

    p = (int *)&x; //强制地把double类型的指针转换位int类型的指针

    y = *p;  // 会发生什么情况了?

    cout << y;

    return 0;

}

下面是程序的输出。(你可能会看到不同的输出结果。)

-1.20259e+09

这个数值显然不是123.33!这是为什么了?在程序中,p是一个int类型的指针, 它被赋值为一个double类型的数据的地址。因此,当通过p给y赋值的时候,y只是被复制了4个字节的数据(而不是double类型的8个字节),因为p是一个int类型的指针。因此,cout语句输出的数据不是123.33而是一个垃圾数据。

通过指针来赋值

     我们可以在赋值语句的左边使用指针来把一个内存地址赋值给该指针。假设p是一个int类型的指针,下面的赋值语句将把101赋值给p所指向的位置。

*p=101;

上面的这个赋值语句可以表述为:“把101赋值给p虽指向的位置。“如果需要对p指向的位置的值进行自增或者自减我们可以使用下面的语句:

(*p)++;

其中括号是有必要的,因为*运算符的优先级是低于++运算符的。

    下面的程序演示了使用指针来进行赋值:

#include <iostream>

using namespace std;

int main ()

{

    int *p, num;

    p = &num;

    *p = 100; //通过使用p来给num赋值为100

    cout << num << ' ';

    (*p)++;   // 通过p来实现num的自增

    cout << num << ' ';

    (*p)--;   // 通过p来实现num的自减

    cout << num << ' ';

    return 0;

}

程序的输出如下:

100 101 100

继续阅读