深入理解const char*p,char const*p,char *const p,const char **p,char const**p,char *const*p,char**const p
来源:互联网 发布:乐秀rx5数据 编辑:程序博客网 时间:2024/06/10 13:21
一、可能的组合:
二、理解助记法宝:
也就是说上面7种情况中,(1)和(2)等价。同理,(4)和(5)等价。在(6)中,由于没有const*运算,const实际上修饰的是前面的char*,但不能在定义时转换写成 const(char *)*p,因为在定义是"()"是表示函数。
三、深入理解7种组合
可以总结以上3点为:char *p中的指针p通常是”万能”的工作指针
另外,要明白“每块内存空间都可有名字;都有每块内存空间内容皆可变(除非有所限)
四、关于char **p、const char **p的类型相容性问题
五、其他
六、问题探讨(由于博文后的留言有字符数目限制,将回复移到这里)
问题1(见博文后留言):讲解非常好,不过有个问题想探讨下:例如: const char wang[]={"wang"}; char *p; p=wang; 是错误的。所以char *p中的P不能指向常变量。(1)需要补充纠正。
回复:你好!谢谢指正!我在ubuntu 10.04(gcc 4.4.3)下做了如下测试:
//test_const.c
#include<stdio.h>
int main()
{
const char wang[]={"wang"};
char *p;
p=wang;
p[2]='c';
printf("p is %s\n",p);
return 0;
}
编译:
gcc -o test_const test_const.c
输出如下:
test_const.c: In function ‘main’:
test_const.c:17: warning: assignment discards qualifiers from pointer target type
执行:
./test_const
p is wacg
结论:根据本博文中第四点--相容性问题,将const型的wang赋值给p是不合法的。但编译器对其的处理只是警告,因此执行时通过p修改了只读区域的数据。这应该是该编译器处理不严所致后果,不知你用的什么编译器?
问题2回答 http://www.linuxsir.org/bbs/showthread.php?t=239058 提到的问题
在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语言里只是摆设一个?
回复:
(1)首先,以上代码编译时会出错warning: initialization discards qualifiers from pointer target type,
因为char *s2 = s1和问题1提到的一样,不符合相容规则。
(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出的变量=>在可执行文件中不占空间,在该二进制文件作为进程在内存中运行时才为它分配堆空间。
问题3:(待进一步分析)验证博文中三(3)提到的是否为s分配空间,初步分析结果为:不分配!文中的s只是s〔0〕的地址的代号而已。
#include<stdio.h>
int main() {
int a=3;
char s1[] = "test";
int b=4;
char s2[] ="test2";
printf("the address of a is %u\n",&a);
printf("s1 is %u\n",s1);
printf("the address of s1 is %u\n",&s1);
printf("the address of b is %u\n",&b);
printf("s2 is %u\n",s2);
printf("the address of s2 is %u\n",&s2);
}
输出结果:
the address of a is 3213037836
s1 is 3213037827
the address of s1 is 3213037827
the address of b is 3213037832
s2 is 3213037821
the address of s2 is 3213037821
由结果可以看出,编译器做了些优化。
七、其他相关经典文章转载
王海宁,华清远见嵌入式学院讲师,对const关键字的理解
http://www.embedu.org/Column/Column311.htm
目前在进行C语言补习时,发现很多的同学对于const这个关键字的理解存在很大的误解。现在总结下对这个关键字理解上的误区,希望在以后的编程中,能够灵活使用const这个关键字。
1、 const修饰的变量是常量还是变量
对于这个问题,很多同学认为const修饰的变量是不能改变,结果就误认为该变量变成了常量。那么对于const修饰的变量该如何理解那?
下面我们来看一个例子:
int main
{
char buf[4];
const int a = 0;
a = 10;
}
这个比较容易理解,编译器直接报错,原因在于“a = 10;”这句话,对const修饰的变量,后面进行赋值操作。这好像说明了const修饰的变量是不能被修改的,那究竟是不是那,那么下面我们把这个例子修改下:
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,已经被我们程序修改了。
那综合这两个例子,我们来分析下,对于第二例子,修改的原因是buf[4]的赋值操作,我们知道buf[4]这个变量已经造成了buf这个数组变量的越界访问。buf数组的成员本身只有0,1,2,3,那么buf[4]访问的是谁那,根据局部变量的地址分配,可以知道buf[4]的地址和int a的地址是一样,那么buf[4]实际上就是访问了const int a;那么对buf[4]的修改,自然也修改了const int a的空间,这也是为什么我们在最后打印a的值的时候看到了97这个结果。
那么我们现在可以知道了,const修饰的变量是不具备不允许修改的特性的,那么对于第一个例子的现象我们又如何解释那。
第一个例子,错误是在程序编译的时候给出的,注意这里,这个时候并没有生成可执行文件,说明const修饰的变量可否修改是由编译器来帮我们保护了。而第二个例子里,变量的修改是在可执行程序执行的时候修改的,说明a还是一个变量。
综上所述,我们可以得出一个结论,那就是const修饰的变量,其实质是告诉程序员或编译器该变量为只读,如果程序员在程序中显示的修改一个只读变量,编译器会毫不留情的给出一个error。而对于由于像数组溢出,隐式修改等程序不规范书写造成的运行过程中的修改,编译器是无能为力的,也说明const修饰的变量仍然是具备变量属性的。
2、被const修饰的变量,会被操作系统保护,防止修改
如果对于第一个问题,有了理解的话,那么这个问题,就非常容易知道答案了。Const修饰的变量是不会被操作系统保护的。
其原因是操作系统只保护常量,而不会保护变量的读写。那么什么是常量?比如“hello world”这个字符串就是被称为字符串常量。
对于这个问题的另一种证明方法,可以看下面这个程序:
int main
{
const int a;
char *buf = “hello world”;
printf(“the &a is %p, the buf is %p\n”,&a, buf);
}
可以发现buf保存的地址是在0x08048000这个地址附近的,而a的地址是在0xbf000000这个地址附近的,而0x08048000附近的地址在我们linux操作系统上是代码段。这也说明了常量和变量是存放在不同区域的,自然操作系统是会保护常量的。
如果我们知道这个道理后,再看下面的题目:
int main
{
char *buf = “hello”;
buf[0] = ‘a’;
printf(“the buf is %s\n”,buf);
}
我们可以思考下,这个程序的运行结果会是什么呢?
文章转自:http://www.cnblogs.com/rushuizhijing/archive/2011/08/26/2154737.html
- const char*p, char* const p, const char* const p
- char *p、const char *p
- 深入理解const char*p,char const*p,char *const p,const char **p,char const**p,char *const*p,char**const p
- 深入理解const char*p,char const*p,char *const p,const char **p,char const**p,char *const*p,char**const p
- 深入理解const char*p,char const*p,char *const p,const char **p,char const**p,char *const*p,char**const p
- 深入理解const char*p,char const*p,char *const p,const char **p,char const**p,char *const*p,char**const p
- 深入理解const char*p,char const*p,char *const p,const char **p,char const**p,char *const*p,char**const p
- 深入理解const char*p,char const*p,char *const p,const char **p,char const**p,char *const*p,char**const p
- 深入理解const char*p,char const*p,char *const p,const char **p,char const**p,char *const*p,char**const p
- 深入理解const char*p,char const*p,char *const p,const char **p,char const**p,char *const*p,char**const p
- 深入理解const char*p,char const*p,char *const p,const char **p,char const**p,char *const*p,char**const p
- 深入理解const char*p,char const*p,char *const p,const char **p,char const**p,char *const*p,char**const p
- 深入理解const char*p,char const*p,char *const p,const char **p,char const**p,char *const*p,char**const p
- 深入理解const char*p,char const*p,char *const p,const char **p,char const**p,char *const*p,char**const p
- 深入理解const char*p,char const*p,char *const p,const char **p,char const**p,char *const*p,char**const p
- 深入理解const char*p,char const*p,char *const p,const char **p,char const**p,char *const*p,char**const p
- 深入理解const char*p,char const*p,char *const p,const char **p,char const**p,char *const*p,char**const p
- 深入理解const char*p,char const*p,char *const p,const char **p,char const**p,char *const*p,char**const p
- 台湾-海洋过程分析研究室--相關研究資源連結
- 【枚举】教主的花园
- 学习ldd3--工作队列(第七章)
- 重庆1辆石油液化气罐车坠崖 已排除爆炸可能-石油液化气罐车-坠崖-爆炸可能
- 学习ldd3--计时、延迟操作(第七章)
- 深入理解const char*p,char const*p,char *const p,const char **p,char const**p,char *const*p,char**const p
- 单元测试101:你测试你的javascript吗?
- Mediator 模式
- 星星
- 学习ldd3--内核定时器(第七章)
- 编译器与解释器的区别
- 黑马程序员__高新技术2
- Firefox 16隐私漏洞的攻击方法
- C++ 银行家算法