C语言中const关键字的用法以及面试技巧

  相信很多初学者或者学了很久C语言的朋友,对const关键字的理解还是存在着一知半解的状态,今天我就讲讲const关键字的用法及其面试技巧。

 先说说技巧,怎么区分const修饰的是什么:

  理解的时候,在定义或声明中跳过或者忽略掉数据类型(int,char,double等),const后面的内容即为修饰的内容。

比如:

普通类型:

const int p;//忽略int,即 const后面是p,修饰的即为p
int const p;//同样的分析方法,这句跟上面的分析一致

指针类型:

const int *p1; //去掉int,const修饰的是 *
int const *p2; //去掉int,const修饰的是 *,等同于上面一句

int* const p3; //去掉int,const修饰的是p3,区别于上面那种情况

const int* const p4; //去掉int,const即修饰*,又修饰p4,区别上面两种情况

说完了怎么看const修饰的内容,谈谈怎么理解这些情况:

1、普通变量:

   变量名的本质:一段连续内存空间的别名。

   理解好这句话对下面的理解很重要。

比如:

定义了 :int p = 10;

则系统会分配4个字节的内存,这块内存我们命名为p,其地址是不确定的,这里我们假定是0x2222,于是在p生存的周期内,这块从0x2222开始的地址的内存空间就被命名为p,p = 10 即向这块内存内写入10。所以说变量名的本质:一段连续内存空间的别名。

接下来,言归正传,对于普通变量,const修饰时,仅仅改变的是变量的属性,即将原来变量的可读写性变成了只读性。

int a1; //a1是可读可写的,既可以被赋值,又可以赋值其他变量
const int a2; //加了const修饰后,a2只能被读取,即可以用于给其他变量赋值,但不可被赋值

a2 = b; //错误,a2不可以被写,重新修改
b = a2;//正确,a2可以被读,传值给另一变量

 可以这么理解:普通变量加const修饰是为了防止变量被修改。

但有个例外:如果该变量为全局变量,则不能通过指针修改,但如果是局部变量,则还是可以通过指针修改的。

原因是:全局变量在全局静态区,内容不能被修改,但局部变量本身仍在栈区,可以使用指针修改。


const int a = 10; //const修饰全局变量必须初始化

void fun(){
    const int b; //const修饰局部变量可以不初始化
    int *p = &b; //可以利用指针指向b对应的内存块,对该内存块修改
    *p = 20;     
    printf("%d , %d",*p,b); //结果为 20 ,20 

    p = &a;    //可以利用指针指向a对应的内存块
    *p = 20;   //(此句运行出错)但不可以通过指针修改全局变量的内容
    a = 20;    //(此句运行出错)因为a为全局常量,不能修改
   
}

总结一下:对于全局常量,不可用指针修改常量,但对于局部常量,仍可以用指针修改常量。

讲完了普通变量,面试大多时候是问指针变量,所以接下来重点讲const修饰指针变量。

利用最前面分析的方法,可以知道const修饰指针变量有三种情况:

①const修饰 * 号

②const修饰变量

③const既修饰 * 号又修饰变量

只要掌握了前两种情况,最后第三种自然明了。

先说说const修饰 * 号,该情况为(局部变量):

const int *p_a;//与int const *p_a;等价

int a = 10;
int b = 20;
p_a = &a;   
*p_a = 20;  //错误
a =20;      //正确
p_a = &b;   //正确

当const修饰*号时,表示不能通过指针p_a修改p_a指向的内容。这句话有3层意思(对应上述代码三种情况):

①当p_a指向a时,不能通过指针p_a取*号修改a中的内容

②a中的内容仍可以由a自身修改

③p_a指向可以改变,即p_a可以指向b

可以这么记忆:*号是指针用来对指向的内存操作(读写)的,当对*加了const修饰,意味着*号的操作只剩下只读的功能,也就是只能使用*号来读取指针指向的内容,而失去了写的特性,这点与普通变量的情况类似。

再说说const修饰变量的情况:

void fun(){
    int a = 10;
    int b = 20;
    int* const p_a = &a; 
    
    *p_a = 20; //正确
    a = 30;    //正确
    p_a = &b;  //错误

}

当const修饰变量p_a时,表示不能改变p_a的内容,即改不了指针的指向。这句话有3层意思(对应上述代码三种情况):

①当p_a指向a时,能通过指针p_a取*号修改a中的内容

②a中的内容仍可以由a自身修改

③p_a指向不可以改变,即p_a不可以修改,指向b或者其他变量

最好的理解便是:指针变量也是变量,变量名的本质:一段连续内存空间的别名。理解好了这句,也就明白了,const修饰了这个变量,也就是修饰了这块内存空间,使得这块内存空间的可读写性改为了可读性,也就是指针指向固定,无法更改。

看到这里,如果前面的都了解了,那么你就会明白了用const修饰,实际上就是将对应的可读写改成了只读性质。

那么,最后一种情况就是,const既修饰*号,又修饰变量的情况了:

void fun(){

    int a = 10;
    int b = 20;
    
    const int* const p = &a; //必须初始化
    *p = 20;                 //错误    
    p = &b;                  //错误
    a = 20;                  //正确
}

如果明白了前两种情况,那么最后这种情况就好理解了(对应上述代码三种情况)

①const修饰*号限定了指针对内存的操作只能为只读

②const修饰变量p限定了指针的指向,p不能指向其他变量

③a或b本身的变量可以自身修改。

至于为什么必须初始化,如果理解了上述内容,那也就明白了。说下不初始化的情况,编译器(我用的VS2017)仍能通过,但有警告,p没有明确初始化,并不知道其指向哪里,由于该变量既不能修改指向,又不能修改指向的内容,所以此时该指针也就没有作用了,这种做法是不好的。

好了,上述内容和情况的分析都是针对局部变量的,如果能充分理解,那么const关键词也就掌握了,至于const修饰全局变量的问题,涉及到内存区域的问题,能理解内存区域也就能充分理解。

猜你喜欢

转载自blog.csdn.net/JayFan_Ma/article/details/82942903