【C++】C 语言 和 C++ 语言中 const 关键字分析 ( const 关键字左数右指原则

您所在的位置:网站首页 地址是变量还是常量 【C++】C 语言 和 C++ 语言中 const 关键字分析 ( const 关键字左数右指原则

【C++】C 语言 和 C++ 语言中 const 关键字分析 ( const 关键字左数右指原则

2024-07-13 05:19| 来源: 网络整理| 查看: 265

一、C 语言 const 关键字简介 - 左数右指原则

【C 语言】const 关键字用法 ( 常量指针 - const 在 * 左边 - 修饰数据类型 - 内存不变 | 指针常量 - const 在 * 右边 - 修饰变量 - 指针不变 )

1、const 关键字左数右指原则

普通类型数据的常量定义时 , const 关键字 在 数据类型 的 左边 和 右边 其作用 是相同的 ;

代码语言:javascript复制 // 下面两种 const 用法效果相同 // 定义普通类型 ( 非指针类型 ) 的常量 const 在 类型左右 都是相同的 const int a = 10; int const b = 20;

指针数据的相关常量类型 :

const 关键字在 指针符号 * 左侧 表示该定义的事 常量指针 ( 指向的内存不能修改 )代码语言:javascript复制 // 左数右指 : const 在指针左边 数据是常量 , const 在指针右边 指针是常量 // 下面两种情况 const 在指针左边 , 数据是常量 , 内存中的数据不能修改 // 但是 , c 和 d 指针的指向可以修改 // 下面两种情况是相同的 const int* c; int const* d;const 关键字在 指针符号 * 右侧是 表示定义的事指针常量 ( 指针本身不能被修改 ) ;代码语言:javascript复制 // 左数右指 : const 在指针左边 数据是常量 , const 在指针右边 指针是常量 // 下面的情况 const 在指针右边 , 指针是常量 , 指针地址不能修改 // 但是 , 指针指向的内存中的数据可以修改 int* const e = (int*)malloc(10);

指针常量与常量指针 : 需要查看 const 修饰的是 指针变量 , 还是 修饰 指针变量 指向的内存空间 ;

const 在 * 右边 ( 指针常量 | const 修饰的是变量 ) : 如果 const 修饰的是 指针变量 , 如 char * const d , const 修饰的是 char * , 指针不能被修改 ; 这是 指针常量 ; const 在 * 左边 ( 常量指针 | const 修饰的是数据类型 ) : 如果 const 修饰的是 指针变量 指向的内存空间 , 如 const char *c , const 修饰的是 char , char 数据不能被修改 , 这是 常量指针 , 指向常量的指针 ; 2、代码示例 - const 关键字左数右指原则

下面的代码中 , 列出了 const 关键字的所有情况 , 看注释即可理解左数右指原则 ;

代码示例 :

代码语言:javascript复制// 导入标准 io 流头文件 // 其中定义了 std 命名空间 //#include // 导入 std 命名空间 //using namespace std; #include #include int main() { // 下面两种 const 用法效果相同 // 定义普通类型 ( 非指针类型 ) 的常量 const 在 类型左右 都是相同的 const int a = 10; int const b = 20; // 左数右指 : const 在指针左边 数据是常量 , const 在指针右边 指针是常量 // 下面两种情况 const 在指针左边 , 数据是常量 , 内存中的数据不能修改 // 但是 , c 和 d 指针的指向可以修改 // 下面两种情况是相同的 const int* c; int const* d; // 左数右指 : const 在指针左边 数据是常量 , const 在指针右边 指针是常量 // 下面的情况 const 在指针右边 , 指针是常量 , 指针地址不能修改 // 但是 , 指针指向的内存中的数据可以修改 int* const e = (int*)malloc(10); // 左数右指 : const 在指针左边 数据是常量 , const 在指针右边 指针是常量 // 下面两种情况 const 在指针左边和右边 , 数据和指针是常量 , 都不能修改 // 下面两种情况是相同的 const int* const f = (int*)malloc(10); int const* const g = (int*)malloc(10); return 0; }3、const 关键字使用场景

const 关键字 一般用于修饰 函数参数 , 给函数传入的参数 如果不想 用户在方法中 修改 数据 或 指针 , 可以使用 const 关键字修饰 形参 ;

定义结构体 :

代码语言:javascript复制struct Student { char name[64]; int age; };

函数接收上述结构体类型变量作为参数 , 如果参数中 const 在 * 左边 , const Student *pS , 根据 左数右指原则 , 指针指向的数据是常量 , 不能被修改 ;

下面是错误示范 :

代码语言:javascript复制// 左数右指 , const 在指针左边 , 指针指向的数据不能被修改 int fun0(const Student *pS) { pS->age = 20; return 0; }

如果强行修改指针指向的数据值 , 就会在编译时报错 :

代码语言:javascript复制表达式必须是可修改的左值在这里插入图片描述在这里插入图片描述

函数接收上述结构体类型变量作为参数 , 如果参数中 const 在 * 右边 , Student* const pS , 根据 左数右指原则 , 指针本身是常量 , 指针指向不能被修改 ;

下面是错误示范 :

代码语言:javascript复制// 左数右指 , const 在指针右边 , 指针本身的指向不能被修改 int fun2(Student* const pS) { pS = NULL; return 0; }

如果强行修改指针指向 , 就会在编译时报错 :

代码语言:javascript复制表达式必须是可修改的左值在这里插入图片描述在这里插入图片描述

上述完整代码示例 :

代码语言:javascript复制// 导入标准 io 流头文件 // 其中定义了 std 命名空间 //#include // 导入 std 命名空间 //using namespace std; #include #include struct Student { char name[64]; int age; }; // 左数右指 , const 在指针左边 , 指针指向的数据不能被修改 int fun0(const Student *pS) { //pS->age = 20; return 0; } // 左数右指 , const 在指针右边 , 指针本身的指向不能被修改 int fun2(Student* const pS) { //pS = NULL; return 0; } int main() { // 下面两种 const 用法效果相同 // 定义普通类型 ( 非指针类型 ) 的常量 const 在 类型左右 都是相同的 const int a = 10; int const b = 20; // 左数右指 : const 在指针左边 数据是常量 , const 在指针右边 指针是常量 // 下面两种情况 const 在指针左边 , 数据是常量 , 内存中的数据不能修改 // 但是 , c 和 d 指针的指向可以修改 // 下面两种情况是相同的 const int* c; int const* d; // 左数右指 : const 在指针左边 数据是常量 , const 在指针右边 指针是常量 // 下面的情况 const 在指针右边 , 指针是常量 , 指针地址不能修改 // 但是 , 指针指向的内存中的数据可以修改 int* const e = (int*)malloc(10); // 左数右指 : const 在指针左边 数据是常量 , const 在指针右边 指针是常量 // 下面两种情况 const 在指针左边和右边 , 数据和指针是常量 , 都不能修改 // 下面两种情况是相同的 const int* const f = (int*)malloc(10); int const* const g = (int*)malloc(10); return 0; }二、C 语言 const 关键字原理分析1、C 语言中常量的原理和缺陷

C 语言中的 const 关键字 并不是 真正的 " 常量 " , 是一个 " 冒牌货 " ;

C 语言中的 const 关键字定义的常量 , 其本质是在 内存 中分配的空间 ;

C 语言 中 , 会为 const 常量 单独分配内存 , 导致 用户可以 通过取地址符 & 获取该内存的地址指针 , 通过该指针可以修改内存中的数据 ;

2、代码示例 - C 语言中直接改变常量值报错

定义一个常量 const int a = 10; , 为该常量值 a 赋值 , 会报错 error: assignment of read-only variable 'a' ;

代码示例 :

代码语言:javascript复制#include int main() { // 定义常量 const int a = 10; // 下面的代码会报错 , 貌似 a 是常量 a = 20; return 0; }

编译时的报错信息 :

代码语言:javascript复制C:\Users\octop\Desktop>gcc hello.c hello.c: In function 'main': hello.c:9:7: error: assignment of read-only variable 'a' a = 20; ^ C:\Users\octop\Desktop>在这里插入图片描述在这里插入图片描述3、代码示例 - C 语言中使用常量地址修改常量值

如果使用 指针 变量 , 接收 常量 a 的地址 , 然后通过该指针修改 指针指向的 内存空间的值 , 然后再打印 常量 a 的值 , 发现 常量 a 的值发生了改变 ;

因此 , C 语言中的常量 , 是可以通过指针进行修改的 ;

代码示例 :

代码语言:javascript复制#include int main() { // 定义常量 const int a = 10; // 下面的代码会报错 , 貌似 a 是常量 //a = 20; // 定义一个指针 int* p = NULL; // 将 常量 a 的地址赋值给指针 p = (int *)&a; // 通过指针修改 常量 a 的值 *p = 20; // 打印 a 的值 printf("a = %d\n", a); return 0; }

执行结果 :

代码语言:javascript复制C:\Users\octop\Desktop>gcc hello.c C:\Users\octop\Desktop>a.exe a = 20 C:\Users\octop\Desktop>在这里插入图片描述在这里插入图片描述

将相同的代码 , 拷贝到 C++ 环境中 , 编译运行的结果 , 与 C 语言环境中的编译运行结果不同

代码语言:javascript复制a = 10 Y:\002_WorkSpace\002_VS\HelloWorld\HelloWorld\Debug\HelloWorld.exe (进程 18604)已退出,代码为 0。 要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。 按任意键关闭此窗口. . .在这里插入图片描述在这里插入图片描述

出现上述问题 , 是因为 C 语言 中 , 会为 const 常量 单独分配内存 , 导致 用户可以 通过取地址符 & 获取该内存的地址指针 , 通过该指针可以修改内存中的数据 ;

三、C++ 语言 const 关键字 - 符号表存储常量1、C++ 语言中常量原理

C++ 语言中 使用 const 关键字 定义的常量 , 是真正的 " 常量 " ;

C++ 编译器 对 const 关键字 修饰 的常量 , 进行了 特殊处理 ;

C++ 编译器 扫描到 const int a = 10; 代码后 , 发现 const 常量 , 不会为其单独分配内存 , 而是 将 常量 a 放在 符号表 中 ,

符号表 中的数据是以 " 键值对 " 的形式存在的 , 一个 键 Key , 对应一个值 Value ;

反映到 const int a = 10; 代码中 , 键 Key 是 a , 值 Value 是 10 , 在之后的代码 使用 常量 a 时 , 会直 从 符号表 中取出 10 ;

在下面的代码中 , 使用指针 p 获取 常量 a 的地址 , 获取的并不是 符号表 中 常量 a 的地址 , 而是 从 符号表中 取出常量 const int a = 10 , 为其 分配一个内存空间 , 将 10 存进去 , 然后将首地址返回 赋值给指针 p ;

实际上 指针 p 指向的是一个内存空间 , 内存空间中的值是 常量 a 的值 , 但是此时与常量 a 没有关系了 , 该值可以被修改 ;

代码语言:javascript复制 // 定义常量 const int a = 10; // 下面的代码会报错 , 貌似 a 是常量 //a = 20; // 定义一个指针 int* p = NULL; // 将 常量 a 的地址赋值给指针 p = (int *)&a; // 通过指针修改 常量 a 的值 *p = 20;

对比 C 语言 中 , 会为 const 常量 单独分配内存 , 导致 用户可以 通过取地址符 & 获取该内存的地址指针 , 通过该指针可以修改内存中的数据 ;

2、代码示例 - 分析指针指向的值和实际常量值

修改上述代码 , 在不同的时间获取 *p 指向的内存空间值 和 常量 a 的值 ;

发现 使用指针 接收 常量 a 的地址 , 是在内存中重新分配内存并赋值为 10 , 并没有获取到符号表的内存地址 ;

修改内存中的值 , 不会影响到 符号表 中常量 a 的值 ;

代码示例 :

代码语言:javascript复制#include int main() { // 定义常量 // 该常量定义在了 符号表 中 // 符号表 不在内存四区中 , 是另外一种机制 const int a = 10; // 下面的代码会报错 , 貌似 a 是常量 //a = 20; // 定义一个指针 int* p = NULL; // 将 常量 a 的地址赋值给指针 // 在 堆内存中重新 分配一个 4 字节的空间 // 将 常量 a 的值 10 存储进去 p = (int *)&a; // 打印 a 和 *p 的值 // 此时 还没有修改 *p 的值 , 两个值都是 10 printf("a = %d , *p = %d\n", a, *p); // 通过指针修改 常量 a 的值 *p = 20; // 打印 a 和 *p 的值 // 此时 通过你指针修改了 *p 的值 , *p 是 20 , 常量 a 仍为 10 printf("a = %d , *p = %d\n", a, *p); return 0; }

执行结果 :

在这里插入图片描述在这里插入图片描述


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3