const指针和指向const指针

const指针和指向const指针

ID:20373832

大小:50.00 KB

页数:8页

时间:2018-10-12

上传者:U-4550
const指针和指向const指针_第1页
const指针和指向const指针_第2页
const指针和指向const指针_第3页
const指针和指向const指针_第4页
const指针和指向const指针_第5页
资源描述:

《const指针和指向const指针》由会员上传分享,免费在线阅读,更多相关内容在行业资料-天天文库

const指针和指向const的指针博客分类:·C系列 指向const对象的指针      指向const对象的指针就是一个指针,不能通过它来修改它所指向的对象的值。声明方法:constint*p;       const对象在初始化后是不允许对其值进行修改的,因此,我们不能用一个普通指针指向一个const对象,即下面的赋值会引起编译错误:  constinti=1;  int*p=&i;否则的话,我们就可以利用普通指针来修改一个const对象的值,那么const也就毫无意义了。正确的方法是利用一个指向const对象的指针来获取const对象的地址:    constinti=1;  constint*p=&i;    charconst*p==constchar*p;这样,利用指向const对象的指针也是不能修改它所指向的const对象的值的。注意:  1.指向const对象的指针本身不是const类型(这也是它与const指针的主要不同点),所以它可以指向另一个const对象  2.指向const对象的指针可以被赋予一个非const对象的地址,但是此时试图通过此指针来修改对象的值的操作是非法的 const指针     const指针就是一个指针,它本身就是const类型,所以将它初始化后不能再改变它的指向,即不能让它指向一个新的对象。声明方法:  int*constp;//指向非const对象的const指针  constint*constp;//指向const对象的const指针使用const指针不可以修改其地址值,但是const指针指向非const对象,就可以利用它修改它所指向的对象的值技巧:    如果指针名前紧邻的关键字为const,那么它就是一个const指针;如果声明指针所指向的对象类型前有const关键字,那么它就是一个指向cosnt对象的指针。补充:    Bjarne在他的TheC++Programming Language里面给出过一个助记的方法:把一个声明从右向左读(这可能和我们平常习惯有所不同,需要特别注意)。     char *constcp;(*读成pointerto)    cpisaconstpointertochar,亦即指针常量,cp值不可改变,但*cp,也就是cp所指对象能够改变。    constchar*p;    pisapointertoconstchar,亦即指向常量的指针,所以p所指的对象不可改变。 补:一、可能的组合:   (1)constchar*p    (2)charconst*p   (3)char*constp   (4)constchar**p   (5)charconst**p   (6)char*const*p   (7)char**constp    当然还有在(5)、(6)、(7)中再插入一个const的若干情况,不过分析了以上7中,其他的就可类推了!二、理解助记法宝:    1。关键看const修饰谁。    2。由于没有const*的运算,若出现const*的形式,则const实际上是修饰前面的。    比如:charconst*p,由于没有const*运算,则const实际上是修饰前面的char,因此charconst*p等价于constchar*p。也就是说上面7种情况中,(1)和(2)等价。同理,(4)和(5)等价。在(6)中,由于没有const*运算,const实际上修饰的是前面的char*,但不能在定义时转换写成const(char*)*p,因为在定义是"()"是表示函数。三、深入理解7种组合   (0)程序 在执行时为其开辟的空间皆在内存(RAM)中,而RAM里的内存单元是可读可写 的;指针只是用来指定或定位要操作的数据的工具,只是用来读写RAM里内存单元的工作指针 。若对指针不加任何限定,程序中一个指针可以指向RAM中的任意位置(除了系统敏感区,如操作系统内核所在区域)并对其指向的内存单元进行读和写操作(由RAM的可读可写属性决定);RAM里内存单元的可读可写属性不会因为对工作指针的限定而变化(见下面的第4点),而所有对指针的各种const限定说白了只是对该指针 的 读写权限 (包括读写位置)进行了限定 。    (1)char*p:p是一个工作指针,可以用来对任意位置 (非系统敏感区域)进行读操作和写操作 ,一次读写一个字节(char占一个字节)。    (2)constchar*p或者charconst*p(因为没有const*p运算,因此const修饰的还是前面的char):可以对任意位置(非系统敏感区域)进行“只读” 操作。(“只读”是相对于char*p来说所限定的内容)   (3)char*constp(const修饰的是p):只能对“某个固定的位置” 进行读写操作,并且在定义p时就必须初始化(因为在后面不能执行“p=..”的操作,因此就不能在后面初始化,因此只能在定义时初始化)。(“某个固定的位置”是相对于char*p来说所限定的内容)可以总结以上3点为:char*p中的指针p通常是”万能”的工作指针 ,而(2)和(3)只是在(1)的基础上加了些特定的限制 ,这些限制在程序中并不是必须的,只是为了防止程序员的粗心大意而产生事与愿违的错误。另外,要明白“每块内存空间都可有名字;每块内存空间内容皆可变(除非有所限) ” 。比如函数里定义的chars[]="hello";事实上在进程的栈内存里开辟了6个变量共6个字节的空间,其中6个字符变量的名字分别为:s1[0]、s1[1]、s1[2]、s1[3]、s1[4]、s1[5](内容是''){   待验证 : 还有一个4字节的指针变量s 。不过s是“有所限制”的,属于char*const类型,也就是前面说的  (3)这种情况,s一直指向s[0],  即(*s==s[0]=='h'),可以通过*s='k'来改变s所指向的s[0]的值,但不能执行(char*h=“aaa”;s=h;)来对s另外赋值。}    (4)上面的(2)和(3)只是对p进行限定,没有也不能对p所指向的空间进行限定,对于"chars[]="hello";constchar*p=s;"虽然不能通过*(p+i)='x'或者p[i]='x'来修改数组元素s[0]~s[4]的值,但可以通过*(s+i)='x'或者s[i]='x'来修改原数组元素的值--RAM里内存单元的可读可写属性不因对工作指针的限定而改变,而只会因对其本身的限定而改变。如constcharc=‘A’,c是RAM里一个内存单元(8字节)的名字,该内存单元的内容只可读,不可写。   (5)constchar**p或者charconst**p:涉及两个指针p和*p。由于const修饰char,对指针p没有任何限定,对指针*p进行了上面情况(2)的限定。  (6)char*const*p:涉及两个指针p和*p。由于const修饰前面的char*,也就是对p所指向的内容*p进行了限定(也属于前面的情况(2))。而对*p来说,由于不能通过"*p=..."来进行另外赋值,因此属于前面的情况(3)的限定。  (7)char**constp:涉及两个指针p和*p,const修饰p,对p进行上面情况(3)的限定,而对*p,没有任何限定。四、关于char**p、constchar**p的类型相容性问题    1。问题       char*p1;const*p2=p1;//合法       char**p1;const char**p2=p1;//不合法,会有警告warning:initializationfromincompatiblepointertype        char**p1;charconst**p2=p1;//不合法,会有警告warning:initializationfromincompatiblepointertype       char**p1;char*const*p2=p1;//合法    2。判断规则       明确const修饰的对象!对于指针p1,和p2,若要使得p2=p1成立,则可读做 :             “p1是指向X类型的指针,p2是指向“带有const限定”的X类型的指针 “。      char*p1;const*p2=p1;//合法:p1是指向(char)类型的指针,p2是指向“带有const限定"的(char)类型的指针。      char**p1;const char**p2=p1;//不合法:p1是指向(char*)类型的指针,p2是指向((constchar)*)类型的指针。     char**p1;charconst**p2=p1;//不合法;与上等价。      char**p1;char*const*p2=p1;//合法: p1是指向(char*)类型的指针,p2是指向“带有const限定"的(char*)类型的指针。五、其他  1。含有const的单层或双层指针的统一读法:         “p是一个指针,是一个[“带有const限定”的]指向[”带有const限定”的]X类型的指针”。  2。定义时const修饰的对象是确定的,但不能在定义时加括号,不然就和定义时用“()”表示的函数类型相混淆了!因此定义时不能写(char*)const*p或者(constchar)**p。六、问题探讨(由于博文后的留言有字符数目限制,将回复移到这里)  问题1 (见博文后留言):讲解非常好,不过有个问题想探讨下:例如:constcharwang[]={"wang"};char*p;p=wang;是错误的。所以char*p中的P不能指向常变量。(1)需要补充纠正。  回复 : 你好!谢谢指正!我在ubuntu10.04(gcc4.4.3)下做了如下测试:   //test_const.c  #include  intmain()  {    constcharwang[]={"wang"};    char*p;    p=wang;    p[2]='c';    printf("pis%s ",p);    return0; }编译 :       gcc-otest_consttest_const.c输出如下 :      test_const.c:Infunction‘main’:      test_const.c:17:warning:assignmentdiscardsqualifiersfrompointertargettype执行:      ./test_const      piswacg结论: 根据本博文中第四点--相容性问题,将const型的wang赋值给p是不合法的。但编译器对其的处理只是警告,因此执行时通过p修改了只读区域的数据。这应该是该编译器处理不严所致后果,不知你用的什么编译器?   问题2  回答 http://www.linuxsir.org/bbs/showthread.php?t=239058 提到的问题在c语言里//test.cintmain(){  constchar*s1="test";  char*s2=s1;  s2="It'smodified!";  printf("%s ",s1);}out:It'smodified!;这样也可以吗?照我的理解岂不是const限定符在c语言里只是摆设一个?    回复: (1)首先,以上代码编译时会出错warning:initializationdiscardsqualifiersfrompointertargettype,    因为char*s2=s1和问题1提到的一样,不符合相容规则。(2)输出结果是正确的,代码分析如下:  intmain(){  constchar*s1="test";  // 在只读数据区(objdump-htest后的.rodata区)开辟5字节存储"test",并用s1指向首字符‘t’。   char*s2=s1;                // s2也指向只读数据区中“test”的首字符't'。   s2="It'smodified!";      // 在只读数据区开辟15字节存储"It'smodified!",并将s2由指向't'转而指向"It'smodified!"的首字符'I'。   printf("%s ",s1);          //从s1所指的‘t’开始输出字符串"test"。 }(3)总结:提问者的误区在于,误以为s2="It's modified!"是对“test”所在区域的重新赋值,其实这里只是将“万能”工作指针s2指向另外一个新开辟的区域而已。比如若在char*s2=s1后再执行s2[2]='a'则是对“test”的区域进行了写操作,执行时会出现段错误。但这个段错误其实与const没有关系,因为“test”这块区域本身就是只读的。为了防止理解出错,凡事对于对指针的赋值(比如 s2="It'smodified!" ),则将其读做:将s2指向“ It'smodified! ”所在区域的首字符。(4)额外收获:执行gcc-otesttest.c后,“test”、“It'smodified!”、"%s "都被作为字符串常量存储在二进制文件test的只读区   域(.rodata)。事实上,一个程序从编译到运行,对变量空间分配的情况如下:A。赋值了的全局变量或static变量=>放在可执行文件的.data段。B。未赋值的全局变量或static变量=>放在可执行文件的.bss段。C。代码中出现的字符串常量或加了const的A=>放在可执行文件的.rodata段。D。一般的局部变量=>在可执行文件中不占空间,在该二进制文件作为进程在内存中运行时才为它分配栈空间。E。代码中malloc或new出的变量=>在可执行文件中不占空间,在该二进制文件作为进程在内存中运行时才为它分配堆空间。    问题3:(待进一步分析) 验证博文中三(3)提到的是否为s分配空间,初步分析结果为:不分配!文中的s只是s[0]的地址的代号而已。#includeintmain(){   inta=3;   chars1[]="test";   intb=4;   chars2[]="test2";   printf("theaddressofais%u ",&a);   printf("s1is%u ",s1);   printf("theaddressofs1is%u ",&s1);   printf("theaddressofbis%u ",&b);   printf("s2is%u ",s2);   printf("theaddressofs2is%u ",&s2);  }输出结果:theaddressofais3213037836s1is3213037827theaddressofs1is3213037827theaddressofbis3213037832s2is3213037821theaddressofs2is3213037821    由结果可以看出,编译器做了些优化。  七、其他相关经典文章转载   王海宁,华清远见嵌入式学院讲师,对const关键字的理解   http://www.embedu.org/Column/Column311.htm     目前在进行C语言补习时,发现很多的同学对于const这个关键字的理解存在很大的误解。现在总结下对这个关键字理解上的误区,希望在以后的编程中,能够灵活使用const这个关键字。1、const修饰的变量是常量还是变量    对于这个问题,很多同学认为const修饰的变量是不能改变,结果就误认为该变量变成了常量。那么对于const修饰的变量该如何理解那?下面我们来看一个例子:intmain        {                charbuf[4];                constinta=0;        a=10;        }    这个比较容易理解,编译器直接报错,原因在于“a=10;”这句话,对const修饰的变量,后面进行赋值操作。这好像说明了const修饰的变量是不能被修改的,那究竟是不是那,那么下面我们把这个例子修改下:intmain        {                charbuf[4];                constinta=0;        buf[4]=97;                printf(“theais%d ”,a);        }其中最后一句printf的目的是看下变量a的值是否改变,根据const的理解,如果const修饰的是变量是不能被修改的话,那么a的值一定不会改变,肯定还是0。但是在实际运行的结果中,我们发现a的值已经变为97了。这说明const修饰的变量a,已经被我们程序修改了。那综合这两个例子,我们来分析下,对于第二例子,修改的原因是buf[4]的赋值操作,我们知道buf[4]这个变量已经造成了buf这个数组变量的越界访问。buf数组的成员本身只有0,1,2,3,那么buf[4]访问的是谁那,根据局部变量的地址分配,可以知道buf[4]的地址和inta的地址是一样,那么buf[4]实际上就是访问了constinta;那么对buf[4]的修改,自然也修改了constinta的空间,这也是为什么我们在最后打印a的值的时候看到了97这个结果。那么我们现在可以知道了,const修饰的变量是不具备不允许修改的特性的,那么对于第一个例子的现象我们又如何解释那。 第一个例子,错误是在程序编译的时候给出的,注意这里,这个时候并没有生成可执行文件,说明const修饰的变量可否修改是由编译器来帮我们保护了。而第二个例子里,变量的修改是在可执行程序执行的时候修改的,说明a还是一个变量。综上所述,我们可以得出一个结论,那就是const修饰的变量,其实质是告诉程序员或编译器该变量为只读,如果程序员在程序中显示的修改一个只读变量,编译器会毫不留情的给出一个error。而对于由于像数组溢出,隐式修改等程序不规范书写造成的运行过程中的修改,编译器是无能为力的,也说明const修饰的变量仍然是具备变量属性的。2、被const修饰的变量,会被操作系统保护,防止修改     如果对于第一个问题,有了理解的话,那么这个问题,就非常容易知道答案了。Const修饰的变量是不会被操作系统保护的。其原因是操作系统只保护常量,而不会保护变量的读写。那么什么是常量?比如“helloworld”这个字符串就是被称为字符串常量。对于这个问题的另一种证明方法,可以看下面这个程序:intmain        {                constinta;                char*buf=“helloworld”;        printf(“the&ais%p,thebufis%p ”,&a,buf);        }可以发现buf保存的地址是在0x08048000这个地址附近的,而a的地址是在0xbf000000这个地址附近的,而0x08048000附近的地址在我们linux操作系统上是代码段。这也说明了常量和变量是存放在不同区域的,自然操作系统是会保护常量的。如果我们知道这个道理后,再看下面的题目:intmain        {                char*buf=“hello”;        buf[0]=‘a’;                printf(“thebufis%s ”,buf);        }我们可以思考下,这个程序的运行结果会是什么呢?

当前文档最多预览五页,下载文档查看全文

此文档下载收益归作者所有

当前文档最多预览五页,下载文档查看全文
温馨提示:
1. 部分包含数学公式或PPT动画的文件,查看预览时可能会显示错乱或异常,文件下载后无此问题,请放心下载。
2. 本文档由用户上传,版权归属用户,天天文库负责整理代发布。如果您对本文档版权有争议请及时联系客服。
3. 下载前请仔细阅读文档内容,确认文档内容符合您的需求后进行下载,若出现内容与标题不符可向本站投诉处理。
4. 下载文档时可能由于网络波动等原因无法下载或下载错误,付费完成后未能成功下载的用户请联系客服处理。
关闭