Summary

1)const润饰的变量是只读的,实质还是变量。(不管值是否扭转)

2)调配空间
const润饰的局部变量在栈上调配空间
const润饰的全局变量在全局数据区调配空间。(这两点印证了const润饰的依然是变量,因为在内存中仍然会调配空间)

3)const只在编译期有用,在运行期无用。(示意:const润饰的变量,在编译的时候不能放在赋值符号左侧,然而在运行期,就能够通过指针扭转该变量的值

4)在古代C语言编译器中,批改const全局变量会导致程序解体。因为古代C语言编译器,会将具备全局生命期的const变量放在只读存储区,改了只读存储区的内容,造成解体。规范C语言编译器依然将全局生命期的const变量放在全局数据区,所以是能够扭转的。

5)const润饰函数参数,示意在函数体内不心愿扭转参数的值形参不能够呈现在赋值符号左侧)。

6)const润饰函数返回值,示意返回值不可扭转,多用于返回指针的状况(指针指向的值不可扭转)。

7)字符串字面量存储于只读存储区必须应用const char*来援用字符串字面量(这样在编译期会将批改只读存储区字面量的谬误报进去);如果只是应用了char *,此时编译的过然而运行时就会遇到段谬误

8)volatile通知编译器每次都必须去内存中去变量值;volatile多用于多线程环境中(变量可能在其余线程或其余中央被扭转)

const和volatile

1、const

1.1 const润饰变量

const int const_global_i = 1;  // const全局变量,全局生命期int main(){    const static int const_static_i = 2; // static局部变量,全局生命期    const int const_local_i = 3;        // 一般局部变量    //    const_local_i = 30;    // error,在编译期不容许呈现在赋值符号左侧    int* p = NULL;        // 规范C语法须要将要应用的变量先申明。        p = (int*)&const_global_i;    *p = 10;    printf("const_global_i = %d\n", const_global_i);    p = (int*)&const_static_i;    *p = 20;    printf("const_statici = %d\n", const_static_i);    p = (int*)&const_local_i;    *p = 30;    printf("const_local_i = %d\n", const_local_i);    return 0;}
  • 在bcc编译器下(规范C语言编译器),这段代码可失常编译,测试的后果为三个变量的值均被扭转,阐明,在规范C编译器下,const定义的所有只读变量的值都能够扭转
  • 在gcc编译器下(扩大C编译器),这段代码能够编译的过,然而上面这两处赋值语句,在运行时都会产生段谬误。阐明,在古代C语言编译器下const定义的只读变量如果是全局生命期(如全局变量、static变量),则会将该变量放到只读存储区,批改了就会段谬误。

      p = (int*)&const_global_i;  *p = 10;  printf("const_global_i = %d\n", const_global_i);  p = (int*)&const_static_i;  *p = 20;  printf("const_statici = %d\n", const_static_i);

1.2 const润饰函数参数

const润饰函数参数示意在函数体内不心愿扭转参数的值

void func(const int a){    a = 2;    // error, assignment of read-only parameter ‘a’}

当应用const润饰了函数参数,在函数外部就不能扭转形参的值。
非凡的:当函数的参数是指针时const仍然在爱护某个值不可扭转,遵循“左数右指”准则。

void func(const int* a){    *a = 1; // error,指向的数不可扭转,即*a不能作为左值}void func(int* const a){    a = (int*)1; // error,指针不可扭转,即a不可作为左值}

1.3 const润饰函数返回值

const润饰函数返回值示意返回值不可扭转多用于返回指针的情景。

const int* GetInt(){    int* p = (int*)malloc(4);    *p = 1;    return p;}int main(){    const int* p = GetInt();    // 返回值为const的指针,也必须const的指针来接                                // 在编译层面保障函数返回的指针指向的值不可扭转    printf("*p = %d\n", *p);    *p = 2;     // error,批改只读的值,read-only    return 0;}

多用于函数返回值是指针的情景,是因为,个别函数返回的这段内存,心愿是只读的,在哪都不容许扭转的。如果是一个一般的变量,函数返回的是一个正本,也不存在批改原来的内存里的值的状况。

1.4 字符串常量的类型?

C语言中的字符串字面量存储于只读存储区中,在程序中必须应用const char*指针。
了解:字符串字面量存储于只读存储区中,天然在代码里,也须要应用const关键字来指明它的只读属性

// 谬误示例char* p = "Delphi";        // "Delphi"是一个字符串字面量,存储于只读存储区p[0] = 'a';                // runtime error,段谬误。批改只读存储区的天然会解体
// 正确用法:应用了const之后,谬误的赋值在编译期就会报进去const char* p = "Delphi";    // 应用const显示阐明"Delphi"字面量的只读属性p[0] = 'a';                // compile error,批改只读变量的值

2、volatile

  • volatile可了解为“编译器正告批示字”,禁止编译器优化
  • volatile通知编译器每次必须去内存中取变量值
  • volatile次要润饰可能被多个线程拜访的变量
  • volatile也能够润饰可能被未知因素更改的变量
    Demo示例

    int obj = 10;int a=0, b=0;a = obj;sleep(100);b = obj;
    • 编译器在编译时发现obj没有作为左值应用,所以是不会扭转的,那么就间接去拿他的字面量应用了,不必去拜访内存了,多“聪慧”(因为CPU拜访内存是耗时的操作,既然obj不变,所以拿字面量用也不会出错,而且更快了,两败俱伤)。
    • 但实际上,如果这是一个多线程程序,或者是嵌入式中断处理程序,在sleep的时候obj的值很可能在某个线程中或者某个中断处理程序中被扭转了,这时候间接用字面量的值就不对了。
  • 所以是否应用volatile关键字,要视状况剖析,因为读内存是耗时的。

小问题:const voaltile int i = 0;变量i具备什么样的个性?编译器如何解决该变量?
答:const使得变量i具备只读属性,所以在程序中不可作为左值;volatile通知编译器每次遇到要用变量i时,都必须去读i所代表的那段内存里的值。

本文总结自“狄泰软件学院”唐佐林老师《C语言进阶课程》。
如有错漏之处,恳请斧正。