user-0 提问: 关于const修饰和指针

回复:
user: user-1
content: 这段程序使用GCC编译时候就已经告知你了  warning: initialization discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]      int *p=i;   编译你的程序，反汇编得到main代码如下，个人PC是Fedora 22, Linux4.2内核，64位80686机器，GCC版本version 5.1.1 20150618 (Red Hat 5.1.1-4) (GCC)  123 0000000000400536 <main>: 124   400536:   55                      push   %rbp 125   400537:   48 89 e5                mov    %rsp,%rbp 126   40053a:   48 83 ec 20             sub    $0x20,%rsp 127   40053e:   c7 45 e0 01 00 00 00    movl   $0x1,-0x20(%rbp) 128   400545:   c7 45 e4 02 00 00 00    movl   $0x2,-0x1c(%rbp) 129   40054c:   c7 45 e8 03 00 00 00    movl   $0x3,-0x18(%rbp) 130   400553:   c7 45 ec 04 00 00 00    movl   $0x4,-0x14(%rbp) 131   40055a:   c7 45 f0 05 00 00 00    movl   $0x5,-0x10(%rbp) 132   400561:   48 8d 45 e0             lea    -0x20(%rbp),%rax 133   400565:   48 89 45 f8             mov    %rax,-0x8(%rbp) 134   400569:   48 8b 45 f8             mov    -0x8(%rbp),%rax 135   40056d:   c7 00 64 00 00 00       movl   $0x64,(%rax) 136   400573:   8b 45 e0                mov    -0x20(%rbp),%eax 137   400576:   89 c6                   mov    %eax,%esi 138   400578:   bf 20 06 40 00          mov    $0x400620,%edi 139   40057d:   b8 00 00 00 00          mov    $0x0,%eax 140   400582:   e8 89 fe ff ff          callq  400410 <printf@plt> 141   400587:   b8 00 00 00 00          mov    $0x0,%eax 142   40058c:   c9                      leaveq 143   40058d:   c3                      retq 144   40058e:   66 90                   xchg   %ax,%ax   注意这几句  124   400536:   55                      push   %rbp 125   400537:   48 89 e5                mov    %rsp,%rbp 126   40053a:   48 83 ec 20             sub    $0x20,%rsp 127   40053e:   c7 45 e0 01 00 00 00    movl   $0x1,-0x20(%rbp) 128   400545:   c7 45 e4 02 00 00 00    movl   $0x2,-0x1c(%rbp) 129   40054c:   c7 45 e8 03 00 00 00    movl   $0x3,-0x18(%rbp) 130   400553:   c7 45 ec 04 00 00 00    movl   $0x4,-0x14(%rbp) 131   40055a:   c7 45 f0 05 00 00 00    movl   $0x5,-0x10(%rbp)  这就是数组的初始化了!显然GCC并没有因为const原因而把它放到只读数据区，但是不排除其他编译器会这么做。以此为前提，那么接下来你修改它当然也是没有问题的！毕竟栈上数据可读写！

user: user-2
content: 在C中，用const修饰的并不代表是真的常量，用#define 的才是。  用const修饰只是告诉编译器：该变量是readonly的，你不打算修改，让编译器帮你检查一下，防止你无意中犯了错误。  const修饰的指针既可以指向真的常量，也可以指向非常量。  指向真常量的例子： const char * s = "Hello World";   s[0] = 'h'; //会导致程序崩溃； 由于“Hello World"字符串是放在.data section，是个真的常量，此时常量指针指向的数据是有内存保护的，不能修改的，如果试图修改就会让程序崩溃。  指向语义上的常量的例子： const int a[5] = {0}; a[0] = 1; // 此时编译器会报错，告诉你不能修改  const int a[5] = {0}; int * p = a; p[0] = 1; // 没有问题，  因为int a[5]是在栈上分配内存，这块内存并没有受保护，可以修改。在前面加上const修饰，并不能把它放到.data段，只是告诉编译器，它应该为只读。    #include <stdio.h>  void modify(const char * str) { 	char * p = (char *)str; 	*p = 'h'; 	return; }  int main(int argc, char ** argv) { 	const char * s1 = "Hello World"; 	const char s2[] = "Hello World";  	const char * p = s2; 	modify(p); // 可以，因为s2指向的内存是在栈上分配的 	printf("%s\n", p);  	p = s1; 	modify(p); // 程序会崩溃（Segmentation fault），因为s1指向的是.data段的常量  	printf("%s\n", p); // 不会执行，因为程序已经崩溃了  	return 0; }

user: user-3
content: 代码里倒是不会出现意想不到的结果，毕竟只是在改变第一个元素的值。书中说的“非法”估计更多的是指“语义”上非法，即“既然定义了一个const数组，后面的代码就应该由程序员保证不会去修改。” 注意：如果把代码改为int *p=i+10;*p=20;这可就是完全的“不可知”行为了，因为已经访问了越界的地址。

user: user-0
content: 引用 1 楼 user-5的回复:这里的非法，意思是什么都可能发生。  你可能得到1，可能得到100，可能程序崩掉，可能 ........... 。。。。。是这个意思？

user: user-0
content: 引用 5 楼 一根烂笔头的回复:这段程序使用GCC编译时候就已经告知你了  warning: initialization discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]      int *p=i;   编译你的程序，反汇编得到main代码如下，个人PC是Fedora 22, Linux4.2内核，64位80686机器，GCC版本version 5.1.1 20150618 (Red Hat 5.1.1-4) (GCC)  123 0000000000400536 <main>: 124   400536:   55                      push   %rbp 125   400537:   48 89 e5                mov    %rsp,%rbp 126   40053a:   48 83 ec 20             sub    $0x20,%rsp 127   40053e:   c7 45 e0 01 00 00 00    movl   $0x1,-0x20(%rbp) 128   400545:   c7 45 e4 02 00 00 00    movl   $0x2,-0x1c(%rbp) 129   40054c:   c7 45 e8 03 00 00 00    movl   $0x3,-0x18(%rbp) 130   400553:   c7 45 ec 04 00 00 00    movl   $0x4,-0x14(%rbp) 131   40055a:   c7 45 f0 05 00 00 00    movl   $0x5,-0x10(%rbp) 132   400561:   48 8d 45 e0             lea    -0x20(%rbp),%rax 133   400565:   48 89 45 f8             mov    %rax,-0x8(%rbp) 134   400569:   48 8b 45 f8             mov    -0x8(%rbp),%rax 135   40056d:   c7 00 64 00 00 00       movl   $0x64,(%rax) 136   400573:   8b 45 e0                mov    -0x20(%rbp),%eax 137   400576:   89 c6                   mov    %eax,%esi 138   400578:   bf 20 06 40 00          mov    $0x400620,%edi 139   40057d:   b8 00 00 00 00          mov    $0x0,%eax 140   400582:   e8 89 fe ff ff          callq  400410 <printf@plt> 141   400587:   b8 00 00 00 00          mov    $0x0,%eax 142   40058c:   c9                      leaveq 143   40058d:   c3                      retq 144   40058e:   66 90                   xchg   %ax,%ax   注意这几句  124   400536:   55                      push   %rbp 125   400537:   48 89 e5                mov    %rsp,%rbp 126   40053a:   48 83 ec 20             sub    $0x20,%rsp 127   40053e:   c7 45 e0 01 00 00 00    movl   $0x1,-0x20(%rbp) 128   400545:   c7 45 e4 02 00 00 00    movl   $0x2,-0x1c(%rbp) 129   40054c:   c7 45 e8 03 00 00 00    movl   $0x3,-0x18(%rbp) 130   400553:   c7 45 ec 04 00 00 00    movl   $0x4,-0x14(%rbp) 131   40055a:   c7 45 f0 05 00 00 00    movl   $0x5,-0x10(%rbp)  这就是数组的初始化了!显然GCC并没有因为const原因而把它放到只读数据区，但是不排除其他编译器会这么做。以此为前提，那么接下来你修改它当然也是没有问题的！毕竟栈上数据可读写！ 谢谢你的热心解答!!!

user: user-0
content: 引用 4 楼 chehw_1的回复:在C中，用const修饰的并不代表是真的常量，用#define 的才是。  用const修饰只是告诉编译器：该变量是readonly的，你不打算修改，让编译器帮你检查一下，防止你无意中犯了错误。  const修饰的指针既可以指向真的常量，也可以指向非常量。  指向真常量的例子： const char * s = "Hello World";   s[0] = 'h'; //会导致程序崩溃； 由于“Hello World"字符串是放在.data section，是个真的常量，此时常量指针指向的数据是有内存保护的，不能修改的，如果试图修改就会让程序崩溃。  指向语义上的常量的例子： const int a[5] = {0}; a[0] = 1; // 此时编译器会报错，告诉你不能修改  const int a[5] = {0}; int * p = a; p[0] = 1; // 没有问题，  因为int a[5]是在栈上分配内存，这块内存并没有受保护，可以修改。在前面加上const修饰，并不能把它放到.data段，只是告诉编译器，它应该为只读。    #include <stdio.h>  void modify(const char * str) { 	char * p = (char *)str; 	*p = 'h'; 	return; }  int main(int argc, char ** argv) { 	const char * s1 = "Hello World"; 	const char s2[] = "Hello World";  	const char * p = s2; 	modify(p); // 可以，因为s2指向的内存是在栈上分配的 	printf("%s\n", p);  	p = s1; 	modify(p); // 程序会崩溃（Segmentation fault），因为s1指向的是.data段的常量  	printf("%s\n", p); // 不会执行，因为程序已经崩溃了  	return 0; }   谢谢你的热心解答!!!

user: user-4
content: VS上int *p=i;这句不能通过编译：“初始化”: 无法从“const int *”转换为“int *”

user: user-5
content: 这里的非法，意思是什么都可能发生。  你可能得到1，可能得到100，可能程序崩掉，可能 ...........