1.变量、值、指针、引用、常量

  2021-8-14 


变量名

C++中的变量名,比如int nObject obj

C++对变量名nobj不做存储,只是用于方便编译成汇编代码,是给编译器看的,是方便人阅读的。

举例:int n=5;

编译器编译它时,产生类似mov [0x00410FC0],5的指令,即:把5放在该内存地址的空间上。其中并没有出现n,n只是编译时供编译器识别的名字,是一个高级语言抽象出来的概念,在真实执行的程序中并不存在n,至于n的地址是0x00410FC0还是其他的什么,这是由连接器(linker)决定的,连接器把全局变量放在.exe文件中,执行.exe文件时全局变量在类似0x00430000左右的地址。局部变量在栈上,一般地址为0x00120000左右。
地址0x00410FC0并不需要一个地址去存放它,因为在最后产生.exe文件它自己知道它需要的一个值存放在什么地址,所以就在它的二进制代码中把0xffbffb0c硬编码进来了。
原文链接:https://blog.csdn.net/tina_ttl/article/details/52648533

C++中的变量名就是个代号,其就是存储单元上具体的数据。它不是指针,也不是引用。

arr&pointer

int a[10];  //数组名可以转化(当作)为指针,即求a数组第n个元素:a[n]实际上是*(a+n),完成了一次解引用过程。

int *p1 = a;  //指向数组a的第一个元素,这个指针的内容的类型为int,p1指针等价于a指针
int (*p2)[10] = &a; //指向一个10个元素数组的指针,这个指针的内容的类型为一个指针(指向了数组具体int元素)也是个指针即上面的p1,所以这个p2是指向指针a的指针,所以要对第一层指针a取地址;在二维数组中,若干个指向指针的指针组成一个数组,即上层维度

//第二句拆开来:
int *temp = a; //定义指向数组的指针
int *p2 = &temp; //定义指向指针的指针
//由于这个指针的内容是一个int[10]数组,所以写为:
int (*p2)[10] = &a;

两者都发生了数组名自动转换为指针(a数组名,转化为指针)

但是区别在于第二句又套了一层指针,主要发生在二维数组上。

建议一切数组操作都从指针角度入手和理解

使用例子(重要):

#include <iostream>
using namespace std;

//传入参数为指针,返回参数为指针
int *test(int *arr)
{
    arr[0] = -1;
    return arr;
}
//传入参数为指向指针的指针,返回参数也为指向指针的指针
int (*test2(int (*arr)[10]))[10]
//int **test2(int **arr)  //也可以这么定义,但是这样后面的所有数组维度声明全部要修改
{
    (*arr)[1] = -2; //由于传入参数是指向指针的指针,要想操作指针所指向的指针所指向的数组具体元素,需要两次解引用
    //这里这句话等价于:
    *(*arr+1) = -2;  //注意:多个解引用从内开始运算,所以要先解引用得内层指针,有[]则优先级最高
    return arr; //返回的是指向指针的指针,所以直接返回传入的arr即可
}
int main()
{
    int a[10];
    int *p1 = test(a);
    int (*p2)[10] = test2(&a);

    cout<<p1[0]<<endl; //or *p1
    cout<<(*p2)[1]<<endl; //or *(*p2+1)
}

pointer & reference

符号 作为左值 作为右值
& 引用类型声明 取地址符
* 指针类型声明 解引用符
  1. 运算表达式的结果是右值,函数参数表是左值

    赋值语句右边是右值,左边是左值

    void func(int &var){}
    int main()
    {
        int a;
        func(a);  //这里实际上发生了隐式初始化:int &var = a ,所以显然参数入口不用&,函数参数表要用&表示引用声明
    }
  2. 非常量左值引用(T&)只能引用一个左值(只能用一个左值对引用进行初始化)

    常量左值引用(const T&)也可以引用一个右值

    右值引用(T&&)只能引用一个右值(但注意,右值引用本身是左值)

  3. 函数传参,不改变参数时,尤其是大数据,尽量使用const T& (常量引用)

    好处:

    1. const T&形参初始化为引用,少了对对象的拷贝
    2. 设置为常量避免对对象的误修改
    3. const T&既可以接收左值也可以接收右值
  4. 函数返回为左值则返回类型必须为引用

  5. 指针成员访问符->,和c中一样

  6. 引用实质是操作地址指针

  7. 引用本身是const的,一旦初始化之后引用不能赋值(禁止引用传递)

  8. 完美引用、引用折叠,见”C++11右值引用产生的一揽子问题“专题

左值,右值,亡值

https://zhuanlan.zhihu.com/p/402251966

https://www.zhihu.com/question/363686723/answer/1976488046

【】

字符串

注意字符串字面量是左值,所以非常量引用(string&)也可以直接引用字符串字面量string &str ="abc";

为什么?因为字符串字面量本质实际是C风格字符串const char[N]/const char*,上面语句实际上发生了字符数组到字符串的转换

const

靠里是顶层,靠外是底层

非常量可以向常量转化(变严),反过来不行

引用不能修改,都是const的,所以&const a等价于&a,不用写const

对常量的引用:const &a (引用对象不能修改,引用本身也不能修改;简称常量引用,注意只是简称!)

对常量的引用实际可能引用一个非常量的对象:常量引用仅对引用可参与的操作做出了限定,对于引用的本身是不是一个常量未作限定。

常量指针:int *const a (指针不能修改)

指向常量的指针: const int *a(指针指向的内容不能修改)

指向常量的常量指针:const int *const a(指针和指针指向的内容都不能修改)

若将成员函数声明为const(函数参数表与语句大括号之间加const),则该函数不允许修改类的数据变量。且,const类型的成员函数不能返回非const类型的引用,比如这样就OK,const vector<int>& operator*() const,如果返回类型去掉const就错了

param

函数中,实参到形参的传递是初始化(用实参来初始化形参),而不是赋值!(显然,引用不能传递,但函数能接收引用)

传参即用实参初始化形参,注意传的是什么。比如传的如果是引用/指针,那么引用的对象是不会触发拷贝/移动构造函数的,因为实际上是传的引用/指针本身而不是对象


且听风吟