一、定义
.const本身的定义很好理解。从英文意思可以明确的得出计算机的含义:常量修饰符。
二、const存在的意义
const变量和其他变量最大的区别,const变量是存储在符号表中的。相对于#define,关于它的优点,有人总结如下:
1、预编译指令只是对值进行简单的替换,不能进行类型检查
2、可以保护被修饰的东西,防止意外修改,增强程序的健壮性
3、编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。
三、关于const的一个误区 :const的位置不同其含义也不同,比如 const int * 和 int *const 。其实这是指针惹的祸,与const无关。
正确的理解:const的意思很简单,就是常量修饰符,相对于其他修饰符,在左在右没有关系,比如 const int 和 int const。它所表达的意思是一样的。
四、要理解第三点,const的误区,就要搞清楚常量指针与指针常量的区别。
关于这两点,百度百科解释的很好。度娘还是有很强的科技含量的,虽然它的付费推广太恶心了。
指针常量的百科
https://baike.baidu.com/item/%E6%8C%87%E9%92%88%E5%B8%B8%E9%87%8F/5333107?fr=aladdin
常量指针的百科
https://baike.baidu.com/item/%E5%B8%B8%E9%87%8F%E6%8C%87%E9%92%88/1673669?fr=aladdin
指针常量:
1,指针常量的本质是一个常量,并且使用指针来修饰它。
2,通过对const定义,我们可以简单理解为这个指针是个常量,它不可以被修改。即它只能指向开始时我们给赋值的变量,不可以被修改从而再指向其他的变量。
3,指针常量一旦定义,他就不可以再指向其他的变量,但是它指向的是一个变量,所以我们可以修改它指向的变量的值。
常量指针:
1,常量指针本质是指针,常量修饰它,表示这个指针是一个指向常量的指针(变量)。 //这是度娘的官话,个人表示解释的很好。
2,如果我们定义了一个常量指针,那么它指向的对象是常量,这个对象不能通过我们的指针被修改。
这里也是最容易引起理解错误的地方,定义一个常量指针的意义只是我们不能通过这个指针来修改它指向的对象。而其实这个对象本身很有可能是个变量,我们可以在其他的地方轻松修改。
常量指针的意义:当我们需要使用某个指针指向的内容但是不需要对其更改,而且也不希望因为误操作而更改指针内容时,我们可以把这个指针定义为常量指针。!给自己点个赞!虽然做了这么久程序员还在理解这些很基础的知识。
例如:
int a=1;
int b=2;
//我们定义一个指针常量
int *const p=&a;
//因为指针是个常量,它的值是不能变的,所以这里是错误的。
p=&b; //编译不通过
//但是我们可以通过这个指针来改变它指向的变量的值:
*p= 3; //此时,a的值变成了3。
//我们定义一个常量指针
const int *p=&a;
//如下就是错误的
*p=3; //编译时系统会报错,因为p是一个常量的指针,而常量是不可以被修改的。
//但是我们可以修改这个指针,让他指向其他的值。
p=&b;
但是不管我们怎么修改p,它都是一个常量指针,我们不能够通过它来修改这个指针所指向的变量的值。
*p=3; //这种写法永远是错误的。
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void normal_const_test(){
//我们对于一个const修饰的常量,我们只能在定义的时候赋值。
// const int a;
// int const a;
// a =1;
return;
}
void const_pointer_test(){
int int_data = 1;
int oth_data =2;
const int *c=&int_data;
printf("data of c pointer to: %d\n", *c);
c=&oth_data;
printf("data of c pointer to: %d\n", *c);
// *c=3; //err
int *const d = &oth_data;
//err
//d=&int_data;
*d=3;
printf("data of d pointer to: %d\n", *d);
}
int main(int argc, char* argv[])
{
int ret=-1;
normal_const_test();
const_pointer_test();
return 0;
}
度娘还有如下的讲解实例:
int a = 2014;
const int b = 2015;
const int *c = &a; // 这是合法的,非法的是对c的使用
*c = 2016; // 非法,但可以这样修改c指向的对象的值:a = 2016;
const int *d = &b; // b是常量,d可以指向b,d被赋值为b的地址是合法的
const(*号)左边放,我是指针变量指向常量;
const(*号)右边放,我是指针常量指向变量;
const(*号)两边放,我是指针常量指向常量;
指针变量能改指向,指针常量不能转向!
要是全都变成常量,锁死了,我不能转向,你也甭想变样!
* (指针)和 const(常量) 谁在前先读谁 ;*象征着地址,const象征着内容;谁在前面谁就不允许改变。
对最后这句话表示赞赏:
* (指针)和 const(常量) 谁在前先读谁 ;*象征着地址,const象征着内容;谁在前面谁就不允许改变。
同时,很久以前面试时,一个前辈的讲解也很到位,可以我没有遇到个好的领导,一切都要自己学。 ):
他说:
关于const,我们以*为分水岭,const在哪边,就意味着谁不变。
比如 int const * p, 和 int * const p;
const 和int在一起,表示这个int值不能变。const和指针在一起,表示这个指针不能变。
不同的总结方式,异曲同工之妙。
五、const放在函数中的理解:
1,首先,可以返回头再看一下常量指针与指针常量的区别。
2,const修饰函数的具体实例:
常量指针,防止修改内容:
void string_copy(char *dst, const char *src);
指针常量,防止修改指针指向的地址
void swap(int * const p1, int * const p2);
上面的例子是前辈写的,它的意思是在不修改p1,p2指向的地址的情况下,交换两者的内容。
其实这个例子并不是很好,如果只是交换内容,我们从写代码的实用性来看,最好的方式就是交换地址。
我们再进一步理解,c里面有大量的二级指针(其实是多级的,我们一级一级的获取内容来解析数据)。我们可能同时有多个线程,访问同一个二级指针解析出来的数据。每个线程的读写都是不确定的。当一个线程a在开始时获取了一个变量的地址,而后一直使用该地址--没有再次重新获取和检测异常的机制,如果有其他的线程也在用这个地址并进行了修改,线程a再次使用它开始获取的地址就会出现异常。
所以,指针常量的意义是,当我们不希望在代码中更改一个指针所指向的内存区域时,这个区域所保存的值是否需要修改无关紧要,而且不论我们是需要修改这个区域所保存的值,只要我们不希望这个指针所指向的内存区域发生变化,就应该使用指针常量。
即:我们需要定义一个数据类型是指针类型的常量,指针常量。它是一个指针,指向一个内存区域,同时它也是一个常量,不可以被更改。它将永远指向这个内存区域,直到程序结束。
3,毕竟是一个应用点,还是写出来吧,指向常量的常指针:
const int * const p;
很好理解,都不能变。
终于彻底完成了const的blog,以后再混淆可以拿出来温习了。鼓励下自己,加油!
六、const + typedef + *
这是一件很神奇的事情,具体可以参考这个网址
http://www.360doc.com/content/12/1122/10/3735408_249472713.shtml
typede struct{
} *p_struct;
const p_struct my_str;
开始我也是习惯性的进行展开:
const struct{
} *my_str;
这样,我们很容易理解,my_str是一个常量指针,它指向一个结构体,这个结构体的值不能更改。
事实上,进行展开是我们对宏所进行的操作,而这也是宏的缺陷。宏是没有优先级的,不能够根据不同修饰符的语义来解析我们的语句。它只会完全按照字符转换的方式来翻译。
我们可以认为这是个语义的问题。
const 修饰的是 p_struct, 即:struct{} * ,这是一个指针。
所以它的正确展开是
struct{} * const my_str;
更进一步理解的话
typdef转义后和const一起,作为指针的修饰符。
这句话展开后应该是:
(const) (struct{} * ) my_str。 //它们是两个平级的修饰符,各自为政,都是修饰my_str;
它等于 (struct{}* ) (const) my_str;
这就像 const int a;和int const a;
而不是
const struct{}* my_str; //这时有三个修饰符,我们要以分析常量和指针的方式来进行理解。