栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C++ const、指针和引用

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C++ const、指针和引用

const 什么是const?

        const修饰的变量不能够再作为左值,初始化完成后,值不能被修改。

 C与C++中const的区别是什么?

        c语言中,const修饰的值,可以不用初始化,不叫常量,叫做常变量;

输出30 30 30

      C++中: const 定义的类型必须初始化,否则报错,c语言中可以不初始化

 

#include 

int main()
{
  const int a = 10 ;
  int array[a] = {};//a 是常量,可以定义数组

  int *p = (int *)&a;
  *p = 30;

  std::cout << a << " " << *p << " " << *(&a);// 10 30 10

  return 0;
}

输出 10 30 10;a的值并未被修改。

原因: const 的编译方式不同,C语言中,const 就是当作一个变量来编译生成指令的。C++中,如果const 赋值是一个立即数,所有出现const常量名字的地方,都被常量的初始化所替换。

为什么上面的输出a的值是10呢?因为const 在编译阶段,就已经被常量替换,也就是: std::cout << 10 << " " << *p << " " << 10;

运行时:debug调试

a的地址是0x00cffafc.查看内存: 

 执行完第9行:

 a的内存中的值变成1e 也即30;但是本来出现a的地方在编译期已经被替换成10,因此输出a依然是10。

        如果不是立即数,则是常变量

#include 

int main()
{
  int b = 1;
  const int a = b ;
  //int array[a] = {};//报错,a是常变量

  int *p = (int *)&a;
  *p = 30;

  std::cout << a << " " << *p << " " << *(&a);// 30 30 30

  return 0;
}
         const 与指针

         const 修饰的量常出现的错误:

(1)常量不能再作为左值

(2)不能把常量的地址泄露给一个普通的指针或者普通的引用变量

 

const 与一级指针:const 如果右边没有指针*,则const 是不参与类型的

C++的语言规范:就近原则 const 修饰的是离它最近的类型

(1)const int *p ;离const 最近的类型是(int)const修饰的是*p,*p不能修改值。  可以指向任意int的内存,但是不能通过指针间接修改内存的值

(2)int const *p;  *不是类型, 离const 最近的类型是(int) 同(1)

(3)int * const p; 离const最近的类型是(int *)const修饰的是p 不能改变p指向的地址,但是可以修改p指向地址的内容

(4)const int * const p;不能改变p 指向的地址,也不能改变p指向地址的内容

#include 

int main()
{
  const int a = 10 ;
  const int * p = &a;//p指向的地址的内容不能修改

  return 0;
}

 

const 如果右边没有指针*的话,const 是不参与类型的,仅表示const修饰的是一个常量,不能作为左值

const类型转化公式:

cont int * <= int * 可以转化

int * <= const int * 是错误的

实例1:

#include 
#include 
int main()
{

  int * p = nullptr;
  int * const p1 = nullptr;//const 右边没有* ,const不参与类型

  std::cout << typeid(p).name() << std::endl;

  std::cout << typeid(p1).name() << std::endl;

  return 0;
}

示例2: 

int a=10;
int *p1= &a;
const int *p2 = &a;// const int * <= int *
int *const p3 = &a;// int * <= int *
int *p4 = p3;//p3是int * 类型,因此没有问题
const与二级指针

 const int ** q;离const 最近的类型是int ,修饰的是**q;

int * const *q;离const 最近的类型是int *,修饰的是*q;

int ** const q ;离const 最近的类型是int **,修饰的q;//同时const 右边没有*,q是int **类型。

类型转化公式:

int ** <= const int **   //错误

const int ** <= int **   //错误

const 与二级指针结合的时候,两边必须同时有const 或没有const 才能转换;

int ** <= int * const *  是const 和一级指针的结合,const 右边修饰的*  (等同于int *  <= const int *  )错误的

int * const * <= int **  (等同于 const int * <= int *)可以的

要看const 右边的* 决定const 修饰的是类型

#include 
#include 
int main()
{
  int a = 10;
  int * p = &a;
  const int ** q = &p;//error  
  
  return 0;
}
引用

        1 引用是必须初始化的,指针可以不初始化,

        2 引用只有一级引用,没有多级引用;指针可以有一级指针,也可以用多级指针

        3 定义一个引用变量和定义一个指针变量,其汇编指令是一样的;通过引用变量修改所引用内存的值,和通过指针解引用修改指针指向的内存的值,其底层指令也是一模一样的;

        引用的错误用法  int &a = 10;由下面的反汇编可以知道,引用的汇编代码第一步是将引用对象的地址拷贝到寄存器中,10是常量;

#include 
#include 
int main()
{
  int a = 10;
  int * p = &a;
  int &b = a;

  std::cout  << a << " " << b << " " << (*p) << std::endl;

  *p = 20;
  std::cout << a << " " << b << " " << (*p) << std::endl;


  b = 30;
  std::cout << a << " " << b << " " << (*p);
  return 0;
}

输出:

 

 反汇编:指针和引用没有区别

lea eax,[a]  将a的地址拷贝到寄存器eax中

mov dword ptr [p],eax 将eax中的值拷贝到p中。

从反汇编中看指针和引用并没有区别。都是拷贝a的地址到p,b 中

 

 对指针和引用赋值,都是一样的:获取地址,然后赋值

        4 引用即别名

#include 
#include 
int main()
{
  int array[5] = {};
  int * p = array;
  int(&q)[5] = array;//定义一个引用指向数组:引用即别名  sizeof(q) =  sizeof(array) 

  std::cout << sizeof(array) << "n" << sizeof(p) << "n" << sizeof(q) << std::endl;//20 5 20

  return 0;
}

        关于定义一个引用类型,到底需不需要开辟内存空间,我认为是需要的,上面的汇编代码中,引用和指针的汇编是一模一样的;C++中只有 const 类型的数据,要求必须初始化。而引用也必须要初始化,所以引用是指针,还应该是 const 修饰的常指针。 一经声明不可改变。 

站在宏观角度,引用也就是别名,所以不开辟看空间。

站在微观的角度,引用至少要保存一个指针,所以一定要开辟空间。站在底层实现的角度,站在C++对于C实现包装的角度,引用就是指针。那么既然是指针至少要占用4个字节空间。

左值引用

        左值:有内存地址,有名字,值可以修改;

        如int a = 10;int &b =a;

        int &c = 20;//错误 20 是右值,20=40是错误的,其值不能修改,没内存,没名字,是一个立即数;

右值引用

       1  int &&c = 10;  专门用来引用右值类型,指令上,可以自动产生临时量,然后直接引用临时量   c = 1;

反汇编:

可以看出,右值引用首先把右值存放在一个内存地址中:ebp-4ch,然后的操作和左值引用类似: 

如果用左值引用:const int &a=1;它首先定义一个临时量int temp=1;然后const int &a = temp;

汇编代码与右值引用没有区别

       2 一个右值引用变量,本身是一个左值,只能用左值引用来引用它;不能用一个右值引用变量来引用一个左值

  int && a = 1;
  a = 10;
  int &e = a;

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/443709.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号