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和#define的不同点:
1.const定义常量是有数据类型的,而#define宏定义常量却没有。
2.const常量有数据类型,而宏常量没有数据类型。编译器可以对const进行类型安全检查,而对后者只进行字符替
换,没有类型安全检查,并且在字符替换中可能会产生意料不到的错误。
3.有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试。
情景三: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=num;
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=num;
(*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=num;
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=num;
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=num;
(*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了!
最后附上在北邮人论坛上看到ebirthatsix对const的评论:
除了传递要求为const的参数以外,自己声明对象没有什么必须要加,但是对于一个逻辑上不应该被修改,应该为常量的对象,没有声明为const,就必须由程序员自己来维护,来记住这个变量不应该被修改,即使你不小心修改导致程序整体混乱了,编译器也不会报错
另,const和普通变量的声明存在于头文件时有区别,总之这些都是与你具体写程序的规划有关系,const这个玩意只是方便程序设计和程序编写,能够使程序更加的清晰,如果说我就是不爱用,就是喜欢一路变量用到底,那也没什么不行。。。
OK,over~
文章转载至:
http://roclinux.cn/?p=557
—————————————————————转载文章结束分隔线—————————————————————
const volatile int i=10;这行代码有没有问题?如果没有,那i 到底是什么属性?
回答一:
没有问题,例如只读的状态寄存器。它是volatile,因为它可能被意想不到地改变;它是const,因为程序不应该试图去修改它。volatile和const并不矛盾,只是控制的范围不一样,一个在程序本身之外,另一个是程序本身。
回答二:
没问题,const和volatile这两个类型限定符不矛盾。const表示(运行时)常量语义:被const修饰的对象在所在的作用域无法进行修改操作,编译器对于试图直接修改const对象的表达式会产生编译错误。volatile表示“易变的”,即在运行期对象可能在当前程序上下文的控制流以外被修改(例如多线程中被其它线程修改;对象所在的存储器可能被多个硬件设备随机修改等情况):被volatile修饰的对象,编译器不会对这个对象的操作进行优化。一个对象可以同时被const和volatile修饰,表明这个对象体现常量语义,但同时可能被当前对象所在程序上下文意外的情况修改。
答案一说的比较直接,这两个关键字只是对变量的一个修饰,另外关键字控制的范围也不一样,所以没什么问题。
const针对的本程序保证该值不变,volatile是针对程序外的(比如多线程,中断),避免优化后出现错误。