标签:const char p char const p char cosnt p 指向常量字符串指针 常量字符串指针
案例1:
#include<iostream> using namespace std; void main(void) { // char* a 与 char a[] 的区别 char* a = "abcdef"; // a为一指针,其值可以改变。现在a指向的是一常量字符串 cout << a << endl; a = "ghijkl"; // a现在指向另一常量字符串 //a[0] = 'z'; // a指向的为常量,编译没问题,但运行出现异常 cout << a << endl; cout << sizeof(a) << endl; // 4 char b[] = "abcdef"; // b为数组首地址,为一常量,其值不可改变。所指内容不是常量字符串 cout << b << endl; //b = "ghijkl"; // 编译无法通过,b为常量,无法为其指定其它值 b[0] = 'z'; cout << b << endl; cout << sizeof(b) << endl; // 7 // ------------------------------------------------------------------------------ // const char* a 与 char* const a 区别 char c[] = "abcd"; char d[] = "efgh"; const char* pC = c; //pC[0] = 'z'; // 编译出错,pC为指向常量的指针,做不能修改指针指向的内容 cout << pC << endl; pC = d; cout << pC << endl; // pC指向的内容不能改,但可以修改指向 char e[] = "abcd"; char f[] = "efgh"; char* const pE = e; //pE = f; // pE为常量指针,不能改变其指向 pE[0] = 'z'; // pE为常量指针,虽然不能改变其指向,但可以修改指向内容 cout << pE << endl; } // 总结:(1) char* a这种形式:a为指针,可以改变其指向, // 其所指向的字符串为常量,不能修改其指向的内容。 // (2) char a[]这种形式,a为数组名,为常量,不能再 // 指向其它字符串,但其指向的内容不是常量字符串, // 故可以改变。 // (1) const char* a 为指向常量的指针,所指内容为常量, // 不能修改,但可以改变其指向,这种形式还与 // char const * a等价。 // (2) char* const a 为指针常量,不能改变其指向,但可 // 以修改其指向的内容。 // (3) const char* const a 为指向常量的常指针,即不能 // 改变其指向,也不能改变其指向的内容
几乎是C++面试中每次都会有的题目。
事实上这个概念谁都有,只是三种声明方式非常相似很容易记混。
Bjarne在他的The C++ Programming Language里面给出过一个助记的方法:
把一个声明从右向左读。
char * const cp; ( * 读成 pointer to ) cp is a const pointer to char
const char * p; p is a pointer to const char;
char const * p;
同上因为C++里面没有const*的运算符,所以const只能属于前面的类型。
C++标准规定,const关键字放在类型或变量名之前等价的。
const int n=5; //same as below
int const m=10;
const int *p; //same as below const (int) * p
int const *q; // (int) const *p
看一道以前Google的笔试题:
题目:const char *p="hello"; foo(&p); //函数foo(const char **pp)
下面说法正确的是[]
A.函数foo()不能改变p指向的字符串内容
B.函数foo()不能使指针p指向malloc生成的地址
C.函数foo()可以使p指向新的字符串常量
D.函数foo()可以把p赋值为 NULL.
至于这道题的答案是众说纷纭。针对上面这道题,我们可以用下面的程序测试:
#include <stdio.h> #include <stdlib.h> #include <stdio.h> void foo(const char **pp) { *pp=NULL; *pp="Hello world!"; *pp = (char *) malloc(10); snprintf(*pp, 10, "hi google!"); (*pp)[1] = 'x'; // runtime error } int main() { const char *p="hello"; printf("before foo %s/n",p); foo(&p); printf("after foo %s/n",p); p[1] = 'x'; //runtime error return; }结论如下:
const char*p,char const*p,char *const p,const char **p,char const**p,char *const*p,char**const p
比如函数里定义的char s[]="hello";事实上在进程的栈内存里开辟了6个变量共6个字节的空间,其中6个字符变量的名字分别为:s1[0]、s1[1]、 s1[2]、s1[3]、s1[4]、s1[5](内容是‘/0‘)
{
待验证 (见后面问题3的分析) : 还有一个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所指向的空间进行限定,对于"char s[]="hello";const char*p=s;"
虽然不能通过*(p+i)=‘x‘或者p[i]=‘x‘来修改数组元素s[0]~s[4]的值,但可以通过*(s+i)=‘x‘或者s[i]=‘x‘来修改原数组元素的值--RAM里内存单元的可读可写属性不因对工作指针的限定而改变,而只会因对其本身的限定而改变。如const char c=‘A‘,c是RAM里一个内存单元(8字节)的名字,该内存单元的内容只可读,不可写。
(5)const char **p或者char const**p :涉及两个指针p和 *p。由于const修饰char ,对指针p没有任何限定,对指针*p进行了上面情况(2)的限定。
(6)char *const *p:涉及两个指针p和 *p。由于const修饰前面的char*,也就是对p所指向的内容*p进行了限定(也属于前面的情况(2))。而对*p来说,由于不能通过"*p=..."来进行另外赋值,因此属于前面的情况(3)的限定。
(7)char **const p : 涉及两个指针p和 *p,const修饰p,对p进行上面情况(3)的限定,而对*p,没有任何限定。
问题1 : 例如: const char wang[]={"wang"}; char *p; p=wang; 是错误的。 所以char *p中的P不能指向常变量。
在ubuntu 10.04(gcc 4.4.3)下做了如下测试:
#include<stdio.h> int main() { const char wang[]={"wang"}; char *p; p=wang; p[2]='c'; printf("p is %s/n",p); return 0; }编译 :
问题2:
在c语言里 // test.c int main() { const char* s1 = "test"; char *s2 = s1; s2 = "It's modified!"; printf("%s/n",s1); } out: It's modified!; 这样也可以吗? 照我的理解岂不是const限定符在c语言里只是摆设一个?回复:
(2)输出结果是正确的,代码分析如下:
int main() { const char* s1 = "test"; // 在只读数据区(objdump -h test后的.rodata区)开辟5字节存储"test",并用s1指向首字符‘t’。 char *s2 = s1; // s2也指向只读数据区中“test”的首字符't'。 s2 = "It's modified!"; // 在只读数据区开辟15字节存储"It's modified!",并将s2由指向't'转而指向"It's modified!"的首字符'I'。 printf("%s/n",s1); // 从s1所指的‘t’开始输出字符串"test"。 }(3)总结:
提问者的误区在于,误以为s2 = "It‘s modified!"是对“test”所在区域的重新赋值,其实这里只是将“万能”工作指针s2指向另外一个新开辟的区域而已。比如若在char *s2 = s1后再执行s2[2]=‘a‘则是对“test”的区域进行了写操作,执行时会出现段错误。但这个段错误其实与const没有关系,因为“test”这块区域本身就是只读的。为了防止理解出错,凡事对于对指针的赋值(比如
s2 = "It‘s modified!" ),则将其读做:将s2指向“ It‘s modified! ”所在区域的首字符。
(4)额外收获:执行gcc -o test test.c后,“test”、“It‘s modified!”、"%s/n"都被作为字符串常量存储在二进制文件test的只读区 域 (.rodata)。事实上,一个程序从编译到运行,对变量空间分配的情况如下:
A。赋值了的全局变量或static变量=>放在可执行文件的.data段。
B。未赋值的全局变量或static变量=>放在可执行文件的.bss段。
C。代码中出现的字符串常量或加了const的A=>放在可执行文件的.rodata段。
D。一般的局部变量=>在可执行文件中不占空间,在该二进制文件作为进程在内存中运行时才为它分配栈空间。
E。代码中malloc或new出的变量=>在可执行文件中不占空间,在该二进制文件作为进程在内存中运行时才为它分配堆空间。
目前在进行C语言补习时,发现很多的同学对于const这个关键字的理解存在很大的误解。现在总结下对这个关键字理解上的误区,希望在以后的编程中,能够灵活使用const这个关键字。
1、 const修饰的变量是常量还是变量
对于这个问题,很多同学认为const修饰的变量是不能改变,结果就误认为该变量变成了常量。那么对于const修饰的变量该如何理解那?
下面我们来看一个例子:
int main() { char buf[4]; const int a = 0; a = 10; }这个比较容易理解,编译器直接报错,原因在于“a = 10;”这句话,对const修饰的变量,后面进行赋值操作。这好像说明了const修饰的变量是不能被修改的,那究竟是不是那,那么下面我们把这个例子修改下:
#include<cstdio> int main() { char buf[4]; const int a = 0; buf[4] = 97; printf("the a is %d/n",a); }其中最后一句printf的目的是看下变量a的值是否改变,根据const的理解,如果const修饰的是变量是不能被修改的话,那么a的值一定不会改变,肯定还是0。但是在实际运行的结果中,我们发现a的值已经变为97了。这说明const修饰的变量a,已经被我们程序修改了。
int main() { const int a; char *buf = "hello world"; printf("the &a is %p, the buf is %p/n",&a, buf); return 0; }可以发现buf保存的地址是在0x08048000这个地址附近的,而a的地址是在0xbf000000这个地址附近的,而0x08048000附近的地址在我们linux操作系统上是代码段。这也说明了常量和变量是存放在不同区域的,自然操作系统是会保护常量的。
程序1
#include<stdio.h> int main() { char *buf = "hello"; buf[0]='a'; printf("the buf is %s\n",buf); return 0; }
运行崩溃
程序2#include<stdio.h> int main() { char s[6]="hello"; char *buf=s; buf[0]='a'; printf("the buf is %s\n",buf); return 0; }运行正常
思考下,这两个程序的运行结果为什么会出现这样的区别呢?
const char*, char const*, char*const使用时的区别
标签:const char p char const p char cosnt p 指向常量字符串指针 常量字符串指针
原文地址:http://blog.csdn.net/tham_/article/details/45846647