const的用法
如何理解const:
const修饰的变量不能再作为左值。初始化以后,值不能被修改。
1.C和C++中的const的区别是什么?
1.初始化
C++的const变量一定要初始化
C的const变量可以不要初始化
2.常量
C++的const变量是常量,,由编译器来将出现常量的地方用其初始值替换。因此可以用来创建数组。但是如果用变量来初始化const常量,那么也会退化成常变量。
C的const变量是常变量,只是把其看作一个变量来生成指令,但是会检测用户对其的修改。因此不可以用来创建数组,且可以被修改(见3)。
int main() {
const int a=10;
/*
CPP里面是可以的
C里面是不可以的
*/
int arr[a] = {};
return 0;
}
3.const修饰的变量,我能不能通过其指针对其修改?
-
CPP文件里面:
//main.cpp int main() { const int a=10; int* p = (int*)&a; *p = 30; printf("%d %d %d \n", a, *p, *(&a)); return 0; } /* 输出结果: 10 30 10; */
其汇编指令如下
const int a=10; 00007FF6397C500D mov dword ptr [a],0Ah # 给a赋值 int* p = (int*)&a; 00007FF6397C5014 lea rax,[a] 00007FF6397C5018 mov qword ptr [p],rax # 指令把a的地址赋值给了p *p = 30; 00007FF6397C501C mov rax,qword ptr [p] 00007FF6397C5020 mov dword ptr [rax],1Eh # 指令通过p修改了a的内容 printf("%d %d %d \n", a, *p, *(&a)); 00007FF6397C5026 mov r9d,0Ah # 获取a的值 00007FF6397C502C mov rax,qword ptr [p] 00007FF6397C5030 mov r8d,dword ptr [rax] # 获取了*p的值 00007FF6397C5033 mov edx,0Ah # 注意此处:直接把10赋值给edx,而并不是从a的地址处取值。 00007FF6397C5038 lea rcx,[string "%d %d %d \n" (07FF6397CACA8h)] 00007FF6397C503F call printf (07FF6397C11B3h)
-
C文件下:
// test.c文件 #include<stdio.h> void main() { const int value = 10; int* ptr = (int*) &value; *ptr = 321; printf("%d %d %d\n", value, *ptr, *(&value)); return 0; } /* 显示结果: 321 321 321 */
其汇编指令如下:
const int value = 10; 00007FF6A5D31A0D mov dword ptr [value],0Ah int* ptr = (int*) &value; 00007FF6A5D31A14 lea rax,[value] 00007FF6A5D31A18 mov qword ptr [ptr],rax *ptr = 321; 00007FF6A5D31A1C mov rax,qword ptr [ptr] 00007FF6A5D31A20 mov dword ptr [rax],141h printf("%d %d %d\n", value, *ptr, *(&value)); 00007FF6A5D31A26 mov r9d,dword ptr [value] 00007FF6A5D31A2A mov rax,qword ptr [ptr] 00007FF6A5D31A2E mov r8d,dword ptr [rax] 00007FF6A5D31A31 mov edx,dword ptr [value] # 此处是从value的地址获取数据,而不是编译器优化直接用数据替代 00007FF6A5D31A34 lea rcx,[string "%d %d %d\n" (07FF6A5D3AD28h)] 00007FF6A5D31A3B call printf (07FF6A5D3119Ah)
总结:
由此可见,对指针进行修改是可以的,但是编译器对代码进行了优化,会直接用const变量的值来替换此处的*(&value)。
3.思考
那么再思考:(看完3的汇编再来看这个)
1.如果我用变量来初始化const常量会怎么样?
2.上述情况下,再通过指针修改const会怎么样?
答:可以,因为退化成了常变量
//问题一:如果我用变量来初始化const常量会怎么样?
int a = 20;
const int b = a;
//如果修改a的值,那么b变化吗?
cout << a << " " << b << endl; // 20 20
a = 30;
cout << a << " " << b << endl;// 30 20.此处还是被20替换
//如果通过const创建数组呢?
int arr[b]={0};//编译不通过
//问题二:上述情况下,再通过指针修改会怎么样?
int main() {
int a = 20;
const int b = a;
//如果修改a的值,那么b变化吗?(不会)
cout << a << " " << b << endl; // 20 20
a = 30;
cout << a << " " << b << endl;// 30 20.此处还是被20替换
//此使再通过指针修改const可以吗?(可以,退化成了常变量)
int* p = (int*)&b;
*p = 99;
cout << a << " " << b<<" "<<*p << endl;//30 99 99
return 0;
}
2. const和一级指针的结合
2.1 const指针类型
C++语言规范:const修饰的是离它最近的类型
-
const int * p;
此处const修饰int 因此*p不可以变
-
int const * p;
*无法单独作为一个类型,因此此处const还是修饰int 同上
-
int * const p;
可以作为一个指向int类型的指针,因此const修饰int *。因此p不可变,*p可以变
-
const int * const p;
前面一个const修饰int*;后面一个const修饰 int* 。因此均不可变。
类型 | const int * p | int const* p | int *const p | const int * const p |
---|---|---|---|---|
修饰谁 | const修饰int | * 不能作为一直类型,因此还是修饰 int | int *表示指向整型的指针 因此修饰int* | 前面的修饰int,后面修饰int * |
可以的语法 | const int* p = &a; p = &b;可以对p进行重新赋值 | 同左 | int* const p = &a; *p = 10086;可以通过p修改原数据 | 无 |
不可以的语法 | const int * p = &a;*p = 100;不能通过p修改数据 | 同左 | const int* p = &a;p = &b; 不可以修改p | const int* const p = &a; p = &b; *p = 10086; 无论是修改p还是修改* p都不可以 |
2.2const指针转换
一级指针的时候
-
int * —> const int * 是可以的
-
const int * —> int * 是不可以的
二级指针的时候(先看后文)
-
const修饰的是int
- const int ** —>int **是错误的
- int ** —> const int ** 也是错误的
-
const修饰的实际上是int*
- int* const * —> int** 是错误的
- int** —> int * const * 是正确的
问:int* const p 能不能把p赋值给int *类型?
答案:可以
int a = 99;
int* q = &a;
cout << typeid(q).name() << endl; //int * __ptr64
cout << endl;
const int* const p = &a;
cout << typeid(p).name() << endl;//int const * __ptr64
cout << typeid(&p).name() << endl;//int const * __ptr64 const * __ptr64
cout << endl;
int const* pp = &a;
cout << typeid(pp).name() << endl;//int const * __ptr64
cout << typeid(&pp).name() << endl;//int const * __ptr64 * __ptr64
cout << endl;
int* const ppp = &a;
cout << typeid(ppp).name() << endl;//int * __ptr64 注意:const不参与类型
int* a1 = ppp;//因此相当于int* 赋值给int *
2.3总结
- const如果右边没有指针*,const是不参与类型的
- 含有const的变量,其指针类型为在const右边加*
- 含有const的指针变量,解地址的类型是在const右边去一个*。
3. const和二级指针的结合
为什么不能从int **
转换为 const int **
假设可以。
int main() {
int value = 10;
int* p1 = &value;
int** pp1 = &p1;
const int** pp2 = (const int**)pp1; // 假设这个转换是合法的
const int newValue = 20;
*pp2 = &newValue; // *pp2 是 const int* 类型
*p1 = 30; // 修改 value 通过 p1, 但 p1 应该指向 const 数据
return 0;
}
如果 const int** pp2 = (const int**)pp1
; 这行代码合法,那么通过 pp2
我们可以将 p1
指向 const int
类型的 newValue
。然而,因为 p1
实际上是 int*
类型,我们最终可以通过 p1
修改它所指向的 newValue
的值,这显然违反了 const
的约束。因此,为了避免这种类型的安全隐患,C++ 标准不允许这样的转换。
正确做法:
如果你需要持有一个指向 const int
的指针的指针,应该直接从 const int
开始:
const int value = 10;
const int* p1 = &value;
const int** pp1 = &p1;