[深入浅出C语言]浅析const限定符

230 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第5天,点击查看活动详情

前言

       在C中,const是一个很重要很常见的类型限定符,用来修饰不同类型的变量,本文就来分享一波笔者对于该关键字的学习心得与经验。

        笔者水平有限,难免存在纰漏,欢迎指正交流。

const限定符

可以用来:

//const修饰一般变量
const int a = 10;

//const修饰数组,则数组中每一个元素都不可被修改
const int arr[4] = {1, 2, 3, 4};

//const修饰指针
int a = 10;
const int* p1 = &a;
int*const p2 = &a;

        const用来修饰变量为常变量,也就是使其具有常量性质而不能被直接修改值(如赋值、自增或自减),而间接可以改,比如使用指针解引用。

        所以const修饰的变量并非是真的不可被修改的常量。

const修饰变量的意义

        1. 让编译器进行直接修改式的检查,如果发现被直接修改的话会报错,也就是不想后续让别人或自己直接修改特定变量的值。

        2. 也属于一种“自描述”含义,告诉其他程序员(正在改你代码或者阅读你代码的人)这个变量后续过程中不要修改。

        3.不过要注意,这种限定不是完全的强约束,真的要修改也是可以修改的(通过指针解引用间接修改)。

        字面量比如字符串字面量才是真正的不可被修改:

image.png

const修饰的注意事项

        const修饰的变量,可以作为数组定义的一部分吗?

int main()
{
    const int n = 100;
    int arr[n];
    system("pause");
    return 0;
}

        在vs(标准C)下直接报错了,而在gcc(GNU扩展)下可以。

        但我们一切向标准看齐,就认为是不可以(而C++下可以)。

        const修饰的变量只能在定义的时候直接初始化,不能二次赋值。为什么?

        因为一旦被修饰了就不能再被直接修改。

image.png

const修饰指针

常量指针

        const 类型 * ptr

        如const int * ptr,而int const* ptr这样写也没问题。

        为什么要叫常量指针?意味着它指向的是常量吗?

        比如:

        int a = 10;
        const int* ptr = &a;

        这样一来就不能通过解引用ptr来改变a的值了,也就是对于指针来说指向的是常量(不可变更),实际上a还是变量。

指针常量

        类型* const ptr

        如int* const ptr

        为什么要叫指针常量呢?真的变成常量了吗?

        比如:

        int a = 10;
        int const*ptr = &a;

        这样一来ptr的值不能改变了,也就是ptr只能指向a了,而ptr还是变量。

总结:

 const修饰指针变量的时候:

        1. const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过该指针来改变。但是指针变量本身的内容可变。

        2. const如果放在*的右边,修饰的是指针变量本身,保证了指针变量的内容不能修改,但是指针指向的内容,可以通过该指针改变。

常量指针常量

        const 类型 * const ptr

        综合了常量指针和指针常量的特点,也就是既不能改变指针的内容,又不能通过该指针改变指针指向的内容。

        

举个日常生活中的例子来加深理解:

image.png

const与非const修饰的类型赋值问题

例1

int a = 10;
const int* p = &a;
int *q = p;

        编译器会报警告

例2

int *p = &a;
const int *q = p;

        编译器不报警告

小经验:

        把一个类型限定不怎么严格的变量赋值给另一个类型限定比较严格的变量,编译器不报警。

        把一个类型限定比较严格的变量赋值给另一个类型限定不怎么严格的变量,编译器会报警。

        比如上面的例1,要不报警的话就要把指针q用const修饰一下:const int *q。

为什么会这样呢?

        编译器为什么会报警?仅仅是类型不同才报警的吗?那例2又不会报警,这样说不通。

        编译器报警是因为检测到存在危险的行为,例1中p被const修饰就表明不想用指针改变a的值,也就是严格限定了p的行为,然而把p的值赋给另一个没有const修饰也就是限定不怎么严格的指针q其实就是危险行为,因为q可以直接解引用去修改a的值,相当于放宽了原来的某种限制,编译器就认为该行为有危险,可能是用户的疏忽导致的,需要报警提示用户。

举个例子

image.png

const修饰函数参数

        在C中,任何函数传参都一定要形成临时变量,即使是传址。

        const修饰函数参数一般修饰的是指针变量,因为只有传址才有可能解引用修改到主函数中的实参,不然形参也只是实参的一份临时拷贝,改变形参并不会影响实参。

        而用const修饰也就意味着不想在调用的函数内部修改到实参的值。

例子:

//该函数我只想让它执行打印功能
void print(const int* p)
{
    printf("This is %d.", *p);
    *p = 10;//编译器会报错
}


int main()
{
    int n = 100;
    int* q = &n;
    print(q);
    return 0;
}

        const修饰后变为不可修改的左值。

image.png

const修饰函数返回值

例子:

//告诉编译器,告诉函数调用者,不要试图通过指针修改返回值指向的内容
const int* test()
{
    static int g_var = 100;
    return &g_var;
}

int main()
{
    int *p = test(); //有告警
    //const int *p = test(); //需要用const int*类型接受,这样在语法/语义上限制了返回值,不能直接修改函数的返回值
    *p = 200;
    printf("%d\n", *p);
    return 0;
}

        一般内置类型(基本类型)返回,加const无意义,因为对于传值引用,形参只是实参的一份临时拷贝,改变形参并不会影响实参。


以上就是本文全部内容,感谢观看,你的支持就是对我最大的鼓励~

src=http___c-ssl.duitang.com_uploads_item_201708_07_20170807082850_kGsQF.thumb.400_0.gif&refer=http___c-ssl.duitang.gif