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 的值很可能在某个线程中或者某个中断处理程序中被扭转了,这时候间接用字面量的值就不对了。
- 编译器在编译时发现 obj
- 所以是否应用 volatile 关键字,要
视状况剖析
,因为读内存是耗时的。
小问题:const voaltile int i = 0;变量 i 具备什么样的个性?编译器如何解决该变量?
答:const 使得变量 i 具备只读属性,所以在程序中不可作为左值;volatile 通知编译器每次遇到要用变量 i 时,都必须去读 i 所代表的那段内存里的值。
本文总结自“狄泰软件学院”唐佐林老师《C 语言进阶课程》。
如有错漏之处,恳请斧正。