const关键字的浅显理解

1 const的含义

const是constant的简写,是不变的意思,用来限定一个变量为只读(Read-only)。换句话说,它限定一个变量为只读,并不是修饰常量。

2 const常见的修饰

2.1 修饰普通变量

const int NUM = 10; //与int const NUM等价
NUM = 9;  //编译错误,不可再次修改

解释:由于使用了const修饰NUM,使得NUM为只读,因此尝试对NUM再次赋值的操作是非法的,编译器将会报错。也就是const变量需要在开始声明时就赋值

blog_test.c:7:6: error: assignment of read-only variable ‘NUM’
  NUM = 9;          //编译错误,不可再次修改

2.2 修饰数组

const int arr[] = {0,0,2,3,4}; //与int const arr[]等价
arr[2] = 1; //编译错误

解释:const关键字修饰数组,使其元素不允许被改变。

blog_test.c:6:9: error: assignment of read-only location ‘arr[2]’
  arr[2] = 1;        //编译错误

2.3 修饰指针–const在*右边修饰指针,在左边修饰变量

其实const用在指针的情况是很常见的,所以还是重点分析指针的情况。
(1):const 修饰 *p,指向的对象只读,指针的指向可变。

int a = 9;
int b = 10;
const int *p = &a;//p是一个指向int类型的const值,与int const *p等价
*p = 11;    //编译错误,指向的对象是只读的,不可通过p进行改变
p = &b;     //合法,改变了p的指向
blog_test.c:29:5: error: assignment of read-only location ‘*p’
	*p = 11;        //编译错误,指向的对象是只读的,不可通过p进行改变

(2):const修饰p,指向的对象可变,指针的指向不可变

int a = 9;
int b = 10;
int * const p = &a;//p是一个const指针
*p = 11;    //合法,
p = &b;     //编译错误,p是一个const指针,只读,不可变
blog_test.c:43:4: error: assignment of read-only variable ‘p’
	p = &b;        //合法,改变了p的指向

(3):指针不可改变指向,指向的内容也不可变

int a = 9;
int b = 10;
const int * const p = &a;//p既是一个const指针,同时也指向了int类型的const值
*p = 11;    //编译错误,指向的对象是只读的,不可通过p进行改变
p = &b;     //编译错误,p是一个const指针,只读,不可变

总结:const放在的左侧任意位置,限定了该指针指向的对象是只读的;const放在的右侧,限定了指针本身是只读的,即不可变的。

blog_test.c:54:5: error: assignment of read-only location ‘*p’
	*p = 11;        //编译错误,指向的对象是只读的,不可通过p进行改变
blog_test.c:55:4: error: assignment of read-only variable ‘p’
	p = &b;        //编译错误,p是一个const指针,只读,不可变

3 const的使用地方

(1):修饰函数形参
比如一些库函数中:

char *strncpy(char *dest,const char *src,size_t n);//字符串拷贝函数
int  *strncmp(const char *s1,const char *s2,size_t n);//字符串比较函数

我们可以知道,源字符串src是只读的,不可变的,而dest并没有该限制。这符合复制的场景,或者说作为程序员得清楚的知道那些变量是只读?读写?
比如:

#include<stdio.h>
void myPrint(const char *str);
void myPrint(const char *str)
{
    str[0] = 'H';
    printf("my print:%s\n",str);
}
int main(void)
{
    char str[] = "hello world";
    myPrint(str);
    return 0;
}

myPrint函数内部如果尝试对str进行修改,将会报错,因为我们预先已经限定了str是const

error: assignment of read-only location ‘*str’
     str[0] = 'H';

(2):修饰全局变量
我们知道,使用全局变量是一种不安全的做法,因为程序的任何部分都能够对全局数据进行修改。而如果对全局变量增加const限定符(假设该全局数据不希望被修改),就可以避免被程序其他部分修改。这里有两种使用方式。
(2.1):在a文件中定义,其他文件中使用外部声明。比如:

//a.h
const int ARR[] = {0,1,2,3,4,5,6,7,8,9};  //定义int数组
//b.c
extern const int ARR[];   //注意,这里不能再对ARR进行赋值
//后面可以使用ARR

(2.2):在a文件中定义,并使用static修饰,b文件包含a文件

//a.h
static const int ARR[] = {0,1,2,3,4,5,6,7,8,9};  //定义int数组
//b.c
#include<a.h>
//后面可以使用ARR

4 一个问题?const修饰的变量是真正的只读吗?

我们看下面的例子:

#include <stdio.h>
int main(void)
{
    const int a = 2018;
    int *p = &a;
    *p = 2019;
    printf("%d\n",a);
    return 0;
}

编译输出:

2019

可以看到,我们通过另外定义一个指针变量(*p),将被const修饰的a的值改变了?。那么我们不禁要问,const到底做了什么呢?它修饰的变量是真正意义上的只读吗?为什么它修饰的变量的值仍然可以改变?

下面的证据不足以完全正确的解释这个问题,但从侧面说明了一些问题;

#include<stdio.h>
int main(void)
{
    int a = 2019;
    //const int a = 2019;
    printf("%d\n",a);
    return 0;
}

无const修饰,汇编代码:

.LC0:
        .string "%d\n"
main:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     DWORD PTR [rbp-4], 2019
        mov     eax, DWORD PTR [rbp-4]
        mov     esi, eax
        mov     edi, OFFSET FLAT:.LC0
        mov     eax, 0
        call    printf
        mov     eax, 0
        leave
        ret

有const修饰,汇编代码:

.LC0:
        .string "%d\n"
main:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     DWORD PTR [rbp-4], 2019
        mov     eax, DWORD PTR [rbp-4]
        mov     esi, eax
        mov     edi, OFFSET FLAT:.LC0
        mov     eax, 0
        call    printf
        mov     eax, 0
        leave
        ret

结论:相比较而言,发现两者汇编代码一致的(当然这并不能说明所有情况,或者你用其他的编译器clang等会得出不一样的结论,但是在这里我们重点不是这个问题),const关键字告诉了编译器,它修饰的变量不能被改变,如果代码中发现有类似改变该变量的操作,那么编译器就会捕捉这个错误。

5 const帮助程序员提前发现问题,避免不该修改的值被意外地修改

正如上面所看到的,const帮助程序员提前发现问题!!!,但是你可以通过其他方法改变const限定的值。这是极其重要的思想,

最后总结一些关键点;
(1):const关键字让编译器帮助我们发现变量不该被修改却被意外修改的错误。
(2):不要试图将const数据的地址赋给普通指针,正如上面我们看到的。
(3):对于不该被修改的入参,应该用const修饰,这是const使用的常见姿势。
(4):const修饰的变量只能正常赋值一次。
(5):onst关键字修饰的变量并非真正意义完完全全的只读。
(6):不要忽略编译器的警告,除非你很清楚在做什么。
(7):虽然可以通过某种不正规途径修改const修饰的变量,但是永远不要这么做。

本文摘自公众号“编程珠玑”,你可以关注,阅读原文。
参见:
https://mp.weixin.qq.com/s/66mDpnHgrnenOGcdQ41ySA

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值