C++入门系列博客三 引用和指针

C++ 引用和指针

作者:AceTan,转载请标明出处!

引用和指针对于C++来说很重要,是学习C++绕不过去的一道坎。

引用

引用(reference) 就是给对象起别名。对引用的操作与对变量直接操作完全一样。

这里说的引用泛指“左值引用(lvalue reference)”,C++11新增了一种引用,即所谓的“右值引用(rvalue reference)”,这里不作讨论。

引用即别名,引用并非一个对象,不能定义引用的引用。

定义引用时,程序把引用和它的初始值进行绑定(bind)在一起,而不是将初始值拷贝给引用。一旦初始化完成,引用和它的初始值对象一直是绑定在一起的,死也不分开。

举个栗子:胡一菲的小名叫小菲菲,那么对小菲菲的所有操作既是对胡一菲的操作。同样,小菲菲这个名字就是胡一菲绑定在一起了。当然,胡一菲还可以有其他的别名,比如叫菲菲菲。但需要注意的是,一旦有了这个小名,那么这个小名就不能指代其他人。不然会死的很惨。

引用的定义

引用定义时就需要初始化。允许一条语句定义多个引用,其中每个引用标示符都必须以符号&开头。

int value = 1;
int &refValue = value;      // refValue 指向value
int &refValue2;             // 报错,引用必须初始化 

引用的写法也还可以是这样:

int value = 1;
int& refValue = value;      // refValue 指向value
int& refValue2;             // 报错,引用必须初始化 

注意&符号和int之间是否紧挨着,两种写法都是对的(下面介绍的指针也可以用这两种写法), 这个只是个人的代码风格问题,并无实质差异。

引用更为常见的用法应该是作为函数的参数,利用引用的特性,可以让一个函数返回多个”返回值”。另外,当使用一个类作为函数参数时,比较好的做法也是使用引用。这样可以减少创建类副本的一些开销。请看一个具体的示例:

#include <iostream>

using namespace std;

const double PI = 3.14;

// 圆的数据结构
struct Circle
{
    double radius;          // 半径
    double circumference;   // 周长
    double square;          // 面积
};

// 给出圆的半径,计算圆的周长和面积,返回是否计算成功。
bool CalCirle(Circle& c)
{
    // 给的圆的半径为负数,返回计算错误
    if (c.radius <= 0)
    {
        return false;
    }

    // 周长的计算
    c.circumference = 2 * PI * c.radius;

    // 面积的计算
    c.square = PI * c.radius * c.radius;

    return true;

}

int main()
{
    Circle c;
    cout << "请输入圆的半径:";
    cin >> c.radius;

    if (CalCirle(c))
    {
        cout << "该圆的周长是" << c.circumference<< endl;
        cout << "该圆的面积是" << c.square << endl;
    }
    else
    {
        cout << "您输入的半径不合法!" << endl;
    }

    return 0;
}

指针

指针(pointer) 是“指向(point to)”另外一种类型的复合类型。

复合类型(compound type) 是指基于其他类型定义的类型。比较常见的是指针和引用。

指针本身就是一个对象,允许对指针的复制和拷贝,而且,在指针的生命周期内,它可以先后指向几个不同的对象。指针和其他变量一样,定义的时候可以不赋值。如果指针未被初始化,那么它将拥有一个不确定的值。指针未初始化和空指针经常引起程序崩溃。

指针通常难以理解,即使是有经验的程序员也常常因为调试指针引发的bug而备受折磨。

举个栗子,便于理解指针:指针好比门牌号,它所指向的是这个房间的东西,它本身也在有值的,它就是门牌上的号码。你可以把门牌号取下来,挂在别的房间,那么这个门牌号就指向了另一个房间。当然,你觉得有个门牌号不吉利,把它重新刷成其他门牌号,这样也是允许的。但你不能把两个不同的房间都挂一样的门牌号,那么别人就分不清了。某天你打开了某个房间(你需要钥匙,钥匙就是解引用),发现里面有个门牌号,这个就是多级指针。房间里的门牌号告诉你,你要找的东西在这个门牌号所指代的房间里。

指针存放某个对象的地址,要想获得该地址,需要使用取地址运算符(操作符&)

注意这里的取地址运算符&声明引用时标示符以符号&开头的意义和用法是不一样的。

int value = 2;
int* p = &value;    // p存放变量value的地址,或者说p是指向变量value的指针

利用指针访问对象:如果一个指针指向一个对象,则允许解引用符(操作符*) 来访问该对象。

int value = 2;
int* p = &value;        // p存放变量value的地址,或者说p是指向变量value的指针

*p = *p * 3;            // 取这个变量,并乘以3

cout << *p << endl;     // 输出结果为6
cout << value << endl;  // 输出结果为6

指针可以为空(引用是不可以的)。空指针不指向任何对象,一个良好的习惯就是使用指针前,一定要判断是否为空。

得到空指针最直接的方法是用字面值nullptr 来初始化指针。(C++11引入的一种方法)

以前的方法是将指针初始化为字面值0或者NULL。(NULL为一个预处理变量,它的值就是0)。

void* 指针 是一个特殊类型的指针,它可用于存放任意对象的地址。

指针的指针 指针级数是没有限制的。但常见的也就是一级指针和二级指针,二级以上比较少见。给出一个多级指针的例子:

#include <iostream>

using namespace std;

int main()
{
    int a[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    int b[3][4] = 
    {
        11, 22, 33,
        44, 55, 66,
        77, 88, 99,
        111, 222, 333
    };

    int *p1 = nullptr, *p2 = nullptr, **p3 = nullptr;

    p1 = a;     // p1指向a数组
    p3 = &p1;   // p3指向p1

    for (int i = 0; i < 10; ++i)
    {
        cout << *(*p3 + i) << " ";  // 输出: 0 1 2 3 4 5 6 7 8 9
    }

    cout << endl;

    for (p1 = a; p1 - a < 10; ++p1)
    {
        p3 = &p1;
        cout << ** p3 << " ";       // 输出: 0 1 2 3 4 5 6 7 8 9
    }

    cout << endl;

    for (int i = 0; i < 3; ++i)
    {
        p2 = b[i];
        p3 = &p2;
        for (int j = 0; j < 4; ++j)
        {
            cout << *(*p3 + j) << " ";  // 输出:11 22 33 44 55 66 77 88 99 111 222 333
        }
    }

    return 0;
}

函数指针 是C++中最大优点之一了。一般来说,使用函数指针比函数引用更为方便一些。

函数指针的声明使用方式:

<想要指向的函数之 返回类型>(*函数指针的 名称)<想要指向的函数之 参数类型…>

#include <iostream>

using namespace std;

void sayHello(const char* name)
{
    cout << "Hello, " << name << endl;
}

int add(int a, int b)
{
    return a + b;
}

int main()
{
    void(*pFunc1)(const char*);
    pFunc1 = &sayHello;
    pFunc1("Ace Tan");              // 输出:Hello, Ace Tan

    int(*pFunc2)(int, int);
    pFunc2 = &add;
    cout << pFunc2(1, 2) << endl;   // 输出3

    return 0;
}

函数指针也可以作为函数的参数,在这儿不做详细介绍了。

总结

引用和指针有如下区别:

  • 修改性:指针可以重新赋值以指向另一个对象。引用在初始化时就被绑定对象,以后无法改变。

  • 非空性: 指针可为空,引用不可为空

  • 传递性: 指针传递参数和值传递是一样的,被当做局部函数来处理,不影响主调函数的实参变量。引用传递对形参的任何操作都影响了主调函数中的实参变量。

结束语

引用和指针在C++中应用非常多,也非常灵活。指针也是C++最难学的部分之一,少年,赶快去征服它吧。

    原文作者:AceTan
    原文地址: https://www.jianshu.com/p/3ad2fed0fb40
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞