乐趣区

关于c:C进阶9const和volatile分析

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 语言进阶课程》。
如有错漏之处,恳请斧正。

退出移动版