变量的属性——类型限定符:const

时间:2022-10-29 13:33:21

const只读变量

  1. const 修饰的变量是只读的,本质还是变量
  2. const 修饰的局部变量在栈上分配空间
  3. 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的本质

  1. C语言中的const变量使得变量具有只读属性
  2. 现代C编译器中的const将具有全局生命周期的变量存储与只读存储区
  3. 所以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 ‘*p1printf ( "p1 = %d\n" ,*p1 );

    p2 = & a;
    *p2 = 7 ;    //error: assignment of read-only location ‘*p2printf ( "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 ‘*p4printf ( "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修饰函数参数和返回值

  1. const修饰函数参数表示在函数体内不希望改变参数的值
  2. 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修饰函数参数和返回值的原因也是尽可能让编译器帮我们做检查。