前言
作为C/C++开发人员,我们看到关键字const时,第一反应就说它就是:只读。const要求其所修饰的对象为常量。不可对其修改和二次赋值操作(不能作为左值出现)。本文说明C中的const关键字,不包括C++。
const基本介绍
const是constant的简写,可以理解成是”只读变量“的限定词,从这里可以看出,const修饰的是变量,跟常量是不同的,常量是被编译器放在内存中的只读区域,当然也就不能够去修改它。
而”只读变量“则是在内存中开辟一个地方来存放它的值,只不过这个值由编译器限定不允许被修改。
const就是用来限定一个变量不允许被改变的修饰符。因为const声明的变量是只读变量。
用const修饰普通变量
例如:
const int NUM = 10; //与int const NUM等价
NUM = 12; //编译错误,不可再次修改
以上代码中的const告诉编译器,const修饰NUM是只读,不可以被修改。如果在函数中不小心使用类似二次赋值操作,编译器会捕获这个错误,并生成一条错误的信息。类似提示向只读变量‘NUM’赋值:
这里要注意一下:
如果需要使用const修饰一个变量,那么它只能在开始声明时就赋值,否则后面就没有机会了。
修饰数组
#include <stdio.h>
int sum( int arr[],int n);
int main(int argc, char const *argv[])
{
int arr[] = {0,0,2,3,4}; //
arr[2] = 1; // 0 + 0 +1 + 3 + 4
int value = sum(arr,5);
printf("ar value: %d\n",value); // 8
return 0;
}
int sum( int arr[],int n){
int i;
int total = 0;
for (i = 0; i < n; i++){
total += arr[i];
}
return total;
}
编译运行:
编译运行一点问题都没有,如果使用const关键字修饰数组,使其元素不允许被改变。
const int arr[] = {0,0,2,3,4}; //与int const arr[]等价
arr[2] = 1; //编译错误
试图修改arr的内容的操作是非法的,编译器将会报错:
修饰指针
修饰指针的情况比较多,主要有以下几种情况。
1.const 修饰 *p,指向的对象只读,指针的指向可变:
int a = 10;
int b = 12;
const int *p = &a;//p是一个指向int类型的const值,与int const *p等价
*p = 11; //编译错误,指向的对象是只读的,不可通过p进行改变
p = &b; //合法,改变了p的指向
const int* p; 这是修饰了谁呢?
其实我们可以这样想,如果我们把int 拿出来 也就是 const int (p); 等价于 int const (p);
由此我们可以看出来const修饰了p啊! 也就是指向的内容不可以改变,比如p已经指向a变量,但是p=11;会报错,因为*p不可改变 但是地址是可以改变的。
比如b=12, p=&b;是正确的,因为改变了p的指向。
2.const修饰p,指向的对象可变,指针的指向不可变:
int a = 10;
int b = 12;
int * const p = &a;//p是一个const指针
*p = 11; //合法,
p = &b; //编译错误,p是一个const指针,只读,不可变
3.指针不可改变指向,指向的内容也不可变:
int a = 10;
int b = 12;
const int * const p = &a;//p既是一个const指针,同时也指向了int类型的const值
*p = 11; //编译错误,指向的对象是只读的,不可通过p进行改变
p = &b; //编译错误,p是一个const指针,只读,不可变
看完上面几种情况之后是否会觉得混乱,并且难以记忆呢?我们使用一句话总结:
const放在的左侧任意位置,限定了该指针指向的对象是只读的;const放在的右侧,限定了指针本身是只读的,即不可变的。
如果还不能很好的理解,可以去掉类型说明符,例如:
const *p; //修饰*p,指针指向的对象不可变
* const p; //修饰p,指针不可变
const * const p; //第一个修饰了*p,第二个修饰了p,两者都不可变
库函数中const怎么用呢!
实际上,为我们可以经常发现const关键字的身影,例如很多库函数的声明:
#include <string.h>
int strncmp(const char *s1, const char *s2, size_t n);
void *memmove(void *s1, const void *s2, size_t n);
har *strncpy(char *restrict s1, const char *restrict s2, size_t n);
通过看strncmp函数的原型可以知道,源字符串s2是只读的,不可变的,而s1并没有该限制。
修饰全局变量
全局变量的作用域是整个文件,我们应该尽量避免使用全局变量,因为一旦有一个函数改变了全局变量的值,它也会影响到其他引用这个变量的函数,导致除了bug后很难发现,如果一定要用全局变量,我们应该尽量的使用const修饰符进行修饰,这样防止不必要的人为修改。例如:
test1.c
定义一些全局变量。
const doubule PI = 3.141;
test2.c
使用在其他文件中定义的全局变量。
extern const double PI;
其次是将常量放在一个include文件中。这时还必须使用静态外部存储类:
constant.h
定义一些全局变量。
static const double PI = 3.141;
/*test1.c——使用在其他文件中定义的全局变量*/
#include "constant.h"
/*test2.c——使用在其他文件中定义的全局变量*/
#include "constant.h"
如果不使用关键字static,在文件test1.c和test2.c中包含constant.h将导致每个文件都有统一标示符的定义声明。导致编译会出现重复定义的错误。
修饰函数的返回值
如果给以“指针传递”方式的函数返回值加 const 修饰,那么函数返回值(即指针)的内容不能被修改,该返回值只能被赋给加const 修饰的同类型指针。
const char * GetString();
如下语句将出现编译错误:
char *str = GetString();
正确的用法是:
const char *str = GetString();
总结
借助上面理解,就会发现以下几种等价情况:
const int NUM = 11; //与int const NUM等价
int a = 12;
const int *p = &a;//与int const *p等价
const int arr[] = {0,7,2,3,4}; //与int const arr[]等价
const 的好处是引入了常量的概念,让我们不要去修改不该修改的内存。直接的作用就是让更多的逻辑错误在编译期被发现。
程序猿编码欢迎关注公众号【程序猿编码】,添加本人微信号(17865354792),回复:领取学习资料。或者回复:进入技术交流群。网盘资料有如下:
欢迎领取:百度网盘资料
网友评论