C和C++的区别(下)

4. const 的使用

c语言里const是常变量,而c++里是常量,即立即数。

因此c语言里const修饰的变量并不能作为初始化数组长度的下标。

c++const修饰的变量编译时就被当作立即数,可以当作数组初始化下标

 c语言里用const修饰的值可以不初始化,只是之后再没有机会对这个const修饰的变量赋值了。因此我们可以总结出c语言里const定义的常变量和一般变量的区别很小,只是常变量定义后不能作为左值存在。但其真身,或者说是编译方式,和普通变量是一样的。

 然而c++里的const修饰的量必须初始化,否则编译无法通过,即在编译的时候把使用const修饰的量都替换为其的值,因此就能作为数组下标,因为编译时,编译器看到的下标并非const 定义的量的名字,而是它对应的值的立即数。

如果写了以下的代码在某cpp文件中

Const int  a=10

Int  *p=int *&a

*p=30

Printf(%d %d,a,*p);

最终打印的结果将是1030,程序运行后,p指向a的内存,修改aa的内存里的数据肯定已经变成30了,但为什么最终打印的a还是10呢,因为编译时已经把printf里那个a换成了10这个立即数了,不会再因为a运行后的任何改变而改变了

 

刚说了c++里的const修饰的量必须初始化。初始化的值必须是个常量,不能是个普通变量,这样的原因是编译器编译时,替换const常量将不明确,只有立即数或者之前定义过的const常量是明确的具体的数字。

Int b=8;

Const int a=b;

是不对的


const int a=8;

const int b=a;

是可以的


 并且在c++中,const修饰的数据,最终生成的符号是local符号,即只有本文件可见的。

如果想要把const修饰的数据供整个工程使用,生成global全局符号,那么其实也有一种方法,就是在定义处前面加extern声明。


举例 const int a=10;是本文件可见

  extern const int a=10;本工程可见


 


6.引用

  C++的一种新的类型,通常为数据类型+&;单独&表示去地址

  比如简单的引用 int &b=a;就是表示b是a的引用。

  引用就是被引用量的别名,如果改变b的值,同样也会改变a的值。

我们来看看引用的底层汇编究竟是什么

int a=10;

00ED439E  mov         dword ptr [a],0Ah  

 int &b=a;

00ED43A5  lea         eax,[a]  

00ED43A8  mov         dword ptr [b],eax  

  可以看到b对a引用,实际上先让eax存放了a的地址,然后再将eax存放的a的地址赋给了b。这不就是个指针做的事情吗。我们再看看指针的汇编是什么样的。

int a=10;

012D439E  mov         dword ptr [a],0Ah  

  int *p=&a;

012D43A5  lea         eax,[a]  

012D43A8  mov         dword ptr [p],eax  

可以看到指针的汇编和引用的汇编原理是一模一样的。所以说引用其实就是被包装了的指针,不过每次使用引用时都其实都自动做了相当于指针解引用的操作。


那么引用变量到底开辟不开辟内存呢?

我们定义

int a=10;int &b=a;  打印&a 和&b的地址发现是一样的,有人就会说不开辟内存。

其实不是的,刚都说了,引用的本质就是指针,指针是要开辟内存的,32位系统下指针都是4个字节的,可是为什么&a和&b一样呢?因为使用引用b时,已经相当于把b引用所隐藏的指针解过引用了,此时b就是a了,所以怎么取地址都相当于&a,所以地址是一样的,至于引用偷偷开辟的那个4个字节我们是没有观测到的。但它真实存在。


 

怎么引用数组,指针呢?

引用一纬数组int arr[10]={0};

就相当于对int *arr 数组指针进行引用

我们看整形引用是

Int &b=int  a;

b实际上对a取地址,int  *b=&a 只不过&和*都掩盖了,然后在b引用名前紧跟了一个&引用类型


 如果是int arr[10]呢,即对int *arr取地址

按上面的推,是不是b其实相当于一个数组指针,对arr数组指针取地址

Int (*b)[10]=&arr

然后掩盖&取地址和一个*  

Int (b)[10]=arr

然后再紧跟着引用名b前面加上一个&引用类型名

Int (&b)[10]=arr  


 


 那么怎么对指针引用呢?用一级指针举例

是不是引用相当于是一个指针的指针,即二级指针

int *p;

int **b= &p;

然后掩盖

Int *b=p;

然后加&

Int * &b=p;

是不是慢慢地感觉规律来了,就好想好写很多了?


 


 


 


7. const 指针 引用结合


 (1) const 与指针

  一级指针

Int a=10;

Int *p=&a;

其中const修饰第二句可以这样2种修饰

即int const *p 和int * const p

前者修饰*p,意味着不能改变p解引用后的值,即不能通过解引用p去修改p指向的内存的值。

后者修饰的是p,表示p是常量,即可以通过p改变p指向的内存的值,但并不能改变p这个指针本身的值,即p的指向不能改变。

如果int a定义为 const int a;那么这个p只能定义成int const *p;

因为const int a代表不能直接或间接的对a修改,直接就是直接修改a,当然是不行的。间接呢?间接就是把a的地址或者引用泄露出去,让其他高权限的指针通过解引用进行修改。这种显然不符合我们对const的要求,编译器是要杜绝的。


还有一种情况就是这样的。

Int a=10;

Const int *p1=&a;

Int *p2=p1;

a是可以修改的,然后定义了一个const指针p1,它表示不能通过p1修改a的值,这属于权限缩小,当然是没有错误的。

可是第三句int *p2=p1;却是不对的,那是为什么?

首先可以通过权限来判断,第三句相当于把低权限的指针p1让高权限的指针p2使用,这是权限放大,显然有误。

可是有人会疑问,a不是可以修改呢?P2权限大修改a也不影响最终结果啊。

实际上是这样的。编译器在读到Const int *p1=&a这时已经认为p1的指向是个const int类型了,之后再和p1相等指针都应该是const int *类型,如果改成下面这种语序,表达的意思其实和上面一样,但能通过编译

Int a=10;

Int *p2=&a;

Const int *p1=&a;


 


二级指针和const 中需要注意下面这一种情况,和上面的很像。

Int a=10;

Const Int *p=&a;

Int **pp=&p;

这样是编译无法通过的。和

Int a=10;

Const int *p1=&a;

Int *p2=p1;

比,就是第三句不同,前者是Int **p=&p;后者是Int *p2=p1;


就是本来把p1赋值给另一个一级指针p2,变成了一个二级指针指向p。

为什么会编译不通过呢。上面也说了Const int *p=&a;

 这句编译器读完后,就认为p1指向的是个const int 类型了,int **pp指向const int *p就会权限扩大,肯定就会失败。虽然 我们想着**pp修改a,a明明可以修改啊,你可以想成编译器并没有记忆力,先到int a了不会记住a的权限,只会记住Const int *p中p的属性,即使pp和a有合理的关联,但无法通过p这关。也就是常说的间接修改。这也算编译器怕你违反 它认为的规则 的一种预防吧。


 


 (2)Const 与函数重载

之前说过函数重载,对于参数来说,const是否能构成一种新的参数类型呢?

就是

Fx(int a);

Fx(const int a);这俩是否能构成重载呢?

答案是不能的。为什么不能,你可以理解为,如果const没有修饰*指针或&引用,那么其类型其实并没有变化。什么意思呢?

Int * const p;只是修饰了p,并没有修饰*号,即p自己随意怎么固定,反正int *p和int *const p最终表示的是一样的,有人会说明显不一样啊,一个p可以改,一个p不可以改。再次强调我说的是对参数,其表现就是究竟对实参的值有影响,反正最终传进来的都是p,管他p怎么固定呢,只要p的解引用都可以修改,权限是一样的,我就认为他俩一样。


下面这就不能构成重载

Fx(int * a);

Fx(int *const a);  最终fx函数里都可以*a=10,请问有什么不同吗。


但是下面这俩就能构成重载

Fx(int * a);

Fx(const int * a);   最终fx里,前者能*a=10,后者*a=10是非法的,这显然等于是2种参数,自然可以构成重载。


 

(3) const 与  &引用


 由于&紧跟着引用变量名,因此就不会出现 int &const b这种东西

我们只用看看 int const &b

在这之前我们先看看普通引用 即 非const引用 和 各种量的结合


①引用和普通变量 这是可以的√

Int a=10;  

Int &b=a;


 ②引用和常量    这是不行的×

  Const int a=10;

  Int &b=a;   

 首先我们可以看到权限都是扩大的,怎么引用。

 其次通过原理来看,编译时a就变成了立即数10,语句转化过来就是

 Int &b=10;这显然是不对的,引用本质是地址,怎么对10取地址?


 ③引用和立即数   这是不行的×

   Int &b=10;

  和②理由一样,引用本质是地址,怎么对10取地址?


 


 然后我们看看const和&结合,也就是所谓的常引用


①常引用和普通变量  这是可以的√

Int a=10;  

Int const &b=a;

相当于一个缩小权限的指针指向a,合情合理


②常引用和常量   这是可以的√

Const Int a=10;  

Int const &b=a;

相当于一个等权限的指针指向a,合情合理


②常引用和立即数   这是可以的√

Int const &b=10;

这个可能有点让人觉得怪异,不是说b本质是指针吗。指针怎么给10取地址。

的确,这里的引用和指针有些区别。

Const Int *p=&10;显然是不对的,立即数前面怎么加取地址都是错误的。

可是引用却可以,巧就巧在引用不必&10,就不会错误。

并且常引用碰到立即数有特殊的解决方法。

我们来看汇编。

 const int &b=10;

00056EAE  mov         dword ptr [ebp-14h],0Ah  

00056EB5  lea         eax,[ebp-14h]  

00056EB8  mov         dword ptr [b],eax


这三句汇编意思是,将立即数10赋值到ebp栈底指针上20字节开始的地方,赋值4个字节。然后把那栈底上20个字节那块区域的内存地址放入eax寄存器,最后把这个地址在赋值给常引用b的底层指针。


相当于什么意思呢?就是开辟了一个临时区域,把立即数存入到了那个临时区域,然后常引用相当于指针,指针指向的是那个临时区域。


 


(4)const &  *与函数


引用 函数返回值

定义函数:

int fx()

  int a=10;

Return a;

Int main()

{

 Int &a=fx();

}


这是不行的。为什么,因为当函数返回值小于8个字节的时候,返回值是由寄存器带回的,这里就具体为eax带回的值,带回的是个立即数,这是个普通引用,无法引用立即数的。


Const int &a=fx();这样使用常引用就可以。


 指针指向函数返回值

int fx()

 int a=10;

Return a;

Int main()

{

 Int *p=&fx();

}


这就相当于int *p=&10;显然是错误,也不会像引用一样会开辟临时区域存储立即数返回值。

Int* fx()

Static  int a=10;

Return  &a;

Int main()

{

 Int *p=fx();

}

如果如上这样定义呢,即fx里有个数据a,返回a的地址,用寄存器带回,是个立即数,相当于就是 int *const p ,赋值给int *p当然是可以的。


 


 


那么如果返回的是指针能被引用吗??如下

Int* fx()

 Static  int a=10;

Return  &a;

Int main()

{

 Int * &b=fx();

}


返回值是个立即数地址,相当于int *const p,


而b的引用相当于个二级指针int **pp,没有任何的const修饰,真面目就是int *pp=&p,想一想就知道不可以了!


怎么修改。


是不是换成常引用就可以了?


 Int * const &b=fx();


最后说返回值是引用

例如

Int & fx()

{

Static int a=10;

Return a;

}

Int main()

{

 Int a=fx();

}


这就相当于用寄存器返回了指向数据a的地址,然后main中的fx()直接就把寄存器解引用*eax,然后main中用a接受了数据a的内容。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值