C语言应用笔记(三):C语言const关键字及其使用

在 C 语言程序中,const 关键字也是经常会使用到的一个关键字,const 用来修饰常量,如果一个变量被 const 修饰,那么它的值就不能再被改变,我想一定有人有这样的疑问,C 语言中不是有 #define 吗,干嘛还要用 const 呢,我想 const 的存在一定有它的合理性,与预编译指令相比,const 修饰符有以下的优点:

1、预编译指令只是对值进行简单的替换,不能进行类型检查;

2、可以保护被修饰的东西,防止意外修改,增强程序的健壮性;

3、编译器通常不为普通 const 常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。

事实上,无论我们是使用 const 关键字声明变量还是声明参数,其目的都是为了告诉使用者这个变量或者参数的应用目的。合理地使用关键字 const 可以使编译器有效地保护那些不希望被改变的变量或参数,防止其无意中被代码修改,所以使用 const 关键字可以避免一些不必要的错误。

上面简述了 const 关键字的使用,接下来讨论 const 关键字的使用:

 

(1)const关键字修饰的变量

const 关键字最常见的用法就是修饰变量,使用 const 修饰的变量可以认为有只读属性。如:

const int a=6;
//int const a=6;  //效果和上一句相同
int b=0;
b = a;    //语句正确
a = b;    //语句错误,编译报错

虽然 const 关键字修饰的变量具有只读属性,但绝不能将它简单地等价于常量。实际上 C 语言的 const 关键字修饰的变量仅仅是语法上的常量。

此外,使用 const 关键字修饰的变量,在声明时必须进行初始化。这是显而易见的,既然是只读的,当然就得有一供兑取的切确的值。如:

const int a=6;  //合法使用
const int a;    //非法使用,编译报错

const 用于修饰常量静态字符串,例如:

const char* str="openssr";

上面的例子中,如果没有 const 的修饰,我们可能会在后面有意无意的写 str[4]=’x’ 这样的语句,这样会导致对只读内存区域的赋值,然后程序会立刻异常终止。有了 const 关键字,这个错误就能在程序被编译的时候就立即检查出来,这就是 const 关键字的好处。让逻辑错误在编译期被发现。

另外需要注意的是,使用 const 关键字声明的变量是全局性的,所以在使用时需注意。

通常使用 #define 定义一个宏,如:

#define PI  3.1415

这样无论在哪里需要使用圆周率时,直接使用 PI 即可,而在编译时预处理器会将所有的 PI 替换成3.1415。这非常方便,但是由于编译器不会对其进行检查,如果使用不慎就会引入错误,而且这种错误很难发现。而且,我们也无法得到PI的地址,不能向 PI 传递指针或引用。

为此,我们常使用 const 关键字修饰的变量来代替宏。使用 const 声明的变量虽然增加了分配空间,但是可以保证类型的安全,并且可以得到其指针。const 消除了预处理器的值代替的不良影响,并且提供了良好的类型检查形式和安全性,所以理解了 const 关键字,对我们编程会有很大帮助。

 

(2)const关键字修饰的指针变量

const 关键字除了用来修饰普通变量外,还常用来修饰指针变量。当然,在讨论之前,我们需要搞清楚两个概念:常量指针和指向常量的指针。所谓常量指针就是指针变量的值一旦初始化就不能更改,故必须初始化。而指向常量的指针就是其指向的值是一个常量的指针。

在声明指针变量时,const 关键字所处的位置,决定了其定义的指针变量的含义,如:

int* const p;  //该语句表示指向整形的常量指针,它不能指向别的变量,但指向(变量)的值可以修改.
const int* p;  //该语句表示指向整形常量的指针,它指向的值不能修改
int const* p;  //该语句与第2行的含义相同
const int* const p;  //该语句表示指向整形常量的常量指针,它既不能指向别的变量,指向的值也不能修改
int const* const p;  //该语句与第4行的含义相同

上面这些定义非常容易让人糊涂,但是有一点技巧:就是看 const 关键字的右边跟着的是什么?当 const 关键字的右边是类型时,则表示值是常量;当 const 关键字的右边是指针变量时,则表示指针本身是常量。

 

(3)const关键字修饰的函数形参

const 关键字也经常用来修饰函数的形参。关键字 const 修饰函数形参时,通常用于参数为指针或引用的情况,且只能修饰输入参数。若输入参数采用“值传递”方式,由于函数将自动产生临时变量用于复制该参数,该参数本就不需要保护,所以不用 const 关键字修饰。

const 关键字修饰函数输入参数的形式如:

void function(const int *a);
/*或者*/
void function(const int &a);

在 C 标准库中就有很多这样的应用,例如字符串处理函数:

/*字符串拷贝函数*/
char *strcpy(char *strDest,const char *strSrc);
/*返回字符串长度函数*/
int strlen(const char *str);

以上两个函数就是将源字符串使用 const 关键字保护起来,防止不注意地修改了源字符串的值。

显然,采用 const 关键字修饰函数形参,就是为了保护输入参数。在调用函数时,用相应的变量初始化 const 常量,则在函数体中,按照 const 所修饰的部分进行常量化,如形参为 const int *a,则不能对传递进来的指针所指向的内容进行改变,保护了原指针所指向的内容;如形参 const int &a ,则不能对传递进来的引用对象进行改变,保护了原对象的属性。

 

(4)const关键字修饰的函数返回值

const 关键字有时候也会用来修饰函数的返回值,这种情况一般是用来修饰返回的指针或引用,保护指针指向的内容或引用的内容不被修改,如:

const char* getString(void);
const char *str=getString();  //正确
char *str=getString();        //错误

 

(5)const 和非const 类型转换

当一个指针变量 str1 被 const 限制时,并且类似 const char *str1这种形式,说明指针指向的数据不能被修改;如果将 str1 赋值给另外一个未被 const 修饰的指针变量 str2,就有可能发生危险。因为通过 str1 不能修改数据,而赋值后通过 str2 能够修改数据了,意义发生了转变,所以编译器不提倡这种行为,会给出错误或警告。

也就是说,const char * 和 char * 是不同的类型,不能将 const char * 类型的数据赋值给 char * 类型的变量。但反过来是可以的,编译器允许将 char *类型的数据赋值给 const char * 类型的变量。

这种限制很容易理解,char * 指向的数据有读取和写入权限,而 const char * 指向的数据只有读取权限,降低数据的权限不会带来任何问题,但提升数据的权限就有可能发生危险。

C 语言标准库中很多函数的参数都被 const 限制了,但我们在以前的编码过程中并没有注意这个问题,经常将非 const 类型的数据传递给 const 类型的形参,这样做从未引发任何副作用,原因就是上面讲到的,将 const 类型转换为非 const 类型是允许的。

下面是一个将 const 类型赋值给非 const 类型的例子:

#include <stdio.h>
void function(char *str){}
int main()
{
  const char *str1 = "openssr";
  char *str2 = str1;
  function(str1);
  return 0;
}

第6、7行代码分别通过赋值、传参(传参的本质也是赋值)将 const 类型的数据交给了非 const 类型的变量,编译器不会容忍这种行为,会给出警告,甚至直接报错。

综上,const 关键字不但强大,而且对编程也有很大的帮助,前提是我们充分地理解了它的用法。

 

欢迎关注微信公众号『OpenSSR』

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值