C语言中关键字const的总结

 在总结const的用法时,我们先简单介绍一下const的概念!

const可以修饰变量,使该变量的值不能改变(即就是可以用来定义常量),而宏常量#define(预编译指令)也可以用来定义常量,这里我们将二者做一个简单的对比。

(1)const常量有数据类型,#define没有数据类型;

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

        #define只进行字符替换,没有类型安全检查,在字符替换过程中可能会出现一些错误,例如:边界问题;

(3)通常情况下,编译器不会为const常量分配存储空间,而是将其保存在符号表中,作为编译期间的一个常量,不会占用内存          以及不进行读操作,效率自然很高;

(4)有些集成化的调试工具可以对const常量进行调试,但不能对宏常量进行调试。(另外,在C++中,只使用const常量而不          使用宏常量,即const常量可以完全替代宏常量)。

有了以上的铺垫,我们来看一看const的用法吧!

一、const修饰常量

举例:

const char* str = "abcdefg";
如果没有const修饰,我们可以在这个字符串后面任意加一些其他的字符串,这样会导致只读存储区域被赋值,接着程序会马上异常终止;但是加上const之后,这样的错误会在程序编译阶段检测出来(即就是,让逻辑错误在编译期间被检测出来)。

二、const修饰变量

1.局部变量

举例:

const int a = 10;
int const a = 10;

以上两种表达形式都是一个意思,指的是变量a的值是10不能被改变了(常变量)。

但这里需要注意的是,const修饰变量时一定要给变量初始化,否则之后不能再进行任何赋值。原因是:给一个变量初始化时,内存会给其分配相应的空间,而如果不初始化,直接使用const,那么该内存空间中就不会存储该变量(见const概念(3))。

2.全局变量

const修饰全局变量和局部变量的用法大致相似,但需要注意的是,在编写代码时,应该避免使用全局变量(假如全局变量作用于一个文件之中,一般来说没有什么打的问题,但当该全局变量要在多个文件间共享时,就牵扯到一个存储类型的问题了)

(1)使用extern

//text.h
extern const double b;
//text.c
const double b = 2.18;

//其他使用b的地方,只需要包含该text.h文件即可,即:#include text.h,或者将该声明复制在需要的地方

这样做的好处是,所有程序连接结束后,所有需要b的地方共享同一块存储区域。

(2)使用static

//text.h
static const c = 2.14;
//需要使用变量c的.c文件中,必须包含该头文件,并且static关键字不能少。否则链接时会出现该变量被多次定义

这样做的实质是,每一份包含了该头文件的源文件,都在其内部进行了一次该变量的复制,其实该变量还是被定义了多次,占用了多个存储空间,但是当加上static关键字时,避免了文件间的定义冲突问题。但是会浪费存储空间,导致链接结束后的可执行文件变大,但是通常情况下,存储空间只有几个字节的变化,不会影响太大的问题。

三、const修饰指针(常量指针&指针常量)

一般有一下四种情况:

int b = 10;
const int *a = &b;//情况1
int const *a = &b;//情况2
int* const a = &b;//情况3
const int* const a = &b;//情况4

怎么区分这几种情况呢?

首先,我们要记住三句话。

(1)忽略类型int(因情况而定,有的代码中是char、double等等类型或者函数名等);

(2)const放在*号左边时,代表的意思是该指针变量指向的内容是不能通过指针变量来改变的(常量指针);

(3)const放在*号右边时,代表的意思是该指针变量本身的内容是不能改变的(指针常量)。

由此很明显我们可以判断以上四种情况:情况1、2属于常量指针,情况3属于指针常量,情况4既是常量指针也是指针常量。下面我们再分别对其进行讨论。

1.常量指针(情况1、2实际上是常量指针的两种写法,表示的意思都是一样的)

需要注意以下两点:

(1)常量指针虽然不能通过指针变量来改变其值,但是可以通过其他方式来改变该变量的值。举例如下:

#include<stdio.h>
int main()
{
	int b = 10;
	const int *a = &b;
	printf("%d\n", b);
	return 0;
}

运行结果:


#include<stdio.h>
int main()
{
	int b = 10;
	const int *a = &b;
	b = 20;
	printf("%d\n", b);
	return 0;
}

运行结果:


(2)虽然常量指针指向的内容不能被改变,但是指针本身可以改变(常量指针可以指向其他地址)。举例如下:

#include<stdio.h>
int main()
{
	int b = 10;
	const int *a = &b;
	printf("%d\n", a);
	return 0;
}

运行结果:


#include<stdio.h>
int main()
{
	int b = 10;
	int c = 20;
	const int *a = &b;
	a = &c;
	printf("%d\n", a);
	return 0;
}

运行结果:


2.指针常量(情况3)

需要注意的是,虽然指针本身不能被改变,但是可以通过其他方式来修改指针指向的内容。举例如下:

#include<stdio.h>
int main()
{
	int b = 10;
	int* const a = &b;
	printf("%d\n", a);
	return 0;
}

运行结果:


#include<stdio.h>
int main()
{
	int b = 10;
	int *p = &b;
	int* const a = &b;
	*p = 20;
	printf("%d\n", a);
	return 0;
}

运行结果:


3.指向常量的常指针(情况4)

常量指针和指针常量的结合就是情况4啦,指针本身不能改变,并且通过指针指向的内容也不能被改变。

通过上面的学习我们再来看一段代码,来巩固以下我们所学的知识!

#include <stdio.h>  
int main(void)
{
	const int a[10];
	const int *pa = a;
	int const *pb = a;
	int* const pc = a;
	pa++;
	(*pa)++;
	pb++;
	(*pb)++;
	pc++;
	(*pc)++;
}

运行结果:


针对以上问题找到其相对应出现问题的位置,如下图:


出现警告:在这个指针常量中,因为该数组已经被const修饰,也就是a的值不能改变,而根据我们学的知识这里是可以改变a的值,二者矛盾,又因为现阶段处于声明状态,因此出现报错情况。

四、const修饰函数的参数

由上面const和指针的分类,const在修饰函数参数时也可以分为以下三类:

1.防止修改指针指向的内容(常量指针的用法应用)
void Funtext(char *dest,const char *sou)

这个函数很常见,在模拟实现str类型的函数时,经常会用到。其中sou是输入参数,dest是输出参数,在输入参数sou前加上const时,如果函数体内部的语句想视图改变sou内的内容,编译器将会报错。在编程时,使用const会给我们带来很大的便利。

2.防止修改指针本身的内容(指针常量的用法应用)
void swap(int* const p1, int* const p2)

指针p1和p2本身的内容不可以改变。

3.上述两种情况的综合,既防止修改指针本身的内容,又防止指针指向的内容被修改。

void Fun(const int* const p)

五、const修饰函数的返回值

如果函数返回值以指针传递方式并且加const修饰,那么函数返回值(指针)的内容就不能被修改,该返回值只能被附加给加const修饰的同类型返回值(指针)。

举例:

const char* GetString(void);
//错误返回值
//char * GetString();
//正确返回值
//const char* GetString();

六、const和非const类型的转换

我们先来看一段代码:

#include<stdio.h>

void Fun(char *p)
{}
int main()
{
	const char *p1 = "hahaha";
	char *p2 = p1;
	Fun(p1);
	return 0;
}

这段代码的运行结果出现警告和报错,这是为什么呢?

我们来仔细想想!

类似于const  char*  p1,这样的形式,p1被const修饰,说明p1指向的内容不能改变,如果将p1赋值给类似于char*  p2这样的p2时就会发生危险,因为p1指向的内容是不可以被修改的,而p2指向的内容是可以修改的,编译器一般不提倡这种做法,会给出警告或报错。

由此可知,const  char*和char*是不同类型的,不能将前者赋值给后者。但是反过来是可以的,也就是说,可以将char*赋值给const  char*。其实仔细想想,确实如此,char*指向的内容是有读取和写入操作的,而const  char*指向的内容只有读取权限,降低权限不会带来什么问题,但是提高权限就会带来危险,因此编译器不会允许其通过。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值