您的位置首页百科知识

C语言const变量及常量的注意事项

C语言const变量及常量的注意事项

的有关信息介绍如下:

C语言const变量及常量的注意事项

C语言中存在const修饰的变量,一般我们会直接称作“常量”,但是其实更准确的称为“只读变量”。其实在C语言程序中存在真正的常量,如:char *p = "Hello World!",“Hello World!”就是真正的常量。那么“Hello World!”和const修饰的变量到底有什么不同呢,接下来将阐述它们之间应该注意的一些事项。

const在C语言中使用比较多,虽然变量由const修饰,但是从本质上仍然是变量,所以存储在堆栈和静态存储区,这么区域从进程角度来讲是可读可写,但为什么const修饰后将变得不可写了。在了解这个问题前先来了解const到底修饰的是什么。

1)const在前面

const int nValue; //nValue是const

const char *pContent; //*pContent是const, pContent可变

const char* const pContent; //pContent和*pContent都是const

2)const在后面,与上面的声明对等

int const nValue; //nValue是const

char const * pContent; //*pContent是const, pContent可变

char* const pContent; //pContent是const,*pContent可变

char const* const pContent; //pContent和*pContent都是const

答案与分析:

const和指针一起使用是C语言中一个很常见的困惑之处,在实际开发中,特别是在看别人代码的时候,常常会因为这样而不好判断作者的意图,下面讲一下我的判断原则:

const只修饰其后的变量,至于const放在类型前还是类型后并没有别。如:const int a和int const a都是修饰a为const。注意*不是一种类型,如果*pType之前是某类型,那么pType是指向该类型的指针一个简单的判断方法:指针运算符*,是从右到左,那么如:char const * pContent,可以理解为char const (* pContent),即* pContent为const,而pContent则是可变的。

了解了const修饰的内容,接下来我们将去看看为什么const修饰后变成只读变量了。想了解这个问题,其实如果有编译的知识,那么这个问题将变得很简单。C语言编译主要分为编译和链接两部分,之所以const修饰的变量变为只读是因为在变量前加了const修饰,如果程序试图去修改此变量的值,那么编译器在编译阶段检查语法错误的时候将报错,这很好理解,这就是C语言规定的语法,违反了将提示相应的错误,而并不是const修饰的变量的内存的属性就真的变成了只读,它和其他不带const修饰变量一样存储在相同属性的内存区域。

根据第2条,我们先看个例子:

//VC6.0 windows_console_application

#include "stdafx.h"

int main(int argc, char* argv[])

{

const int i = 0;

i = 0;

return 0;

}

我们只编译程序将提示error C2166: l-value specifies const object,且要注意在定义const变量同时要初始化变量,否则也将提示错误,这都是由C语言语法规定。

从以上可知,既然const变量所在区域的属性为可读可写,那么我们当然可以修改其所处内存的值了。当然,只不过我们可以偷梁换柱进行更新,具体例子如下:

#include "stdafx.h"

int main(int argc, char* argv[])

{

const int i = 100;

int *temp = (int *)(&i);

*temp = 200;

printf("now the value of i is %d\n", i);

return 0;

}

通过编译链接,没有任何的错误。和我们的预期还是比较接近的。

但是运行程序得到如下结果:

now the value of i is 100

Press any key to continue

看结果好像和我们预期的不对啊,是不是变量真的是只读的呢?要解释这个问题我们要通过VC提供给我们的断点来调试,断点设置如下图1:我们dubug程序运行到断点1,查看此时程序运行是变量的情况如下图2:此时可以看到i所处在内存区域的值为0x00000064即100,和我们初始化值一样;执行代码到断点2,是情况如图3:确实也是我们修改的200,那为什么执行printf输出的却是100呢?

只读变量的值我们确实能够修改,但是最终输出结果却是100,这的确匪夷所思?要解答这个问题还要从C语言const变量的作用说起:const是一个c语言的关键字,它限定一个变量不允许被改变,产生静态作用。使用const在一定程度上可以提高程序的安全性和可靠性。另外,在观看别人代码的时候,清晰理解const所起的作用,对理解对方的程序也有一些帮助。

其实const变量在一定程度上加快了程序的运行,这是为什么呢,我们把上诉程序的汇编拿来看看就知道了,汇编如下:

5: const int i = 100;

0040D418 mov dword ptr [ebp-4],64h

6: int *temp = (int *)(&i);

0040D41F lea eax,[ebp-4]

0040D422 mov dword ptr [ebp-8],eax

7: *temp = 200;

0040D425 mov ecx,dword ptr [ebp-8]

0040D428 mov dword ptr [ecx],0C8h

8: printf("now the value of i is %d\n", i);

0040D42E push 64h

0040D430 push offset string "now the value of i is %d\n" (00422e80)

0040D435 call printf (0040d6b0)

0040D43A add esp,8

9: return 0;

这里只提供程序的一部分汇编指令,从汇编中可以看到给printf传参数i的时候,直接传递的是i的初始值64h,但是如果将代码中i的const修饰去掉的汇编将是如何呢,我们请看如下:

6: int *temp = (int *)(&i);

0040D74F lea eax,[ebp-4]

0040D752 mov dword ptr [ebp-8],eax

7: *temp = 200;

0040D755 mov ecx,dword ptr [ebp-8]

0040D758 mov dword ptr [ecx],0C8h

8: printf("now the value of i is %d\n", i);

0040D75E mov edx,dword ptr [ebp-4]

0040D761 push edx

0040D762 push offset string "now the value of i is %d\n" (00422e80)

0040D767 call printf (0040d6b0)

0040D76C add esp,8

9: return 0;

从上可以看出传递i的时候要先寻址i的值,就是这么导致我们的匪夷所思。当然这其实也是const导致的。

以上讲解了const变量,那么让我们来看看真正的常量吧,比如如下代码:

#include "stdafx.h"

int main(int argc, char* argv[])

{

char *p = "Hello World!";

*p = 'L';

return 0;

}

这段代码有什么问题吗?直接编译链接,好像通过了,然后就觉得没问题呢。其实直接运行发现程序崩溃了。然后可能就是各种dubug,log什么的查问题,但代码很长的时候,问题就更难定位。即使程序就这么短,给人感觉这可能有什么问题呢。到底问题出在什么地方呢,这就要了解常量到底是么,为什么称作常量,其实常量是我们使用比较多,但是也是容易忽视的,在内存中存在真正一块只读区域,常量就放在这块区域。单从语法上来看,上面代码当然是没问题的,但是程序执行了一些非法的指令,操作系统拦截并发出异常将程序终止了。显然对应"Hello World!"常量所处的地址,我们只有只读的权限,并不能修改,一切用户指令试图修改都是不可能且禁止的。

所以从以上分析,const变量和常量从本质上来说是不一样的,他们在进程中所处的区域,及他们的属性的控制都是不同的。所以对它们进行操作的错误也是不同,即管理它们的对象不同,即编译器和操作系统去约束的。所以对于程序员我们不管要专注于我们的C的基本语法,我们仍然要了解程序所处的平台。只有这样,才能算合格的软件工程师。