1
xinglp 2017-05-09 15:33:45 +08:00 via Android
char s1[] = “ hello ”;
char s2[] = “ xello ”; *s2 = *s1 ; |
2
di94sh OP @xinglp 那样可以我是知道的,但是指针不是指向字符串头地址也就是 s2 的指针指向 x 吗。为什么这样操作不行呢
|
3
choury 2017-05-09 15:38:02 +08:00 via Android
s1 是指针,*s1 是 h
你应该用 s2=s1 |
4
wevsty 2017-05-09 15:40:25 +08:00 2
@di94sh *s2 是指向 x 没错,但是“ xello ”是一个字符串常量,字符串常量在编译的时候是保存在一些不可修改的区域,所以你下改动理所当然的会失败。
|
5
coderluan 2017-05-09 15:41:52 +08:00
再好好看看书,问这个问题说明你没理解指针:
指针只是指向一块内存,并不会开辟内存,你这个只是指向了文字常量区的数据而已。 |
6
coderluan 2017-05-09 15:44:37 +08:00
看了 2L,说你没理解 C 语言内存分配更准确些。
|
7
czzhengkw 2017-05-09 15:48:31 +08:00
C 语言没有字符串赋值操作,要用 strncpy 或者 memcpy 函数……
字符串其实是字符数组 |
8
paradoxs 2017-05-09 15:51:40 +08:00
#include <stdio.h>
int main(int argc, const char * argv[]) { char *s1= "hello"; char *s2= "xello"; s1= s2 ; printf("%s\n",s1); return 0; } |
9
msg7086 2017-05-09 15:53:56 +08:00
*s2 的确是 x,但是是常量 x 不是变量 x。
修改常量,我不记得是一定会炸还是未定义行为了。 |
10
eoyx 2017-05-09 16:04:27 +08:00
你应该往更底层学,当你学了 32 位汇编之后就知道是怎么回事了
在 c 这个层面的解释都是不准确的,什么狗屁常量区,不懂装懂,根本就没有这个概念 x86 内存管理->保护模式->分段保护模式->数据段描述符->存储段可写位 |
11
jinhan13789991 2017-05-09 16:10:51 +08:00
java 程序员瑟瑟发抖
|
13
jiankangxin 2017-05-09 16:17:53 +08:00
@jinhan13789991 哈哈哈
|
14
drlalll 2017-05-09 16:24:12 +08:00 1
|
16
bombless 2017-05-09 16:26:20 +08:00
说 strcpy 或者 memcpy 没看完问题吧……
这个其实是因为在常见的设计中匿名数组位于一个不可写的位置。 在操作系统中这些位置在内存不足的时候可以丢掉,需要时再从新读出来,甚至不用占据虚拟内存(顺便说下这套机制称为是文件内存映射,Linux 和 Windows 都用的这样的机制来执行程序) |
18
bombless 2017-05-09 16:33:34 +08:00 1
至于说 po 主说的怎样修改的问题
在 Windows 下可以通过 VirtualProtect 把这段不可写内存重新设为可写 https://msdn.microsoft.com/en-us/library/windows/desktop/aa366898 在 Linux 下是 mprotect,http://man7.org/linux/man-pages/man2/mprotect.2.html |
19
pright 2017-05-09 16:51:34 +08:00 6
其实很简单,楼主这个试图修改指向常量字符串指针的操作在 C 语言规范里面是未定义行为,就是说你这样做了不保证会发生任何事,可能在这个平台的实现上正常工作,也可能在另个平台的实现上导致你电脑爆炸,楼上那些扯到什么 32 位汇编什么的完全就是不知所云。
The contents of the arrays are modifiable. On the other hand, the declaration char *p = "abc"; defines p with type ''pointer to char'' and initializes it to point to an object with type ''array of char'' with length 4 whose elements are initialized with a character string literal. If an attempt is made to use p to modify the contents of the array, the behavior is undefined. http://port70.net/~nsz/c/c99/n1256.html#6.7.8p32 |
20
eoyx 2017-05-09 17:02:53 +08:00 1
@pright 傻逼不懂就谦虚点,别像个畜生一样愚昧无知还这么张狂
本质就是 32 位汇编对于分段管理机制的表现,18L 都给出了修改方法 你这个弱智的傻逼没学过不代表它就不可能 大庭广众说话前先要点逼脸,当众秀无知不能提高你家族的智力水平 |
21
baixiangcpp 2017-05-09 17:05:44 +08:00
ISOIEC 9899:2011 6.4.5 第 6 条
It is unspecified whether these arrays are distinct provided their elements have the appropriate values. If the program attempts to modify such an array, the behavior is undefined. |
22
bombless 2017-05-09 17:07:29 +08:00
@greatghoul 不知道会不会往精彩的方向发展
|
23
XDDD 2017-05-09 17:10:53 +08:00 via iPhone 3
@eoyx c 语言的问题就要用 c 语言来解释。你给的只是某种情况下底层的实现方式,和问题一点关系都没有
另外嘴巴真脏 |
24
fy 2017-05-09 17:11:17 +08:00
桌椅板凳,有杀气
|
26
lishunan246 2017-05-09 17:35:54 +08:00
char *s="hello",是把一个指向 const char 数组的指针,转换成 char 指针赋给 s。
char s[]="hello",是定义一个叫 s 的 char 数组。 两句话区别很大。 |
27
shuax 2017-05-09 17:41:27 +08:00
楼都歪了。进入看戏模式。
|
28
wevsty 2017-05-09 17:58:44 +08:00 1
|
29
wevsty 2017-05-09 18:02:28 +08:00
最后,作为一个开发者,我觉得应该尽可能的避免使用一些依赖于平台或者特定架构的一些技巧。
编译器怎么处理这段内存那是编译器的事情,想强行修改字符串常量这种做法就应该是要尽可能避免的,因为这是未定义的行为。 |
30
josephshen 2017-05-09 18:14:30 +08:00 via iPhone
|
31
zjp 2017-05-09 18:37:42 +08:00 via Android
讨论 C 语言的帖子都能丰富 bolck 名单…
|
34
Livid MOD @josephshen 谢谢举报。已经处理。
|
35
lrxiao 2017-05-09 19:32:19 +08:00
....这什么鬼..又是对 C/C++研究汇编的
|
36
owt5008137 2017-05-09 19:32:31 +08:00 via Android
因为你用 char*的时候栈上就一个指针,指向常量区的.text 段里的数据。这个段默认是只读的。你用系统 api 强行改成可写就能改了,但是这么暴力不太好不是?
而用 char[]的时候数据在栈上 |
37
lcsoft 2017-05-09 20:45:15 +08:00
某人占点优势就喷别人不懂装懂,外加傻逼、弱智、畜生、愚昧、无知、张狂这些词教别人如何做人。可以,这很牛逼。
|
38
Vfeather 2017-05-09 20:53:22 +08:00 via Android
一楼,三楼,都没毛病
|
39
lechain 2017-05-09 21:04:19 +08:00
其实楼主的问题和问 为什么用 const char*p 定义的指针 p 无法通过*p 操作修改内容 p 所指向的变量一样...
研究研究以下这两组区别: 指针变量 和 指针所指向的变量; const char* p 和 char* const p; |
40
raiz 2017-05-09 21:10:52 +08:00
定义出来的字符串指针 s1,s2 指向的是常量,值不可修改
|
42
picasso250 2017-05-09 21:34:03 +08:00
系统对只读内存的保护。
一定有一些嵌入式的系统可以无错运行。(仅限 C 语言) |
43
pwcong 2017-05-09 22:57:52 +08:00
char *s1=“ hello ”;
char *s2=“ xello ”; *s2= *s1 ; s1 是 char 型指针变量,值是 "h" 的内存地址 s2 是 char 型指针变量,值是 "x" 的内存地址 所以 *s1 为 char 值 "h",*s2 为 char 值 "x" 不信可以 printf("%c",*s1) 输出的是 "h" 这样的话 *s2 = *s1 翻译过来就是 "x" = "h",这跟 1=2 是一个道理语法错误。 所以你要想 s2 输出 "hello",直接 s2=s1 即可,将 s1 内存地址赋给 s2 说错了的话大佬轻喷,lz 还是个学生 ε=ε=ε=┏(゜ロ゜;)┛ |
46
exiahan 2017-05-10 01:20:03 +08:00
Linux 的话 char *s1="hello" 和 char *s2= "xllo"指向的.rodata,.rodata 段运行时没有写权限,而你尝试 *s1 = *s2 是在尝试去写 s1 指向的.rodata 的一个字符串第一个字节,运行时肯定会报错。
这里牵扯到一般编译链接后生成的可执行文件各段的读写执行权限问题,linux 上.rodata 段现在一般装载到内存后没有写权限,运行时的 stack 具有读写权限(如果不开 NX 可能还有执行权限。) 楼主可以反编译看看定义成 char *s1 = "hello"和 char s1[] = "hello"的区别,32 位机器的话,第一个 s1 在其所属函数的 stack 上只会是一个指针,其值指向.rodata 段的一个字符串"hello"的第一个字节;而第二个在运行时会在其所属函数的 stack 上开辟至少 strlen("hello") + 1 的空间(之所以说至少是因为有时候编译器有做对齐优化),所以 s1[]的 s1 在运行时是指向 stack 上存储 ‘ h ’, ‘ e ’, ‘ l ’, ‘ l ’, ‘ o ’, ‘\0 ‘的一块 stack 上内存的最低地址,也就是存放'h'的地方。 至于下面这段: char s[]=“ xello ”; char *p=s ; *p=`h`; *p = 'h'可以成功是因为前一个操作 char *p=s 会导致 p 在 stack 上占有 sizeof(char *)个字节,所以*p='h'会让计算机把'h'放到 p 在 stack 上占有的最低字节的地方。 PS: 上面君假设你的代码至少位于 main 或者一个函数内,如果是一个全局变量,那操作是在.data 段上而不是在 stack 上,最大的改变是寻址方式和可能的对齐操作。 |
47
exiahan 2017-05-10 01:42:07 +08:00
@exiahan 才看到 19 楼的说法,感觉我说偏了 T_T。。19 楼正解,按规范来,未定义那就依赖于具体编译器实现,不用才对,真要用那就是针对不同平台的 tricky 了。
|
49
DiamondY 2017-05-10 08:49:25 +08:00
19 楼正解,C 语言里常量不能被修改
|
50
qian19876025 2017-05-10 11:58:57 +08:00
@exiahan 如果要依赖于特殊的编译实现 那还不如直接写汇编算了 使用 C 的目的是虚拟一个层出来 方便自己同时也方便来人
|
51
dhxsy1994 2017-05-10 16:03:18 +08:00
看戏模式,优越感是个危险物品
|
52
JamesMackerel 2017-05-10 17:10:03 +08:00 via iPhone
不是很明白,连文档都翻出来了说是 ub 了,还有什么可说的呢。
|
53
visionsmile 2017-05-10 23:54:29 +08:00 via Android
C++里 string literal 是 const char*
|
54
crazyjin 2017-06-21 14:56:17 +08:00
当年对 c 语言的指针了如指掌,现在忘得差不多了。
s2 和 s1 是两个指针变量,和普通的变量没区别,代表的是栈里的一块内存。 *s2 和*s1 是 s2 和 s1 这两个变量的值,是两个内存地址,分别指向“ xello ”和“ hello ”在内存中的首地址。 “ hello"和“ xello ”是静态数据,编译后应该是放在数据段里的静态数据。 s2=s1 ;指的是让 s2 存储 h 的地址。 *s2=*s1; 意思是把"xello"中的 x 替换成 h ?这样肯定是不行的,xello 是静态的数据。 |