const与指针
1.const在C语言和C++中的区别
(1)在C语言中
#include<stdio.h>
int main()
{
const int n = 10;
int arr[n] = { 1,2 }; //error
return 0;
}
在C语言中,const修饰的变量是只读变量(常变量),const限制了其“写”操作,其值在编译时不能被使用,因为编译器在编译时不知道其存储的内容。const修饰的变量是不能作左值,也不能用来定义数组长度,和用在switch语句中,可以取地址。
所以以上代码中,在C语言中这样定义数组是错误的,因为在C语言中认为n就是一个变量,虽然加了const修饰,但它仍然是变量,只不过const限制了其“写”操作,而在C语言中规定,定义数组时数组大小是一个大于零的整形常量。
C编译
#include<stdio.h>
int main()
{
const int a = 10;
int b = 0;
int* p = (int*)&a;
*p = 100;
b = a;// C编译时,会借助一个“临时空间”,先将a的值取出来存放在临时空间,在将临时空间的值赋给b
printf("a=%d b=%d *p=%d", a, b, *p);
return 0;
}
按照C的编译方法,虽然a用const修饰,但是可以通过p去改变a的值,将a的值10通过p去修改为100;在对内存进行访问,将a的值100(通过p去修改,此时为100)赋值给b,在C编译时,会借助一个“临时空间”,先将a的值取出来存放在临时空间,在将临时空间的值赋给b,b的值也是100,所以按照C的编译方式,输出的结果为:
(2)在C++中
#include<iostream>
using namespace std;
int main()
{
const int n = 10;
int ar[n] = { 1,2,3,4 }
return 0;
}
在C++中,使用const修饰变量时,是以常性为主,遇见常变量会用值去替换掉。被const修饰的变量是常量,必须初始化,可以在定义数组长度时使用,也可以在switch语句中使用。
在C++中这样定义数组是合法的,会用整数值10去替换n,等价于:int ar[10] = { 1,2 ,3,4};
C++编译
#include<iostream>
using namespace std;
int main()
{
const int a = 10;
int b = 0;
int* p = (int*)&a;
*p = 100; // 将 *p的值改为100
b = a; //C++编译时,直接将a的值替换成10,然后再将10赋值给b
cout << "a=" << a << " b=" << b << " *p=" << *p << endl;
}
C++编译时,遇见const修饰的变量时(直接使用常变量这个值得时候),会先将其替换成具体的数值,所以使用C++的方式编译,输出的结果为:
2.const与指针的关系
(1)常变量与指针
对于一个普通变量a,以下代码,在C编译和C++编译中都可以成功编译。
#include<stdio.h>
int main()
{
int a = 10;
int* p1 = &a;// p1是一个普通指针
const int* p2 = &a;// 指向为常性(解引用为常性)const修饰的是“*”
int const* p3 = &a;// 等价于const int* p2 = &a
int* const p4 = &a;// 指针变量自身为常性(const修饰的是指针p3)
const int* const p5 = &a;// 指向(解引用)和指针变量自身都为常性
return 0;
}
#include<iostream>
using namespace std;
int main()
{
int a = 10;
int* p1 = &a;
const int* p2 = &a;
int const* p3 = &a;
int* const p4 = &a;
const int* const p5 = &a;
return 0;
}
对于一个const修饰的变量a,编译方式不同,结果不同
C编译时可以成功编译:
#include<stdio.h>
int main()
{
const int a = 10;
int* p1 = &a;
const int* p2 = &a;
int* const p3 = &a;
const int* const p4 = &a;
int* p5 = (int*)&a;
return 0;
}
C++编译时不合法:
#include<iostream>
using namespace std;
int main()
{
const int a = 10;
int* p1 = &a; // error 编译不通过,因为可以使用普通指针p1解引用去改变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语言中的“强盗”,虽然编译可以通过,但是不安全
return 0;
}
(2)同类型指针的赋值兼容规程
以下代码在C编译和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
return 0;
}
以下代码在C编译下可以通过,在C++编译下不合法:
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
}
以下代码在C编译和C++编译下都可以成功编译:
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
}
总结:
1.能力强的指针赋值给能力收缩的指针(能力收缩,允许)
2.能力弱的指针不能赋值给扩张的指针(能力扩张,不允许)
3.编译方式的不同,会导致不同的结果