原创 情景分析“C语言的const关键字”

2015-3-13 14:13 1189 21 24 分类: MCU/ 嵌入式 文集: C语言

C语言中的const一直是C语言初学者心中的痛,这是因为const在不同位置有不同作用,在不同情景有不同角色。这让初学者摸不清头脑。今天,和大家一起研究一下const,让它的每个角色都“深入人心”!

==============================================================================================

情景一:最简单的const用法

 

#include
int main()
{
int const a;
a=5;
printf("a=%d\n",a);
return 0;
}

如果编译这个c文件,就会报错:

1071.c: In function ‘main’:
1071.c:5: error: assignment of read-only variable ‘a’

显而易见,这是const在搞鬼,因为声明了const的变量是不能修改的!

如果将源代码修改为如下这样,就没有问题了!

#include
int main()
{
int const a=5;
printf("a=%d\n",a);
return 0;
}

总结:const声明的变量必须要进行初始化赋值,如果错过这个机会,以后再想给const的变量赋值,可就没门了!切记~

PS:int const和const int是一回事,“颠倒写”都是可以的。以后遇到了别犯晕,呵呵。但是,还是要留个心眼,当const和指针搀和到一起时,这个“颠倒写”的规律可未必成立。

==============================================================================================

情景二:发明const为了什么?

在const诞生之前,开发者一直使用#define VAR 100来定义一些有特殊用途的类常量,不过这样定义是存在一些劣势的。因此const应运而生,之后开发者可以使用const int VAR=100;来定义类常量了。

至于为什么#define有其劣势,还要读者自己去google下。:)

==============================================================================================

情景三:const和指针的配合是噩梦!

你能分辨得清这些声明么:

const int *A;
int const *A;
int *const A;
const int *const A;

如果有点犯晕的话,那就先给出它们的讲解,然后继续看后面的情景分析吧。

const int *A; //修饰指向的对象,A可变,A指向的对象不可变
int const *A;   //修饰指向的对象,A可变,A指向的对象不可变
int *const A;   //修饰指针A, A不可变,A指向的对象可变
const int *const A; //指针A和A指向的对象都不可变

==============================================================================================

情景四:const int *A

[rocrocket@wupengchong const_test]$ cat test1.c

#include
int main()
{
int num=12;
const int *A=#
printf("result=%d\n",*A);
return 0;
}

编译执行结果为:

[rocrocket@wupengchong const_test]$ cc test1.c
[rocrocket@wupengchong const_test]$ ./a.out
result=12

接下来,我们动动手脚,在代码中加入了(*A)++;这条语句:

[rocrocket@wupengchong const_test]$ cat test1.c

#include
int main()
{
int num=12;
const int *A=#
(*A)++;
printf("result=%d\n",*A);
return 0;
}

编译这个c文件:

[rocrocket@wupengchong const_test]$ !cc
cc test1.c
test1.c: In function ‘main’:
test1.c:6: error: increment of read-only location ‘*A’
可以看到,报错了,报错的内容表示”*A”是只读的,不能修改。

我们再修改一下源代码为下面这样:

[rocrocket@wupengchong const_test]$ cat test1.c

#include
int main()
{
int num=12;
int tmp=100;
const int *A=#
A=&tmp;
printf("result=%d\n",*A);
return 0;
}

编译执行结果为:

[rocrocket@wupengchong const_test]$ !cc
cc test1.c
[rocrocket@wupengchong const_test]$ ./a.out
result=100

好了,如果你仔细看了这几个小得不能再小的程序,你自己都可以给出结论了!

结论:如果声明了const int *A,那么A值是可以修改的,而*A是不可以修改的。更通俗的说,A指针可以随便指向一个整型,但只要被A盯上了的整型变量在使用*A引用时就不能修改了。

[rocrocket@wupengchong const_test]$ cat test1.c

#include
int main()
{
int num=12;
int tmp=100;
const int *A=#
A=&tmp;
tmp=3;
printf("result=%d\n",*A);
return 0;
}

编译执行的结果为:

[rocrocket@wupengchong const_test]$ !cc
cc test1.c
[rocrocket@wupengchong const_test]$ ./a.out
result=3

结论2:即使A指向了tmp,我虽然不能修改*A,但是我仍然是可以用tmp来修改这个值的,完全不管*A的存在。呵呵

==============================================================================================

情景五:int *const A

[rocrocket@wupengchong const_test]$ cat test1.c

#include
int main()
{
int num=12;
int *const A=#
printf("result=%d\n",*A);
return 0;
}

编译执行结果为:

[rocrocket@wupengchong const_test]$ !cc
cc test1.c
[rocrocket@wupengchong const_test]$ ./a.out
result=12

我们稍微修改下源代码:

[rocrocket@wupengchong const_test]$ cat test1.c

#include
int main()
{
int num=12;
int tmp=100;
int *const A=#
A=&tmp;
printf("result=%d\n",*A);
return 0;
}

编译时报错了:

[rocrocket@wupengchong const_test]$ !cc
cc test1.c
test1.c: In function ‘main’:
test1.c:7: error: assignment of read-only variable ‘A’
[rocrocket@wupengchong const_test]$ cat test1.c

可见A本身的值已经不能再变了。

继续修改源代码如下:

[rocrocket@wupengchong const_test]$ cat test1.c

#include
int main()
{
int num=12;
int *const A=#
(*A)=100;
printf("result=%d\n",*A);
return 0;
}

编译执行结果为:

[rocrocket@wupengchong const_test]$ !cc
cc test1.c
[rocrocket@wupengchong const_test]$ ./a.out
result=100

可以看出,(*A)是可以改变的。

结论又可以轻易推出了:int *const A;   //const修饰指针A, A不可变,A指向的对象可变

==============================================================================================

情景六:const int *const A; //指针A和A指向的对象都不可变

[rocrocket@wupengchong const_test]$ cat test1.c

#include
int main()
{
int num=12;
int const *const A=#
(*A)=100;
printf("result=%d\n",*A);
return 0;
}

编译会报错:

[rocrocket@wupengchong const_test]$ !cc
cc test1.c
test1.c: In function ‘main’:
test1.c:6: error: assignment of read-only location ‘*A’

改下源代码:

[rocrocket@wupengchong const_test]$ cat test1.c

#include
int main()
{
int num=12;
int tmp=100;
int const *const A=#
A=&tmp;
printf("result=%d\n",*A);
return 0;
}

编译仍然会报错:

[rocrocket@wupengchong const_test]$ !cc
cc test1.c
test1.c: In function ‘main’:
test1.c:7: error: assignment of read-only variable ‘A’

呵呵,结论很明显了,const int *const A; //指针A和A指向的对象都不可变

当然const int *const A;和int const *const A=#是等价的!

情景七:如果const用在函数形参里呢?是不是又要复杂很多?

答案是NO!一点也不复杂。

来看看这个函数头:int addnum(const int num, int a, int b);

这个函数声明中的第一个形参是const int num,这就表明如果我调用了这个函数,那么第一个实参被传到addnum函数里之后,就不能再做修改了!呵呵 就这么简单。

给个例子吧,让大家能更一目了然:

[rocrocket@wupengchong const_test]$ cat test2.c

#include
int addto(const int num, int a, int b)
{
if(num==1){
return a+b;
}else{
return 0;
}
}
 
int main(){
int num=100;
int a=12,b=22;
int res;
num=1;
res=addto(num,a,b);
printf("res=%d\n",res);
return 0;
}

编译执行结果为:

[rocrocket@wupengchong const_test]$ !cc
cc test2.c
[rocrocket@wupengchong const_test]$ ./a.out
res=34

如果我修改一下,编译就会出错:

[rocrocket@wupengchong const_test]$ cat test2.c

#include
int addto(const int num, int a, int b)
{
if(num==1){
num=3;
return a+b;
}else{
return 0;
}
}
 
int main(){
int num=100;
int a=12,b=22;
int res;
num=1;
res=addto(num,a,b);
printf("res=%d\n",res);
return 0;
}

编译报错为:

[rocrocket@wupengchong const_test]$ !cc
cc test2.c
test2.c: In function ‘addto’:
test2.c:5: error: assignment of read-only location ‘num’

可见在函数里形参被声明为const的变量也是不能修改的哦!呵呵~

const其实不难,把本文的几个小例子看懂就OK了!

PARTNER CONTENT

文章评论3条评论)

登录后参与讨论

用户617569 2015-3-16 08:55

程序里很多语句显示的是const int *A=#,不知道啥意思,希望博主能改一下

用户1831175 2015-3-14 19:43

好东西

DiracFatCat 2015-3-13 17:47

好文
相关推荐阅读
潇洒哥 2015-07-08 16:01
位操作符的使用技巧
在C语言编程中,数据的位是可以操作的最小数据单位,理论上可以用“位运算”来完成所有的运算和操作。一般的位操作是用来控制硬件的,或者做数据变换使用,但是,灵活的位操作可以有效地提高程序运行的效...
潇洒哥 2015-04-04 17:39
finish shell分析之底层usart
rt_thread的finsh shell系统不愧是调试的一项利器,它可以除了完成一般shell的功能外,甚至还可以自定义命令。这个对功能单一的嵌入式系统来说是十分可贵的。在此我并不想对finsh...
潇洒哥 2015-04-01 21:29
__main() 和 main()
因为我们通常在BOOTLOADER中都已做好了比较细致的初始化工作,包括代码的搬运,所以我们最好别再调用库函数__main(),因为__main()作为ADS集成好的库函数,会对系统进行初始化设置...
潇洒哥 2015-03-26 22:54
RT-Thread学习之scons篇--解析rtconfig.py文件
rtconfig.py文件,主要用于指定编译器以及安装路径。除此之外,该文件中定义了大量的变量,这些变量包括编译选项,汇编选项,链接选项。   import os # toolchain...
潇洒哥 2015-03-26 22:53
RT-Thread学习之scons篇--SConsruct脚本文件解析
       scons的构建文件名称是统一的都称为SConstruct。其是scons所接受的编译脚本主文件。当然为了方便目录的组织,也允许在各个目录下面存放SConscript, 然后最上面S...
潇洒哥 2015-03-26 22:53
RT-Thread学习之scons篇--SConcript文件解析
SConscript文件是用来指定哪些文件会加入编译。先来分析下BSP主目录下的SConscript文件: import rtconfig Import('RTT_ROOT') fro...
EE直播间
更多
我要评论
3
21
关闭 站长推荐上一条 /3 下一条