频道栏目
首页 > 资讯 > C语言 > 正文

C语言关键字const用法

17-08-04        来源:[db:作者]  
收藏   我要投稿

C语言中的const

const是C语言中保留的一个关键字,用来定义常量,如果一个变量被const修饰,那么它的值就不能被改变。使用符号常量写出的代码更容易维护;一些指针常常是边读边移动,而不是边写边移动;许多编程规范对于函数参数会强制要求只读不写,在这些情况下,都需要借助const实现。

那么有些人会问,C语言中不是已经存在#define了吗,为什么还要使用const呢?相比于#define,const修饰符有如下优势:
1. const能够使编译器进行类型检查,而预编译指令#define只是简单的对值进行替换。
2. const可以保护被修饰的变量等,防止变量因为意外被修改,从而增强程序的健壮性。
3. 编译器通常不为普通const常量分配存储空间,而是将他们保存在符号表中,这使得它成为了一个编译期间的常量,没有了存储于内存操作,使得它的效率很高。

const用法

const最常见的用法就是作为数组的边界和switch分情况标号,分类如下:
常变量: const + 类型说明符 + 变量名
常引用: const + 类型说明符 + &引用名
常对象: 类名 + const 对象名
常成员函数: 类名::fun(形参) + const
常数组: 类型说明符 + const 数组名[大小]
常量指针: const + 类型说明符* 指针名 或者 类型说明符 + const *指针名
首先提示的是:在常变量(const + 类型说明符 + 变量名)、常引用(const + 类型说明符 + &引用名)、常对象(类名 + const 对象名)、 常数组(类型说明符 + const 数组名[大小]), const”与“类型说明符”或“类名”(其实类名是一种自定义的类型说明符) 的位置可以互换。如:

const int a=5; 
int const a=5; //等同

其实这种可以很好理解,const只能修饰int(类型名或类名),所以二者是等同的。

修饰局部变量

const int n=5;
int const n=5;

这两种写法是一样的,都是表示变量n的值不能被改变了,需要注意的是,用const修饰变量时,一定要给变量初始化(用来修饰函数的形参除外),否则之后就不能再进行赋值了。C语言中,const定义的常量是全局的,C++中则是视声明位置而定。
接下来看看const用于修饰常量静态字符串,例如:

const char* str = "fdsafdsa";

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

常量指针与指针常量

常量指针

常量指针表示一个指向常量的指针,即该指针指向的内容是个常量(至少该常量指针是这样认为的),可以有如下的定义方式:

const int * n;
int const * n;

需要注意以下两点:
1.常量指针指向的值是不能改变的,但是这不意味指针本身不能改变,常量指针是可以指向其他的地址的。

int a=5;
int b=6;
const int* n = &a;
int const *n1 = &a; 
n=&b;

这个例子中,n和n1是等价的,都是指向a的一个常量指针,这时候有人会说,常量指针指向的应该是一个常量吧,但是a并不是一个常量啊,为什么常量指针依旧可以指向a呢,这时候又要注意一下了:
编译器允许把非 const 对象的地址赋给指向 const 对象的指针,不允许把一个 const 对象的地址赋给一个普通的、非 const 对象的指针。
其实这个也很好理解,对于一个变量,我可以对你增加束缚,用指向const对象的指针指向你,这只是意味着我现在认为你是常量,不能通过这个常量指针对你进行修改,但是对于第二种情况就不一样了,你本来是一个被束缚的常量,如果用一个普通的指针指向你的话,就意味着我认为你这个常量是一个变量,这样或许我会在下面的代码中,对你的值进行修改,对一个const常量进行修改,会发生什么,你应该明白。
然后回到上面的代码,我们将a的地址给了n,然后在下一行又改变了n的值,将b的地址赋给了n,这是没有任何问题的,因为n只是一个指针,指向了一个常量,所以被称为常量指针,它本身依旧是一个普通的指针,比较特殊的是它所指向的内容,而不是它本身。所以它本身的值可以修改,指向不同的内容。
2.当常量指针指向了一个变量时,不能通过这个常量指针改变变量的值,但是还是可以通过其他的引用来改变变量的值的。

int a=5;
const int* n=&a;
a=6; // 正确
*n = 7; //错误,对于常量指针,不能通过该指针来改变所指向的内容(即使它所指向的内容并不是一个常量)。
int *ni = (int *)n;
*n = 100; //正确

在n这个指针眼中,n所指向的内容是一个常量,所以不允许别人通过n本身对a进行修改,但是a本质上是一个变量,所以我们可以直接通过变量名或者新的普通指针对a进行修改。实际上,在将程序载入内存的时候,会有专门的一块内存区域来存放常量。但是,上面的a本身不是常量,是存放在栈或者堆中的。我们仍然可以修改它的值。而n不能修改指向的值应该说是编译器的一个限制。

指针常量

指针常量是指指针本身是一个常量,它只能指向一个固定的地址,不能指向别的地址。写法如下:

int *const n;

和上面的常量指针不一样,指针常量是指指针本身比较特殊,而它所指的内容并不特殊,因此,指针常量指向的内容是可以修改的,可以通过别的指向该地址的指针进行修改。如下所示:

int a=5;
int *p=&a;
int* const n=&a;
*p=8;

在函数中,指针常量时表示不允许将该指针指向其他内容:

void func_02(int* const p)
{
    int *pi = new int(100);
    /* 错误!P是指针常量。不能对它赋值。 */
    p = pi;
}

int main()
{
    int* p = new int(10);
    func_02(p);
    delete p;
    return 0;
}

然后看下面的代码:

const int *m1 = new int(10);
int* const m2 = new int(20);

在上面的两个表达式中,最容易让人迷惑的是const到底是修饰指针还是指针指向的内存区域?其实,只要知道:const只对它左边的东西起作用,唯一的例外就是const本身就是最左边的修饰符,那么它才会对右边的东西起作用。根据这个规则来判断,m1应该是常量指针(即不能通过m1来修改它所指向的内容。);而m2应该是指针常量(即不能让m2指向其他的内存地址)。或者我们可以把星号看做指针,把const看做常量,那么m1就是常量指针,m2就是指针常量。

指向常量的常指针

是以上两种的结合,指针指向的位置不能改变并且也不能通过这个指针改变变量的值,但是当它指向一个变量时,依然可以通过其他的普通指针改变变量的值。
const int* const p;

修饰函数的参数

根据常量指针与指针常量,const修饰函数的参数也是分为三种情况
1.防止修改指针指向的内容

void StringCopy(char *strDestination, const char *strSource);

其中 strSource 是输入参数,strDestination 是输出参数。给 strSource 加上 const 修饰后,如果函数体内的语句试图改动 strSource 的内容,编译器将指出错误。这种形式通常用于在数组形式的参数中模拟传值调用。也就是相当于函数调用者声称:”我给你一个指向它的指针,但你不能去修改它。”如果函数编写者遵循了这个约定,那么就相当于模拟了值传递。这也是const最有用之处了:用来限定函数的形参,这样该函数将不会修改实参指针所指的数据。这里注意了,是函数不应该去修改而不是不能修改,也就是说const不能阻止参数的修改(原因见上)。
2.防止修改指针指向的地址

void swap (int* const p1 , int* const p2);

指针p1和指针p2都是指针常量,指向的地址都不能修改。

修饰函数的返回值

很多时候,我们的函数中会返回一个地址或者引用。调用这得到这个返回的地址或者引用后就可以修改所指向或者代表的对象。这个时候如果我们不希望这个函数的调用这修改这个返回的内容,就应该返回一个常量。
如果给以“指针传递”方式的函数返回值加 const 修饰,那么函数返回值(即指针)的内容不能被修改,该返回值只能被赋给加const 修饰的同类型指针。
例如函数

const char * GetString(void);

如下语句将出现编译错误:

char *str = GetString();

正确的用法是

const char *str = GetString();

修饰全局变量

全局变量的作用域是整个文件,我们应该尽量避免使用全局变量,以为一旦有一个函数改变了全局变量的值,它也会影响到其他引用这个变量的函数,导致除了bug后很难发现,如果一定要用全局变量,我们应该尽量的使用const修饰符进行修饰,这样方式不必要的以为修改,使用的方法与局部变量是相同的。

const 在c和c++中的区别

C++中的const正常情况下是看成编译期的常量,编译器并不为const分配空间,只是在编译的时候将期值保存在名字表中,并在适当的时候折合在代码中.所以,以下代码:
using namespace std;
int main()
{
const int a = 1;
const int b = 2;
int array[ a + b ] = {0};
for (int i = 0; i < sizeof array / sizeof *array; i++)
{
cout << array << endl;
}
}
在可以通过编译,并且正常运行.但稍加修改后,放在C编译器中,便会出现错误:
int main()
{
int i;
const int a = 1;
const int b = 2;
int array[ a + b ] = {0};
for (i = 0; i < sizeof array / sizeof *array; i++)
{
printf(“%d”,array);
}
}
错误消息:
c:\test1\te.c(8): error C2057: 应输入常数表达式
c:\test1\te.c(8): error C2466: 不能分配常数大小为 0 的数组
出现这种情况的原因是:在C中,const是一个不能被改变的普通变量,既然是变量,就要占用存储空间,所以编译器不知道编译时的值.而且,数组定义时的下标必须为常量. 在C语言中: const int size; 这个语句是正确的,因为它被C编译器看作一个声明,指明在别的地方分配存储空间.但在C++中这样写是不正确的.C++中const默认是内部连接,如果想在C++中达到以上的效果,必须要用extern关键字.即C++中,const默认使用内部连接.而C中使用外部连接.
(1) 内连接:编译器只对正被编译的文件创建存储空间,别的文件可以使用相同的表示符或全局变量.C/C++中内连接使用static关键字指定.
(2) 外连接:所有被编译过的文件创建一片单独存储空间.一旦空间被创建,连接器必须解决对这片存储空间的引用.全局变量和函数使用外部连接.通过extern关键字声明,可以从其他文件访问相应的变量和函数.
相关TAG标签
上一篇:springboot+Junit测试rest接口,报错显示url无法连接
下一篇:jetty部署
相关文章
图文推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站