一个 c 风格字符串与指针问题

2017-05-09 15:27:09 +08:00
 di94sh
char *s1=“ hello ”;
char *s2=“ xello ”;
*s2= *s1 ;
这样为什么不行呢。
我希望 printf s2 输出一个 hello

这样编译可以,运行出错。环境试了 gcc 与 vs2010
4098 次点击
所在节点    C
54 条回复
aheadlead
2017-05-09 21:29:50 +08:00
@pright 赞同这个回复。
picasso250
2017-05-09 21:34:03 +08:00
系统对只读内存的保护。
一定有一些嵌入式的系统可以无错运行。(仅限 C 语言)
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 还是个学生 ε=ε=ε=┏(゜ロ゜;)┛
HHehr0ow
2017-05-09 23:47:38 +08:00
@pwcong
*s2 并不能完全认为是'x',1 楼就举出了一个反例。可以看 19 楼的回答。
pwcong
2017-05-10 01:12:28 +08:00
@HHehr0ow 看来我 c 忘得差不多了😂
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 上,最大的改变是寻址方式和可能的对齐操作。
exiahan
2017-05-10 01:42:07 +08:00
@exiahan 才看到 19 楼的说法,感觉我说偏了 T_T。。19 楼正解,按规范来,未定义那就依赖于具体编译器实现,不用才对,真要用那就是针对不同平台的 tricky 了。
j5shi
2017-05-10 07:07:33 +08:00
@eoyx 比无知更可怕的是知道了一点但以为知道了全部…
DiamondY
2017-05-10 08:49:25 +08:00
19 楼正解,C 语言里常量不能被修改
qian19876025
2017-05-10 11:58:57 +08:00
@exiahan 如果要依赖于特殊的编译实现 那还不如直接写汇编算了 使用 C 的目的是虚拟一个层出来 方便自己同时也方便来人
dhxsy1994
2017-05-10 16:03:18 +08:00
看戏模式,优越感是个危险物品
JamesMackerel
2017-05-10 17:10:03 +08:00
不是很明白,连文档都翻出来了说是 ub 了,还有什么可说的呢。
visionsmile
2017-05-10 23:54:29 +08:00
C++里 string literal 是 const char*
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 是静态的数据。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/360139

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX