const与指针
一、const与指针
1.const在C和C++中的区别
【注】const在C和C++中的使用方式不同,C语言中,使用const修饰变量时,是以变量为主的;但是在C++中,const修饰变量,是以“常性”为主的,也就是说,使用const定义变量之后,是以常性为主的,遇见常变量会用值去替换掉
常性替换是在编译阶段进行替换
#include <stdio.h>
// C const
int main()
{
const int n = 10; // C语言中const修饰变量时,以变量为主,也就是说,const针对的是变量
int arr[n] = { 1,2 }; // C语言中此定义错误,因为其会认为n就是一个变量,虽然加了const修饰,仍然是变量,定义数组时数组大小是一个大于零的整形常量,
// 上述定义会出现类型错误,
return 0;
}
// C++ const
#include <iostream>
using namespace std;
int main()
{
const int n = 10; // C++在修饰变量的时候,是以“常性”为主
int arr[n] = { 1,2 }; // 在C++中此定义合法,会用整数值10取替换n,等价于:int arr[10] = { 1,2 };
return 0;
}
1.1 如果在C语言中使用const去定义变量,然后再去定义数组,则会提示:类型错误
因为C的编译器会认为n是一个变量,即使用const去修饰,其仍然是一个变量,只不过const限制了其“写”操作
const int n = 5;
int b = n; // C语言中,从变量n这个内存空间取值赋值给整形变量b,是需要访问内存空间
int b = n; // 对于C++,在编译的过程中,遇见n就会将其替换成5
1.2 C编译
按照C的编译方法,虽然a用const修饰,但是可以通过p去改变a的值,在对内存进行访问,将a的值100(通过p去修改,此时为100)赋值给b,b的值也是100;所以按照C的编译方式,输出的结果为:100 100 100
【注】b = a; // C编译时,会借助一个“临时空间”,先将a的值取出来存放在临时空间,在将临时空间的值赋给b
1.3 C++编译
但是使用C++的方式编译,输出的结果为10 10 100。
- *p=100; // 将 *p的值改为100
- b = a; // C++编译时,直接将a的值替换成10,然后再将10赋值给b
- 所以输出为:10 10 100
【注】C++编译时,遇见const修饰的变量时(直接使用常变量这个值得时候),会先将其替换成具体的数值
二、const和指针的关系
1. 常变量与指针
#include <stdio.h>
int main()
{
int a = 10, b = 10;
int* p1 = &a; // p1是一个普通指针
const int* p2 = &a; // 指向为常性(解引用为常性)const修饰的是“*”
//int const* p2 = &a; // 等价于const int* p2 = &a
int* const p3 = &a; // 指针变量自身为常性(const修饰的是指针p3)
const int* const p4 = &a; // 指向(解引用)和指针变量自身都为常性
}
int main()
{
int a = 0;
int* p1 = &a; // OK
const int* p2 = &a; // OK
int* const p3 = &a; // OK
const int* const p4 = &a; // OK
return 0;
}
对于使用C方式编译:
// 按照C编译方式,a为一个常变量,丢弃了“写”操作,只读
int main()
{
const int a = 10;
int* p1 = &a; // error 编译不通过,因为可以使用普通指针解引用去改变a的值 --- 能力扩张
const int* p2 = &a; // ok const修饰指向(*),所以不能通过p2解引用去修改a的值,但是指针p2本身可以修改
int* const p3 = &a; // error const修饰指针p3,不允许修改p3,但是其指向,也就是通过对p3解引用修改a的值 --- 能力扩张
const int* const p4 = &a; // ok 指针本身和其指向均不能被修改
int* p5 = (int*)&a; // 强转,C语言中的“强盗”,虽然编译可以通过,但是不安全
}
2. 同类型指针的赋值兼容规程
// C编译方式
int main()
{
int a = 10, b = 20;
int* p = &a; // 定义一个普通指针p指向a的地址
int* s1 = p; // ok 定义一个普通指针s1指向p指向的地址,也就是s1和p都指向a的地址
const int* s2 = p; // ok const修饰指向,能力缩小
int* const s3 = p; // ok 指针s3为常性,但可以通过解引用修改a的值(允许)
const int* const s4 = p; // ok
}
3. 练习
练习1:
int main()
{
int a = 10, b = 20;
const int* p = &a; // const修饰指向,不能通过对p解引用去修改a的值,但是可以修改指针p本身
int* s1 = p; // error 能力扩张,
const int* s2 = p; // ok const修饰指向,即不能通过s2去修改a的值(s2和p都指向a),但是s2本身可以修改,s2的修改不影响p
int* const s3 = p; // error const修饰指针s3,即可以通过对s3解引用去修改a的值(不允许),但s3本身不允许被修改
const int* const s4 = p; // ok
}
练习2:
int main()
{
int a = 10, b = 20;
int* const p = &a; // const修饰指针p,即p本身不允许被修改,但是其指向可以被修改
int* s1 = p; // ok 定义一个指针s1指向a(和p都指向a),可以通过对s1解引用去修改a的值,也可以对s1本身进行修改,不会影响p
const int* s2 = p; // ok const修饰指向,即不允许通过对s2解引用修改a的值,但是s2可以被修改,不影响p
int* const s3 = p; // ok const修饰s3,即不允许修改s3本身,但是可以通过对s3解引用去修改a的值
const int* const s4 = p; // ok
}
总结
- 能力强的指针赋值给能力收缩的指针(能力收缩,允许)
- 能力弱的指针不能赋值给扩张的指针(能力扩张,不允许)
- 特别重要的一点就是,编译方式的不同,会导致不同的结果