const 关键字

零,常量和只读变量(不可变的变量)

const int maxn = 10;
int a[maxn];

//C程序报错
//error:variably modified 'a' at file scope
//c++ 编译通过

原因:

常量 与 只读变量 (不可变的变量)的区别:

  • 常量 肯定是只读的,例如数字6, 字符串“abc”等,肯定是只读的,因为程序中根本没有地方存放它的值,当然也就不能够去修改它。
  • 只读变量 则是在内存中开辟一个地方来存放它的值,只不过这个值由编译器限定不允许被修改。
  • C语言 规定数组定义时下标 必须是 常量只读变量 是不可以的。

const

  • C语言中,const 就是用来限定一个变量不允许被改变的修饰符,即只读变量,因为占有存储空间,所以编译器不知道编译时的值,所以就不知道该给数组定义多大的。
  • C++ 中, const修饰的 可以看成是编译期的常量,但是其是占有内存空间的

一,C语言中的const

在c语言中, const 不是常量,只能说是不可改变的变量,C的编译器,不能将const修饰的变量看成一个编译期间的常量,因为其在内存中有分别,c编译器不能知道在编译期间的值,所以不能作为数组定义的下标。

const int a=10;
int Array[b];  //编译错误

const 类名 变量名 与 类名 const 变量名 同义 

1,初始化问题

在c语言中,const int a;因为a只是个变量,且只会分配存储在“只读数据段”中内存,只读数据段中存放着常量和只读变量等不可修改的量。未初始化时,修饰全局变量默认的是0,修饰的局部变量是未知值。无论是全局变量还是局部变量,生存周期都是程序运行的整个过程。

2,使用说明

1) 修饰数组时

      const int a[]={1,2,3,4,5,5}

数组变量表明已经是const的指针了,这里的const表明数组的每个单元都是const int 所以必须通过初始化进行赋值。

2)修饰一般变量

 与c++中用法一样。

二,C++中的const修饰符

c++中的const 修饰变量,和宏定义基本一样,但其是占用存储空间的。  const修饰的变量内存存放问题,不同的编译器会有不同的方式。一般如下两点:

首先,对于函数体内或者类中,baseline是该怎么分配就怎么分配,根据这个object的storage class来分配,跟const修饰符无关。

然后,对于函数体外,全局变量的const变量根据编译器的不同而不同,如果这个常量同时是编译期已知的,并且你没有取址或者extern声明之类的操作,则编译器可以选择将这个常量optimize out不分配前提是这个优化不改变你的代码的行为 且你没有引发UB(比如const_cast并强行修改常量),同时,函数体外的const变量默认为文件的局部变量

 可以修饰一般变量,成员变量,成员函数,函数的返回值,函数的参数,类的对象。

  • 修饰一般变量

const常量,具有不可变性。在常变量,常引用,常对象,常数组,const与“类型说明符”或“类名”的位置可以互换。

const int a=5; 与int const a=5; 等同
类名 const 对象名 与const 类名 对象名 等同

//对于const修饰指针时除外
const int* ptr 与int* const ptr,不等同

const修饰指针时的含义是不同的。当用const进行修饰时,根据const位置的不同有三种不同效果。 判断的标准是:const修饰谁,谁的内容就是不可变的。

int a = 10;
const int*p = &a;   //修饰*p, p指向的内存单元内容不可变。指针p指向可变。
a = 12;      //OK
*p=24;       //ERROR
p++;         //OK

int const *p = &a;  //修饰*p, p指向的内存单元内容不可变。指针p指向可变。
a = 13;      //OK
*p=25;       //ERROR
p++;         //OK

int* const p = &a;  //修饰p, p指向的内存单元内容可变,指针p指向不可变。
*p=26;       //OK
p++;         //ERROR

const int* const p = &a; //p指向的内存单元内容不可变,指针p指向不可变。
*p=26;       //ERROR
p++;         //ERROR

这些可以总结成这样一句话:
以*为分界点, 
当const在*的左边时,实际物体值不变
当const在*的右边时,指针不变

  • 修饰成员变量

被const修饰的变量只能通过参数列表初始化或者声明的时候初始化。且不能被修改

class A{
public:
    A():m_bflag(false){}   //const修饰的成员变量参数列表初始化
private:
    const bool m_bflag;
    const int m_iStatus = 0; //const修饰的成员变量类内声明时初始化
};
  • 修饰成员函数

const 修饰成员函数时,在函数的声明和定义时都必须包括const修饰符。

函数申明后,函数体之前:承诺,函数不会修改数据成员,同时只能调用const成员函数。static函数是不允许加入修饰符的

class A{
public:
  bool getFlag() const { 
     int idx = getIndex()  //编译通过
     return m_bflag;
  } 
  
  int getIndex()const{
     test();     //编译报警,不能调用非const成员函数 
     return 1;
  }
  void test(){
     return;
  }
  static void test1() const {}  //编译告警
private:
  bool m_bflag;
};
  • 修饰函数参数

防止传入的参数代表的内容在函数体内被改变,但仅对指针和引用有意义。因为如果是按值传递,传给参数的仅仅是实参的副本,即使在函数体内改变了形参,实参也不会得到影响。

  • 修饰函数返回值

修饰函数返回值时,如果返回类型时一般数值时,一般没有意义,如果返回值是指针或者引用时,保护指针指向的内容或引用的内容不被修改,也常用于运算符重载。归根究底就是使得函数调用表达式不能作为左值

class A{
public:
    const int getIndex(){return 1;}
    const int& getNumA(){return 2;}
    int& getNumB() {return 3;}
    const char * getName(){return m_name.c_str();}
private:
    string m_name;
};

int idx = getIndex();    //可以赋值给非const数值变量
int num = getNumA() = 4;  //error const修饰的引用返回值不可以作为左值
int num = getNumB() = 5;  //正确 非const修饰的引用返回值不可以作为左值

char* name1 = getName();   
//error const修饰的指针只能被const修饰的同类型指针接收,否则需要强转const_cast<>()
char* name2 = const_cast<char*>(getName());
const char* name2 = getName();
  • 修饰类的对象

const 修饰类的对象,只能调用const 修饰的函数,不能调用非const函数 

class A{
public:
    int getIndex() const{
        return m_iIdex;
    }
    void setIndex(int idex){
        m_iIdex = idex;
    }
public:
    int m_iIdex;
    mutable int m_num;
};

const A a;
a.setIndex(1);    //error,const修饰的对象只能调用const函数
a.getIndex();     //编译通过,const修饰的对象只能调用const函数
a.m_num = 2;      //编译通过,mutable,说明符可以修改
a.m_iIdex = 1;    //error,const修饰的对象的成员为const
  • const_cast

在c++中,提供了const_cast<new_type>(expression)强转运算符用来移植变量的const或volatile的限定,后者涉及多线程设计,不做讨论,对于const变量,不能修改它的值,这是限定符的最直接表现,const_cast并不能改变其本身的const常量属性

const int constant = 20;
const int *const_p = &constant;
int *modifier = const_cast<int*>(const_p);
//int *modifier = const_cast<int*>(&constant);
*modifier = 7;


cout << "const_p: "<< *const_p <<endl;
cout << "modifier: "<< *modifier <<endl;
/**
constant: 21
const_p: 7
modifier: 7
**/

//constant还是保留了它原来的值。可是它们的确指向了同一个地址呀:
cout << "constant: "<< &constant <<endl;
cout << "const_p: "<< const_p <<endl;
cout << "modifier: "<< modifier <<endl;

/**
constant: 0x7fff5fbff72c
const_p: 0x7fff5fbff72c
modifier: 0x7fff5fbff72c
**/

上述说明,c++中的const是真的constant。对于修改const变量,属于:未定义行为(不可以预测的)。 虽然指向相同的地址,内存里的值也改变了,但是编译器并不从内存地址里读取数据,而是直接用用初始值替换了,类似于#define。原因是因为c++的常量折叠:指const变量(即常量)值放在编译器的符号表中,在预编译时已经替换,不会去访问内存里的值。

如下提供了可修改const修饰的变量的方法:

方法一:前提本体必须是非const变量
int num = 10;
const int* const_p = &num;
int *ptr = const_cast<int*>(const*p); 
*ptr = 20;
//num = 20;

string str = "1234";
char *p=const_cast<char*>(str.c_str());
strcpy(p, "abcd");
//str=abcd

方法二:
const volatile int a = 10;
a=20;

三,C/C++中的const修饰符的不同

1,C标准中,const定义的常量是全局的,生命周期随整个程序。  C++中视声明位置而定。

2,const 定义的是只读变量,就相当于是定义一个常量。但是只读变量也是变量,所以 const 定义的变量仍然不能作为数组的长度。但是需要注意的是,在 c++中可以!C++ 扩展了 const 的含义,在 C++ 中用 const 定义的变量也可作为数组的长度。

const int size = 10;
int array[size];   //c++中编译通过,c语言中编译报价

3,在C中,const int a;是可以的,并且会分配内存。在C++中,必须初始化,C++为了起到和c语言一样的效果,需要将const修饰为extern,在c++ 中const 对象默认为文件的局部变量。与其他变量不同,除非特别说明,在全局作用域声明的 const 变量是定义该对象的文件的局部变量。此变量只存在于那个文件中,不能被其他文件访问。通过指定 const 变更为 extern,就可以在整个程序中访问 const 对象。

4,c语言中没有const修饰函数的语义。c++可以修饰函数,但该函数必须时成员函数。

void func() const {} //编译警告
class A{
public:
    void fun() const;
}
void A::fun() const{}  //编译通过

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值