C之 const 和 volatile(九)

在  C 语言中,我们经常会见到 const 和 volatile 这两个关键字,那么我们今天就来介绍下这两个关键字。

先来介绍 const 关键字。提起 const 关键字,我们可能首先想到的是经过它修饰的变量便是常量了。其实我们这种想法是错误的,其实 const 修饰的变量是只读的,其本质还是变量。它修饰的局部变量是在栈上分配空间的,它修饰的全局变量在全局数据区分配空间(也就是我们平时所说的 read-only data 段),const 只在编译期有用,在运行期无效。注意:const 修饰的变量不是真的常量,它只是告诉编译器该变量不能出现在赋值符号的左边。

在现代的 C 语言编译器中,修改 const 全局变量将导致程序崩溃。但是得注意标准 C 语言编译器不会将 const 修饰的全局变量存储于只读存储区,而是存储于可修改的全局数据区,其值依然可以改变。

下来我们来做个示例代码,验证下我们所说的,代码如下:

#include <stdio.h>

const int g_cc = 2;

int main()
{
    const int cc = 1;
    
    int* p = (int*)&cc;
    
    printf("cc = %d\n", cc);
    
    *p = 3;
    
    printf("cc = %d\n", cc);
    
    p = (int*)&g_cc;
    
    printf("g_cc = %d\n", g_cc);
    
    *p = 4;
    
    printf("g_cc = %d\n", g_cc);
    
    return 0;
}

我们来分析下这个示例代码,cc 是 const 修饰的局部变量,是分配在栈上的。也就意味着它的属性是只读的,其本质还是变量。因而我们可以通过第13行的指针操作来改变它的值,所以第11行和第15行打印出的值分被为 1 和 3;而 g_cc 不同,因为它是个 const 修饰的全局变量,因而会被放在只读数据段。所以它的值不能被改变。以上的分析是基于现代的 C 语言编译器,如果是以前的 C 语言编译器(如 BCC)则不会这样处理,会将全局变量存储于可修改的全局数据区,其值依然可以改变。我们分别用现代编译器和 BCC 编译器编译,图一为现代 C 语言编译器gcc编译的结果,图二为 BCC 编译的结果。

图一

图二

我们可以看到同一份代码在两个编译器上竟然出现了两个结果,在 gcc 中出现了段错误,而在 BCC 中则通过指针顺利的改变了由 const 修饰的 g_cc 全局变量变量。由此证明了我们的分析是对的。

C语言中的 const 使得变量具有只读属性,现代 C 编译器中的 const 将具有全局生命周期的变量存储于只读存储区,const 不能定义真正意义上的常量!!!

下面我们来分析这个示例代码,代码如下:

#include <stdio.h>

const int g_array[5] = {0};

void modify(int* p, int v)
{
    *p = v;
}

int main()
{
    int const i = 0;
    const static int j = 0;
    int const array[5] = {0};
    
    modify((int*)&i, 1);           // ok
    modify((int*)&j, 2);           // error
    modify((int*)&array[0], 3);    // ok
    modify((int*)&g_array[0], 4);  // error
    
    printf("i = %d\n", i);
    printf("j = %d\n", j);
    printf("array[0] = %d\n", array[0]);
    printf("g_array[0] = %d\n", g_array[0]);
    
    return 0;
}

在这份代码中,我们发现变量 i 的定义竟然是 int const 的,其实这样也就相当于 const int i;由于它是局部变量,分配在栈上。因而在被 const 修饰后,就具有只读属性,所以还是可以通过指针来改变的。但是变量 j 就不一样啦,它是由 static 修饰的,其具有全局的生命周期,所以是不能通过指针来改变的,因而第17行代码将会报错。第18行我们可以通过指针来改变 array 数组第一个元素,而 g_array 数组是全局的,所以就不能通过指针来改变它的值,而已第19行会报错。同样的上面的分析我们是基于现代的编译器,在 BCC 中不适用。下来我们就来编译下,看看是否如我们所分析的那样。

我们用 gcc 编译直接报段错误,那么我们来把第17和19行注释掉,再次编译。

下来我们再用 BCC 来编译下,结果如下

那么我们发现代码直接通过,而且值也都改变了。由此可以看出现代的编译器越来越强大了,已经将 const 的意思表达的更加完善了。下面这张图更好的阐释了 const 的意思

那么 const 修饰函数参数又是什么意思呢?它表示在函数体内不希望改变参数的值,const 修饰函数返回值表示返回值不可改变,多用于返回指针的情形。C 语言中的字符串字面量存储于只读存储区,在程序中需要使用 const char* 指针。下来我们来分析个示例,代码如下:

#include <stdio.h>

const char* f(const int i)
{
    i = 5;
    
    return "hello world";
}

int main()
{
    const char* pc = f(0);
    
    printf("%s\n", pc);
    
    pc[6] = '_';
    
    printf("%s\n", pc);
    
    return 0;
}

我们可以看出变量 i 是由 const 修饰的,因而第5行的代码肯定会报错。在第16行我们想要将中间的那个空格改变成“_”,但是返回的字符字面量是 const 型的,因而不能改变,在这块也会报错。我们编译下看看结果是否如此

我们可以看出这两行代码已经报错。我们注释掉这两行代码,再次编译,结果如下

我们接下来再来分析下 volatile 关键字。它可理解为“编译器警告指示字”,告诉编译器必须每次去内存中取变量值,主要修饰可能被多个线程访问的变量同时也可以修饰可能被未知因数更改的变量。我们在嵌入式的开发中,常常会用的多线程。那么在这个线程里一个变量的值被改变,我们在另一个线程里需要用到它原来的值,但是已经被改变了,这时怎么办呢?只能去内存中读取这个变量的值,这时用 volatile 修饰就OK了。在每次需要用到这个变量时,便会去内存中读取,虽然比较耗时一点,但是是安全的!

那么我们今天学习了 const 和 volatile 的相关知识,总结如下:1、const 使得变量具有只读属性;2、它不能定义真正意义上的常量;3、const 将具有全局生命期的变量存储于只读存储区;4、volatile 强制编译器减少优化,必须每次从内存中取值。后面我们会继续对 C 语言的学习。

欢迎大家一起来学习 C 语言,可以加我QQ:243343083。

原文地址:http://blog.51cto.com/12810168/2095264

时间: 2024-07-30 19:59:49

C之 const 和 volatile(九)的相关文章

C++的类型转换:static_cast、dynamic_cast、reinterpret_cast和const_cast(dynamic_cast还支持交叉转换,const_cast将一个类的const、volatile以及__unaligned属性去掉)

在C++中,存在类型转换,通常意味着存在缺陷(并非绝对).所以,对于类型转换,有如下几个原则:(1)尽量避免类型转换,包括隐式的类型转换(2)如果需要类型转换,尽量使用显式的类型转换,在编译期间转换(3)避免使用reinterpret_cast和老式的强制类型转换操作符通常,隐式的类型转换会导致警告,需要重视这些警告,并使用显式的类型转换代替,例如static_cast代替一些算术类型转换. 在C++中,对象的类型通常有如下几种:(一)内置类型,如int ,bool ,枚举类型等(二)自定义类型

【C++】const、volatile不能修饰没有this指针的成员函数

一般所有的成员函数都只有一个复本,当不同的对象调用成员函数时,为了区分是哪个成员在调用,会传入this指针. 当调用有const.volatile修饰的成员函数时,会相应的传入一个const.volatile修饰的this指针. 故const.volatile不能修饰没有this指针的成员函数. 如:const.volatile不能修饰static成员函数

void指针意义、Const、volatile、#define、typedef、接续符

1.C语言规定只有相同类型的指针才可以相互赋值. Void*指针作为左值用于接收任意类型的指针, void*指针作为右值赋给其他指针时需要强制类型转换. 2.在C语言中Const修饰的变量是只读的,本质还是变量. 口诀:左数右指  不可改变: 当Const出现在*号左边时,指针指向的数据为常量. 当Const出现在*号右边时,指针本身为常量. const int* p ; //P可变,P指向的内容不可变 int const * p; //跟上一句一样 int *const p; //p不可变,p

const,volatile,static,typdef,几个关键字辨析和理解

1.const类型修饰符 const它限定一个变量初始化后就不允许被改变的修饰符.使用const在一定程度上可以提高程序的安全性和可靠性.它即有预编译命令的优点也有预编译没有的优点.const修饰的变量被编译器处理只读变量(不是常量,常量是放到内存的只读区域的)放在内存中,由编译器限定不允许改变. (1)具有不可变性.  例如:const int Max=100; Max++会产生错误; (2)便于进行类型检查,使编译器对处理内容有更多了解,消除了一些隐患. 例如: void f(const i

C++ const、volatile、mutable的用法 (转)

  const.volatile.mutable的用法 鸣谢作者: http://blog.csdn.net/wuliming_sc/article/details/3717017 const修饰普通变量和指针 const修饰变量,一般有两种写法: const TYPE value; TYPE const value; 这两种写法在本质上是一样的.它的含义是:const修饰的类型为TYPE的变量value是不可变的.对于一个非指针的类型TYPE,无论怎么写,都是一个含义,即value值不可变. 

const和volatile同时修饰同一个变量

主要要搞清楚 编译期 和 运行期的关系. 编译期就是 C 编译器将 源代码转化为 汇编再到机器代码 的过程. 运行期就是 实际的机器代码在CPU执行 的过程.很多书上说的东西,其实都只是指编译期进行的事情.const 和 volatile 也一样,所谓的 const ,只是告诉编译器要保证在 C的"源代码"里面,没有对该变量进行修改的地方,就是该变量不能而出现在赋值符号左边.实际运行的时候则不是 编译器 所能管的了.同样,volatile的所谓"可能被修改",是指&

C语言中关键字auto、static、register、const、volatile、extern的作用

原文:C语言中关键字auto.static.register.const.volatile.extern的作用 关键字auto.static.register.const.volatile.extern 这些关键词都是c++基础知识,我整理了一下,希望对新学的朋友们有用: (1)auto 这个这个关键字用于声明变量的生存期为自动,即将不在任何类.结构.枚举.联合和函数中定义的变量视为全局变量,而在函数中定义的变量视为局部变量.这个关键字不怎么多写,因为所有的变量默认就是auto的. (2)reg

const与volatile

C或者C++基本上是按照从上到下.从左至右的顺序来读.但对于指针声明从某种意义上来讲是倒着的. C或者C++中每个声明都由两部分组成:零个或者多个声明说明符,一个或者多个用逗号隔开的声明符. const 1.类型说明符表明声明符ID的类型:其他说明符提供直接适用于这个声明符ID的一些类型无关的信息. eg1.static unsigned long int *x[N]; 声明x的类型是"指向unsigned long int的N个指针元素的序列".关键词static表明x有静态分配的存

const和volatile的区别

一.关键字const有什么含意? 只要一听到说:“const意味着常数”,就知道我正在和一个业余者打交道.去年Dan Saks已经在他的文章里完全概括了const的所有用法,因此ESP(译者:Embedded Systems Programming)的每一位读者应该非常熟悉const能做什么和不能做什么.如果你从没有读到那篇文章,只要能说出const意味着“只读”就可以了.尽管这个答案不是完全的答案,但我接受它作为一个正确的答案.(如果你想知道更详细的答案,仔细读一下Saks的文章吧.)如果应试