const

const修饰的变量不可被修改,在定义的时候,就必须赋值。

如果修改的类、结构体(的指针),其成员已不可以被修改。

struct Date
{
    int year;
    int month;
    int day;
};

int main()
{
    const int a = 1;
    a = 2;	// 错误
    
    const Date d = { 2021, 7, 3 };
    Date d1 = { 2021, 8, 3 };
   	d = d1;	// 错误
    d.year = 2015;	// 错误
    
    Date *p = &d1;	// 如果改成    Date *p = &d1,下面3行操作全部都会报错
    p->year = 2015;	// d1.year: 2015;
    (*p).month = 5;	// d1.month: 5;
    *p = d; 		// p->year: 2021
    
    return 0;
}

以下5个指针分别声明的含义:

int main()
{
    int a = 10;
    int b = 20;
    
    // *p1是常量,p1不是常量,所以,p1是可以修改的,而*p1是不可以被修改的
    const int *p1 = &a;
    *p1 = 20;   // error
    p1 = &b;    // ok
    
    int const *p2 = &a;	// 和p1一样。
    
    // p3是常量,*p3不是常量,所以,*p3是可以修改的,而p3是不可以被修改的
    int * const p3 = &a;
    *p3 = 20;   // ok
    p3 = &b;    // error
    
    // p4是常量,*p4也是常量,所以,p4和*p4都不可以被更改
    const int * const p4 = &a;
    *p4 = 20;   // error
    p4 = &b;    // error
    
    int const * const p5 = &a;  // 和p4一样
    return 0;
}

const修饰的是右边的内容

引用

在C语言里,可以使用指针间接访问和修改某个变量的值。

int main()
{
    int a = 10;
    int *p = &a;
    *p = 20;	// a = 20
    return 0;
}

C++中,使用引用可以起到跟指针类似的功能。

struct Date
{
    int year;
    int month;
    int day;
};

int main()
{
    int a = 10;
    int &ref = a;
    ref = 20;	// a = 20;
    
    // 结构体的引用
    Date d = { 2021, 7, 3 };
    Date &d_ref = d;
    d_ref.month = 5;
    
    // 指针的引用
    int *p = &a;
    int *&p_ref = p;
    *p_ref = 20;

    // 数组的引用
    int array[] = { 1, 2, 3 };
    // int (&array_ref)[3] = array; 下面的也可以
    int * const &array_ref = array;
    array_ref[0] = 10;
	return 0;
}

注意

  1. 引用相当于是变量名的别名(基本数据类型、枚举、结构体、类、指针、数组等,都可以有引用)。
  2. 对引用做计算,就相当于对引用指向的变量做计算。
  3. 在定义的时候必须初始化,一旦指向了某个变量,就不可以修改引用的指向。可以修改引用里的值。
  4. 不存在引用的引用、指向引用的指针、引用数组。

本质

  1. 引用就是指针,只是编译器削弱了它的功能,它就是弱化了的指针。
  2. 一个引用占用一个指针的大小。

使用sizeof运算符可以简单证明一下。

//	系统是x64的,所以,一个指针占用8个字节
struct Int
{
    int a;
};

struct P
{
    int *a;
};

struct Ref
{
    int &a;
};

int main()
{
    printf("%d\t%d\t%d", sizeof(Int), sizeof(P), sizeof(Ref));
    return 0;
}

image-20210703120633148

const引用

引用可以被const修饰,这样就无法通过引用修改数据了,可以称为常引用。

const必须写在&符号的左边,才能算是常引用。例如:const int &ref = 30;

int main()
{
    int a = 10;
	const int &ref1 = a;
    ref1 = 30; // error
    
    int & const ref2 = a; // 不能修改ref2的指向,本来就不可以修改,没有意义。
    ref2 = 1;	// ok
    
    return 0;
}

特点

  1. 可以指向临时数据(常量、表达式、函数返回值等)。

  2. 可以指向不同的类型的数据。

  3. 可以作为函数的形参时:(此规则同样适用与const指针)

    1. 可以接受const和非const实参(非const引用,只能接受非const实参)。
    2. 可以跟非const引用构成重载。
  4. 当常引用指向了不同类型的数据时,会产生临时变量,即引用指向的并不是初始化时的那个变量。

    int sum(const int &a, const int &b)
    {
        return a + b;
    }
    
    int main()
    {
        int a = 1, b = 2;
        // int &ref = 30; // error
    	const int &ref = 30; // ok
        
        // int &ref = a + b; // error
    	const int &ref2 = a + b; // ok
        
        // double &ref = a; // error
        const double &ref = a;
        
        sum(10, 30);
        
        return 0;
    }

指针/引用的本质

int a = 1;
int *p = &age;
*p = 2;

上面三句代码的汇编代码如下

mov dword ptr[ebp-0Ch], 1;    int a = 1; ebp-0Ch就是a的地址
lea eax, [ebp-0Ch];           将ebp-0Ch赋值给eax,即eax == ebp-0Ch。这时候,eax存放的是a的地址
mov dword ptr [ebp-18h], eax; int *p = &a; eax存放的是a的地址,所以,指针变量p里的值,就是a的地址。ebp-18h是指针变量p的地址
mov eax, dword ptr [ebp-18h]; 取ebp-18h(指针p)存储的值(就是a的地址),赋值给eax
mov [eax], 2;                 *p = 2; 将eax存放的地址(a的地址)的值修改为2。