const只读变量
- const 修饰的变量是只读的,本质还是变量
- const 修饰的局部变量在栈上分配空间
- const 修饰的全局变量在全局数据区分配空间
4. const 只在编译器有用,在运行期无用
const 修饰的变量不是真的常量,它只是告诉编译器该变量不能出现在赋值符号的左边。所以我们只要想办法骗过编译器,就可以修改const修饰的变量了。这个方法就是指针!
5. 在现代C语言编译器中,修改const全局变量将导致程序崩溃。
标准C语言编译器不会将const修饰的全局变量存储与只读存储区中,而是存储于可修改的全局数据区,其值依然可以改变。
示例代码:const的变量本质
#include <stdio.h>
const int g_cc = 2;
int main ()
{
const int cc = 1;
int * p = (int *)& cc;
printf ( "%p\n" ,&cc );
printf ( "%p\n" ,&g_cc );
printf ( "cc = %d\n" , cc ); //1
*p = 3 ;
printf ( "cc = %d\n" , cc ); //3
p = ( int*)& g_cc ;
printf ( "g_cc = %d\n" , g_cc ); //2
*p = 4 ;
printf ( "g_cc = %d\n" , g_cc ); // 段错误
return 0 ;
}
输出结果
0xbfee686c
0x8048550
cc = 1
cc = 3
g_cc = 2
段错误
const的本质
- C语言中的const变量使得变量具有只读属性
- 现代C编译器中的const将具有全局生命周期的变量存储与只读存储区
- 所以const不能定义真正意义上的常量。
示例代码:const的本质分析
#include <stdio.h>
const int g_array[5] = {0};
void modify(int* p, int v)
{
*p = v;
}
int main()
{
int const i = 0;
const static int j = 0;
int const array[5] = {0};
modify((int*)&i, 1); // ok
modify((int*)&j, 2); // error
modify((int*)&array[0], 3); // ok
modify((int*)&g_array[0], 4); // error
printf("i = %d\n", i);
printf("j = %d\n", j);
printf("array[0] = %d\n", array[0]);
printf("g_array[0] = %d\n", g_array[0]);
return 0;
}
分析:static修饰的局部变量也放在全局数据区,那么再用const修饰也会放到只读存储区。
const与指针变量
指针变量代表着两个值,一个是指向变量的地址(p),另一个是指向变量的值(*p)。那么我们用const修饰指针变量到底会使那个值变为只读属性呢?
示例代码:const与指针变量
#include <stdio.h>
int main (void )
{
int a = 5 ;
const int * p1 ;
int const * p2 ;
int * const p3 ;
const int * const p4 ;
p1 = & a;
*p1 = 6 ; //error: assignment of read-only location ‘*p1’
printf ( "p1 = %d\n" ,*p1 );
p2 = & a;
*p2 = 7 ; //error: assignment of read-only location ‘*p2’
printf ( "p2 = %d\n" ,*p2 );
p3 = & a; //error:assignment of read-only variable ‘p3’
*p3 = 8 ;
printf ( "p3 = %d\n" ,*p3 );
p4 = & a; //error: assignment of read-only variable ‘p4’
*p4 = 9 ; //error: assignment of read-only location ‘*p4’
printf ( "p4 = %d\n" ,*p4 );
return 0 ;
}
分析:
1.当我们这样定义 const int* p1;时:修改 p1指向地址无报错,修改p1指向地址的值报错。
2.当我们这样定义 int const* p2;时:修改 p1指向地址无报错,修改p1指向地址的值报错。
3.当我们这样定义 int* const p3;时:修改 p1指向地址报错,修改p1指向地址的值无报错。
4.当我们这样定义 const int* const p4;时:修改 p1指向地址报错,修改p1指向地址的值报错。
记忆方法:const靠近谁,就是修饰谁,谁就不能被修改。
const修饰函数参数和返回值
- const修饰函数参数表示在函数体内不希望改变参数的值
- const修饰的函数返回值不可改变,多用于返回指针的情形
示例代码:const修饰函数参数与返回值
#include <stdio.h>
const char * f ( const int i )
{
// i = 5; //error: assignment of read-only location ‘i’
return "Delphi Tang" ;
}
int main ()
{
char * pc = f ( 0);
printf ( "%s\n" , pc );
pc [ 6] = '_';
printf ( "%s\n" , pc );
return 0 ;
}
输出结果
Delphi Tang
段错误
小贴士:
1. C语言中的字符串字面量存储于只读存储区中。所以修改字符串内容会导致程序崩溃,而这种错误编译时不会报错的。
2. 所以我们一般定义字符串都需要加上const关键字,目的是让编译器帮我们检查我们是否有修改字符串的内容。我们用const修饰函数参数和返回值的原因也是尽可能让编译器帮我们做检查。