const /*/& 在一起要干嘛 ?!

一、关键字Const作用:

1.可以定义const常量,具有不可变性。 

 例如:const int Max=100; Max++会产生错误; 


2.便于进行类型检查,消除了一些隐患。const常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查,而对后者只进行字符替换,没有类型安全检查,并且在字符替换时可能会产生意料不到的错误。

 例如: void f(const int i) { .........}  //编译器就会知道i是一个常量,不允许修改; 


3.可以避免意义模糊的数字出现,同样可以很方便地进行参数的调整和修改。 同宏定义一样,可以做到不变则已,一变都变!如1.中,如果想修改Max的内容,只需要:const int Max=you want;即可!


4.可以保护被修饰的东西,防止意外的修改,增强程序的健壮性。 还是上面的例子,如果在函数体内修改了i,编译器就会报错; 
 例如: void f(const int i)  { i=10; } 
//error!


5.为函数重载提供了一个参考;

   例如:class A
         {
            ......
           void f(int i)       {......} //
一个函数
           void f(int i) const {......} //
上一个函数的重载
           ......
        };


6. 可以节省空间,避免不必要的内存分配。
const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是像#define一样给出的是立即数,所以,const定义的常量在程序运行过程中只有一份拷贝,而#define定义的常量在内存中有若干份拷贝。 

 例如: 
  #define PI 3.14159 //常量宏 
  const double Pi=3.14159; //此时并未将Pi放入RAM中 ...... 
  double i=Pi; //此时为Pi分配内存,以后不再分配! 
  double I=PI; //编译期间进行宏替换,分配内存 
  double j=Pi; //没有内存分配 
  double J=PI; //再进行宏替换,又一次分配内存! 
  


7. 提高了效率。 编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。


二.Const限定符在C/C++中区别:

1.C语言中的常变量的不可以定义数组。因为C中的常变量和变量唯一的区别是不能作为左值,其他性质都和变量一样。eg: 在.C中,有三种修改const常变量值的方式:

       int b=20; const int a= 10;

      1) int *p=&a; *p=20;

  1. 2) _asm 
    {mov dword[ebp-8] 14h}

    3) *(&b-1)=20;

C语言中可以引用外部const变量声明/定义,会生成一个global符号。链接器在连接的时候,只看g符号。而在其前加上static,就会变为local。除了外部常量,外部变量产生的符号也是g。

2 . C++中的常变量可以作为数组的下标,可以定义数组。常变量是常量。但是在某些情况下,会退化为 变量。

     C++中常变量在编译阶段所有出现常量名字的地方会被常量值替换,但不会替换类似&a表达式中的a,当a是const常量时。而C中的常变量在编译阶段并不会被替换。

    C++中不可以引用外部文件定义的常量,因为产生的是local符号 ;
因为替换的时候只能在当前文件中替换。如果要能被外部引用到,在变量定义时加上一个extern,就可将其链接属性变为global.

     当一个常变量的初始值为外部定义的值时,此时常量就会退换成常变量。因为编译阶段不知道初始值,链接时才知道有这个符号的定义,运行时才会赋值。那么常量的所有汇编也都会变成和变量一样。

三.Const限定符与&.*的结合:

1.const 和一级指针的结合 :
      当const修饰一个变量名时,并不是意味着这个变量所占的内存是const,而是不能通过这个名字对其修改或泄露常量内存地址或引用。例如:

const /*/& 在一起要干嘛 ?!

 常见用法:

      int a = 10;

      const int *p = &a;

      int *q = p;


4.const和二级指针的结合,例如:

1)常见错误:

int a = 10;

int *p = &a;

const int **q = &p;

int** -> const int **: const修饰的哪个变量就先改变哪一边 *q<->p这里的*p会泄露常量地址,所以出错!如果,**q<->*p,将一个常量的值放入const修饰的变量中,则不会出错。

2)经过修改之后:

    int a = 10;

    int *p = &a;

    const int * const *q  = p;//这里组织*q被非法修改;

3)又如:

int a = 10;

int *p = &a;

    int  * const *q  = &p;//**q<->*p也没有问题;

 4)当const和指针结合时,若const右边无指针时,有没有const无区别。因此不能构成重载。 

void func(const int a)

{

}

void func(int b)

{

}
  
  5)当对实参的值有影响时,才会构成重载。下面这个就可以构成重载。 

void func(const int* a)

{

}

void func(int* b)

{

}

6)类型转换总结: 

错误的类型转换:会把常量的地址放在一个普通的指针里面!!

    int * - > const int *       --> right


    const int * -> int *       --> error!

int **- > const int **  <相当于>  int *& -> const int *  --> error! 

const int ** -> int **   <相当于> const int *& -> int *  --> error!

int* const * -> int **    -->error!

//const跟多级指针结合,必须两边都给修饰!!

5.引用:

     引用就是一个指针,无论是什么时候占四个字节。 
     C++中一个空类占一个字节(不同编译器不一样,有的不可以定义) 
    struct 
   { 
      char a; 
      char b; 
      char c; 
   }data; 
   //sizeof(data)=3

6.const 跟 */&的结合:

     引用在使用时自动解引用 
const /*/& 在一起要干嘛 ?! 
    sizeof(p)=16 (引用了一个数组) 
const /*/& 在一起要干嘛 ?!

//sizeof(p)=16

如果不好直接写引用,可以在写的时候可以先写成一个函数指针(引用数组名),然后改为引用。

const /*/& 在一起要干嘛 ?!

在这个内存地址上写一个四字节的整数10 
const /*/& 在一起要干嘛 ?!

     int p=(int )0x0018ff44 
     *p=10;

//要想定义一个引用变量,如果右边地址不可取,就弄一个常引用

int const &p=(int )0x0018ff44 
     *p=10; 
const /*/& 在一起要干嘛 ?!

下面的错误,是vc的一个bug,编译器检测不出泪,vs可以检测出来

const /*/& 在一起要干嘛 ?!

相当于将int**转化为一个const int *,从引用角度来看,p和q是同一块内存,int 和const int *不能是同一块内存。所以错误 !
const /*/& 在一起要干嘛 ?!

错误, p是一个const常量,由于*q=p,所以最后一句应改为 
      int *const*q=&p; 
const /*/& 在一起要干嘛 ?! 
    //由于*q<->p,因此给*q赋一个整型常量的地址也就是相当于给p附一个整型常量地址,但是这样会使得整型常量地址泄露,因此修改的办法有两种防止地址泄露被修改: 
1) 
const /*/& 在一起要干嘛 ?!//让*p为常量,防止它被赋值。 
2) 
const /*/& 在一起要干嘛 ?!

//禁止*q的改变,达到给p赋值的目的

7.C++11中出现了左引用和右引用。 
     引用占内存,四个字节,&b打印出来就是a的地址,引用地址就是所引用变量的地址,使用引用时,自动解引用。

1)左值引用

   左值引用只能绑定左值,不能绑定右值.

如下:

[cpp] view plain copy
  1. int x = 1;  
  2. int &y = x;                //绑定左值,正确  
  3. int &z = 2;                //绑定右值2,编译错误  
但是可以将右值绑定到一个const左值引用(这是一个例外)

如:

[cpp] view plain copy
  1. //右值绑定到const左值引用  
  2. nt const &i = 28;                 //正确  
在右值引用诞生之前,为了传递临时对象到函数中以引用,这将允许实参隐式转换到形参类型,如下:

[cpp] view plain copy
  1. void print(std::string cosnt& s) {}  
  2. print("hello");        //“hello”字符串是临时字符串对象,也是右值  


2)右值引用

   右值引用只能绑定右值,不能绑定左值.使用两个&&表示右值引用.

如:

[cpp] view plain copy
  1. int&& i = 8;              
  2. int j = 18;         
  3. int&& k = j;               //编译错误  
通过使用函数重载来决定函数参数是左值/右值,重载函数参数设置为左值引用和右值引用.

[cpp] view plain copy
  1. void fun_reference(int& a)  
  2. {  
  3.      std::cout << "左值引用:" << a << std::endl;  
  4. }  
  5.   
  6. void fun_reference(int&& a)  
  7. {  
  8.      std::cout << "右值引用:" << a << std::endl;  
  9. }  
  10.   
  11. int main()  
  12. {  
  13.     int x = 8;  
  14.     fun_reference(x);     //输出左值引用:8  
  15.     fun_reference(18);  //输出右值引用:18  
  16.     return 0;  
  17. }  

[就暂且先总结这么多吧,水平跟见识也有限,还望大家多多批评指正!!]