掘根宝典之C语言和C++中的const

const的基本概念

const名叫常量限定符,用来限定特定变量,以通知编译器该变量是不可修改的。

习惯性的使用const,可以避免在函数中对某些不应修改的变量造成可能的改动。

const修饰基本数据类型

2.1 const修饰一般常量及数组

cpp 复制代码
int const a = 100;
const int a = 100; //与上面等价
int const arr [3] = {1,2,3};
const int arr [3] = {1,2,3};//与上面等价

对于这些基本的数据类型,修饰符const可以用在类型说明符前,也可以用在类型说明符后,其结果是一样的。

2.2 const修饰指针(*)

主要有以下几种类型:

cpp 复制代码
char *p = "hello";     // 非const指针,
                       // 非const数据

const char *p = "hello";  // 非const指针,
                          // const数据

char * const p = "hello";   // const指针,
                            // 非const数据

const char * const p = "hello";  // const指针,
                                 // const数据

2.2.1 常量指针

当为常量指针时,不可以通过修改所指向的变量的值,但是指针可以指向别的变量。

cpp 复制代码
int a = 5;
const int *p =&a;
*p = 20;   //error  不可以通过修改所指向的变量的值

int b =20;
p = &b; //right  指针可以指向别的变量

2.2.2 指向常量的指针(指针常量)

当为指针常量时,指针常量的值不可以修改,就是不能指向别的变量,但是可以通过指针修改它所指向的变量的值。

cpp 复制代码
int a = 5;
int *const p = &a;
*p = 20;     //right 可以修改所指向变量的值

int b = 10;
p = &b;      //error 不可以指向别的变量

2.2.3 总结

如果const位于星号*的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量;

如果const位于星号*的右侧,const就是修饰指针本身,即指针本身是常量。

2.2.4具体举例

代码如下:

cpp 复制代码
#include<stdio.h>
int main()
{
    int a = 10, b = 20;

    int* p1 = &a;    //可以修改值 也可以修改方向
    *p1 = 100;
    p1 = &b;

    const int* p2 = &a;   //指向
    //int x2=*p2;
    //*P2=100;//error
    //p2=&b;
    int const* p3 = &a;
    //const 在指针前 修饰指向方向可以读取值 自身值可以改变
    int* const p4 = &a;
    //int x=*p4;
    // *p4=100;
    //p4=&b;//error
    //const 在指针后 修饰值本身 p4的值不可以改变 但是后面修饰的值可以
    const int* const p5 = &a;//指针前有指针后也有 均不可以改变
    return 0;
}

const修饰函数

const 在函数中根据修饰的位置分为三种:函数参数、函数返回值、成员函数。

cpp 复制代码
const  int fun (const int a) const;

3.1 const修饰函数参数

修饰函数形参;函数体内不能修改形参a的值。

如果函数作为输出用,不论是什么数据类型,也不论采用指针传递还是引用传递,都不能加const 修饰,否则参数会失去输出功能。

所以:const 只能修饰输入作用的参数

3.1.1 如果参数为指针

如果输入参数为指针,加上const之后就会起保护指针意外修改的作用。

cpp 复制代码
void StringCopy(char* strDest, const char* strSource);

在这个函数定义中,我们的的参数strsource加上const修饰,就是为了防止strsource被修改。

可以起到保护作用的原因是:
实参中,指针会指向一段内存地址,调用函数之后,函数会产生一个临时指针变量,这个变量的地址与实参的地址不一样,但是这两个指针指向的内存是同一块。形参加上const 修饰之后,保护了这一块内存地址不被修改,如果刻意修改这一块内存,编译器会报错。

3.1.2 如果参数为引用

如果输入参数为引用,加上const 之后既起到了保护作用,也提高了程序效率。

cpp 复制代码
void func(Y y);//这里的Y类型为用户定义的类型
void func(Y &y);//采用引用的方式

调用这个函数我们会产生一个临时对象,随后调用拷贝构造函数,当函数结束的时候,进行析构释放资源。

如果改成引用void func(A &a); 只是相当于实参的一个别名,不会产生临时变量。

所以,如果是自定义类型,建议用引用作为函数形参。

3.2 const修饰函数返回值

修饰返回值,就是不能修改返回值

3.2.1 值传递

如果是值传递,没有必要将返回值用const修饰

cpp 复制代码
const int func();

对于函数来说,它的返回值为一个int类型的值,是一个临时的值没有必要用const修饰。

3.2.2 返回值为指针

如果返回值为指针,加上const修饰后,同样的内容是不可以修改的。

这个时候我们接收的变量也必须是const修饰:

cpp 复制代码
const char* func();
char* str = func();// error 
const char* str = func(); //right

3.2.3 返回值为引用

如果返回值为引用,也可以提高效率。但是一般返回引用的地方并不是很多,一般会出现在类的赋值函数中。而且,用const 修饰返回值为引用类型的更少。一般来说不常用。

3.3 const修饰成员函数

const 修饰的成员函数为了保护成员变量,要求const 函数不能修改成员变量,否则编译会报错。

函数体内不能修改成员变量的值,增加程序的健壮性或鲁棒性。只有成员函数才可以在后面加const,普通函数后加const无意义。

cpp 复制代码
class MyClass {
public:
    void func(int x) const;
};

const函数的规则

  1. const 对象只能访问const 成员函数,非const 的对象可以访问任何成员函数,包括const 成员函数。
  2. 如果函数名、参数、返回值都相同的const成员函数和非const成员函数是可以构成重载,那么const对象调用const成员函数,非const对象默认调用非const的成员函数。
  3. const成员函数可以访问所有成员变量,但是只能访问const的成员函数。
  4. 非const成员函数,可以访问任何成员,包括const成员成员函数。
  5. const成员函数不能修改任何的成员变量,除非变量用mutable修饰。

类中定义变量(const的特殊用法)

在类中实现常量的定义大致有这么几种方式实现:使用枚举类型,使用const或static

4.1使用枚举类型

cpp 复制代码
class test
{
     enum { a = 10, b = 20}; // 枚举常量
     int array1[a];  
     int array2[b];
};

4.2 使用const或static

C++11仅不允许在类声明中初始化static非const类型的数据成员。

cpp 复制代码
// using c++11 standard
class CTest11
{
public:
    static const int a = 3; // Ok in C++11
    static int b = 4;       // Error
    const int c = 5;        // Ok in C++11
    int d = 6;              // Ok in C++11
public:
    CTest11() :c(0) { }     // Ok in C++11
};
 
int main()
{
    CTest11 testObj;
    cout << testObj.a << testObj.b << testObj.c << testObj.d << endl;
    return 0;
}

4.3 总结

在不同的标准下 是略微有所不同的

  1. 对于static const 类型的成员变量不管是旧的C++标准还是C++11都是支持在定义时初始化的。
  2. 对于static 非const类型的成员变量C++03和C++11的标准都是不支持在定义时初始化的。
  3. 对于const 非static类型的成员变量C++03要求必须在构造函数的初始化列表中来初始化,而C++11的标准支持这种写法,同时允许在定义时进行初始化操作。
  4. 对于非static 非const成员变量,C++03标准不允许在成员变量定义时初始化,但是C++11标准允许在类的定义时对这些非静态变量进行初始化。
  5. 对于static非const成员变量的初始化方式并未改变,就是在相应的cpp文件中写成int CTest11::b = 5即可,注意要在类定义之后。

五 使用const的好处

可以定义const常量

这样可以避免由于无意间修改数据而导致的编程错误,提供了一个保护作用。

便于进行类型检查

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

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

const修饰的函数可以看作是对同名函数的重载。

可以节省空间,避免不必要的内存分配

const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是象宏一样给出的是立即数,所以,const定义的常量在程序运行过程中只有一份拷贝,而宏定义的常量在内存中有若干个拷贝。

提高了效率

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

const在C和C++中的区别

在C语言中,用const修饰的变量,其本质上还是个变量,只是它不允许作为左值存在,也就是不法对该变量进行直接赋值修改该变量,但是这不意味着该变量的值就无法修改。

在C++中 ,在编译的时候,当碰到用const修饰的变量时,编译器是直接将变量的值和变量的符号对应起来一起存到符号表。

例如const int a = 5;在符号表中就会将a和5对应起来,在编译的过程中,当碰到printf("a = %d\n",a); 时,用 5 直接将 a 替换掉。在C++中,编译器不会为a分配存储空间,在C语言中就会为a分配存储空间,所以在C编译器中,就可以通过指针来改变用const修饰的变量。

其实在C++中,const修饰的变量,在编译的时候如果前面有extern和取地址符 & 时,会为变量分配存储空间是为了兼容C语言,但是在C++中,用const修饰的变量就真的无法修改它的值,可以说是常量。

但是在C语言中,const修饰的变量本质上还是变量而不是常量。

这在C语言中其实也是矛盾的,因为我们想要用const定义一个常量,但又可以通过指针来改变该常量的值。

而C++为了兼容C语言,所以保留了这个特性,但是却无法修改它的值,这就是const在C语言和C++中的区别。

相关推荐
EricWang1358几秒前
[OS] 项目三-2-proc.c: exit(int status)
服务器·c语言·前端
我是谁??2 分钟前
C/C++使用AddressSanitizer检测内存错误
c语言·c++
小码农<^_^>4 分钟前
优选算法精品课--滑动窗口算法(一)
算法
羊小猪~~6 分钟前
神经网络基础--什么是正向传播??什么是方向传播??
人工智能·pytorch·python·深度学习·神经网络·算法·机器学习
软工菜鸡32 分钟前
预训练语言模型BERT——PaddleNLP中的预训练模型
大数据·人工智能·深度学习·算法·语言模型·自然语言处理·bert
南宫生34 分钟前
贪心算法习题其三【力扣】【算法学习day.20】
java·数据结构·学习·算法·leetcode·贪心算法
发霉的闲鱼36 分钟前
MFC 重写了listControl类(类名为A),并把双击事件的处理函数定义在A中,主窗口如何接收表格是否被双击
c++·mfc
小c君tt39 分钟前
MFC中Excel的导入以及使用步骤
c++·excel·mfc
希言JY44 分钟前
C字符串 | 字符串处理函数 | 使用 | 原理 | 实现
c语言·开发语言
午言若1 小时前
C语言比较两个字符串是否相同
c语言