V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
di94sh
V2EX  ›  C

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

  •  
  •   di94sh · May 9, 2017 · 4925 views
    This topic created in 3276 days ago, the information mentioned may be changed or developed.
    char *s1=“ hello ”;
    char *s2=“ xello ”;
    *s2= *s1 ;
    这样为什么不行呢。
    我希望 printf s2 输出一个 hello

    这样编译可以,运行出错。环境试了 gcc 与 vs2010
    Supplement 1  ·  May 9, 2017
    char *s=“ xello ”;
    *s= `h`;
    失败


    char s[]=“ xello ”;
    char *p=s ;
    *p=`h`;
    成功

    我想知道如何用指针直接修改第一个字符。有没有方法呢。
    54 replies    2017-06-21 14:56:17 +08:00
    xinglp
        1
    xinglp  
       May 9, 2017 via Android
    char s1[] = “ hello ”;
    char s2[] = “ xello ”;
    *s2 = *s1 ;
    di94sh
        2
    di94sh  
    OP
       May 9, 2017 via Android
    @xinglp 那样可以我是知道的,但是指针不是指向字符串头地址也就是 s2 的指针指向 x 吗。为什么这样操作不行呢
    choury
        3
    choury  
       May 9, 2017 via Android
    s1 是指针,*s1 是 h
    你应该用 s2=s1
    wevsty
        4
    wevsty  
       May 9, 2017   ❤️ 2
    @di94sh *s2 是指向 x 没错,但是“ xello ”是一个字符串常量,字符串常量在编译的时候是保存在一些不可修改的区域,所以你下改动理所当然的会失败。
    coderluan
        5
    coderluan  
       May 9, 2017
    再好好看看书,问这个问题说明你没理解指针:
    指针只是指向一块内存,并不会开辟内存,你这个只是指向了文字常量区的数据而已。
    coderluan
        6
    coderluan  
       May 9, 2017
    看了 2L,说你没理解 C 语言内存分配更准确些。
    czzhengkw
        7
    czzhengkw  
       May 9, 2017
    C 语言没有字符串赋值操作,要用 strncpy 或者 memcpy 函数……

    字符串其实是字符数组
    paradoxs
        8
    paradoxs  
       May 9, 2017
    #include <stdio.h>

    int main(int argc, const char * argv[]) {
    char *s1= "hello";
    char *s2= "xello";
    s1= s2 ;
    printf("%s\n",s1);
    return 0;
    }
    msg7086
        9
    msg7086  
       May 9, 2017
    *s2 的确是 x,但是是常量 x 不是变量 x。
    修改常量,我不记得是一定会炸还是未定义行为了。
    eoyx
        10
    eoyx  
       May 9, 2017
    你应该往更底层学,当你学了 32 位汇编之后就知道是怎么回事了
    在 c 这个层面的解释都是不准确的,什么狗屁常量区,不懂装懂,根本就没有这个概念

    x86 内存管理->保护模式->分段保护模式->数据段描述符->存储段可写位
    jinhan13789991
        11
    jinhan13789991  
       May 9, 2017
    java 程序员瑟瑟发抖
    buf1024
        12
    buf1024  
       May 9, 2017
    @wevsty 正解。
    jiankangxin
        13
    jiankangxin  
       May 9, 2017
    @jinhan13789991 哈哈哈
    drlalll
        14
    drlalll  
       May 9, 2017   ❤️ 1
    https://www.drlalll.com/2017/03/01/cyu-yan-bian-cheng-zhi-zhen-wei-shi-yao-bu-neng-gei-zi-fu-chuan-fu-zhi-xiang-jie/
    正好我前段时间研究了下,主要来说还是常量变量的原因。
    drlalll
        15
    drlalll  
       May 9, 2017
    @drlalll s1=s2 是可以的,主要是*s1 和*s2 的值都是常量
    bombless
        16
    bombless  
       May 9, 2017
    说 strcpy 或者 memcpy 没看完问题吧……
    这个其实是因为在常见的设计中匿名数组位于一个不可写的位置。
    在操作系统中这些位置在内存不足的时候可以丢掉,需要时再从新读出来,甚至不用占据虚拟内存(顺便说下这套机制称为是文件内存映射,Linux 和 Windows 都用的这样的机制来执行程序)
    drlalll
        17
    drlalll  
       May 9, 2017
    @drlalll 有时候对指针不明白可以把它的内存地址打印出来做做参考
    bombless
        18
    bombless  
       May 9, 2017   ❤️ 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
    pright
        19
    pright  
       May 9, 2017   ❤️ 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
    eoyx
        20
    eoyx  
       May 9, 2017   ❤️ 1
    @pright 傻逼不懂就谦虚点,别像个畜生一样愚昧无知还这么张狂
    本质就是 32 位汇编对于分段管理机制的表现,18L 都给出了修改方法

    你这个弱智的傻逼没学过不代表它就不可能
    大庭广众说话前先要点逼脸,当众秀无知不能提高你家族的智力水平
    baixiangcpp
        21
    baixiangcpp  
       May 9, 2017
    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.
    bombless
        22
    bombless  
       May 9, 2017
    @greatghoul 不知道会不会往精彩的方向发展
    XDDD
        23
    XDDD  
       May 9, 2017 via iPhone   ❤️ 3
    @eoyx c 语言的问题就要用 c 语言来解释。你给的只是某种情况下底层的实现方式,和问题一点关系都没有
    另外嘴巴真脏
    fy
        24
    fy  
       May 9, 2017
    桌椅板凳,有杀气
    pright
        25
    pright  
       May 9, 2017   ❤️ 2
    @eoyx
    哟,就懂个 32 位汇编真了不起,可惜我不用 x86,你咬我啊
    lishunan246
        26
    lishunan246  
       May 9, 2017
    char *s="hello",是把一个指向 const char 数组的指针,转换成 char 指针赋给 s。
    char s[]="hello",是定义一个叫 s 的 char 数组。
    两句话区别很大。
    momo1999
        27
    momo1999  
       May 9, 2017
    楼都歪了。进入看戏模式。
    wevsty
        28
    wevsty  
       May 9, 2017   ❤️ 1
    @bombless
    说的是正确的,在不同的平台和架构下可能会有不同的实现方式,Windows 下面是做了一个映射,所以 Windows 下面可以使用 VirtualProtect 来修改属性达到修改的目的。
    @eoyx
    至于为什么那个内存空间是不可写的,在不同的架构上会有不同的处理方法,说要懂常见架构的原理固然是没什么错误的,但是不要把所有人当傻 x。技术上你没说错,但没准人家真就不用 x86 呢。
    抛开这一点,我看到了浓浓的自我优越感,也是事实。
    wevsty
        29
    wevsty  
       May 9, 2017
    最后,作为一个开发者,我觉得应该尽可能的避免使用一些依赖于平台或者特定架构的一些技巧。
    编译器怎么处理这段内存那是编译器的事情,想强行修改字符串常量这种做法就应该是要尽可能避免的,因为这是未定义的行为。
    josephshen
        30
    josephshen  
       May 9, 2017 via iPhone
    zjp
        31
    zjp  
       May 9, 2017 via Android
    讨论 C 语言的帖子都能丰富 bolck 名单…
    choury
        32
    choury  
       May 9, 2017 via Android
    @eoyx 我用的是 PowerPC,你怎么解释呢?
    jarell
        33
    jarell  
       May 9, 2017
    @zjp 还在用 block 说明你的心还不能像冰面一样平静
    Livid
        34
    Livid  
    MOD
    PRO
       May 9, 2017
    @josephshen 谢谢举报。已经处理。
    lrxiao
        35
    lrxiao  
       May 9, 2017
    ....这什么鬼..又是对 C/C++研究汇编的
    owt5008137
        36
    owt5008137  
       May 9, 2017 via Android
    因为你用 char*的时候栈上就一个指针,指向常量区的.text 段里的数据。这个段默认是只读的。你用系统 api 强行改成可写就能改了,但是这么暴力不太好不是?

    而用 char[]的时候数据在栈上
    lcsoft
        37
    lcsoft  
       May 9, 2017
    某人占点优势就喷别人不懂装懂,外加傻逼、弱智、畜生、愚昧、无知、张狂这些词教别人如何做人。可以,这很牛逼。
    Vfeather
        38
    Vfeather  
       May 9, 2017 via Android
    一楼,三楼,都没毛病
    lechain
        39
    lechain  
       May 9, 2017
    其实楼主的问题和问 为什么用 const char*p 定义的指针 p 无法通过*p 操作修改内容 p 所指向的变量一样...

    研究研究以下这两组区别:
    指针变量 和 指针所指向的变量;
    const char* p 和 char* const p;
    raiz
        40
    raiz  
       May 9, 2017
    定义出来的字符串指针 s1,s2 指向的是常量,值不可修改
    aheadlead
        41
    aheadlead  
       May 9, 2017
    @pright 赞同这个回复。
    picasso250
        42
    picasso250  
       May 9, 2017
    系统对只读内存的保护。
    一定有一些嵌入式的系统可以无错运行。(仅限 C 语言)
    pwcong
        43
    pwcong  
       May 9, 2017
    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
        44
    HHehr0ow  
       May 9, 2017   ❤️ 1
    @pwcong
    *s2 并不能完全认为是'x',1 楼就举出了一个反例。可以看 19 楼的回答。
    pwcong
        45
    pwcong  
       May 10, 2017 via Android
    @HHehr0ow 看来我 c 忘得差不多了😂
    exiahan
        46
    exiahan  
       May 10, 2017
    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
        47
    exiahan  
       May 10, 2017
    @exiahan 才看到 19 楼的说法,感觉我说偏了 T_T。。19 楼正解,按规范来,未定义那就依赖于具体编译器实现,不用才对,真要用那就是针对不同平台的 tricky 了。
    j5shi
        48
    j5shi  
       May 10, 2017 via iPhone
    @eoyx 比无知更可怕的是知道了一点但以为知道了全部…
    DiamondY
        49
    DiamondY  
       May 10, 2017
    19 楼正解,C 语言里常量不能被修改
    qian19876025
        50
    qian19876025  
       May 10, 2017
    @exiahan 如果要依赖于特殊的编译实现 那还不如直接写汇编算了 使用 C 的目的是虚拟一个层出来 方便自己同时也方便来人
    dhxsy1994
        51
    dhxsy1994  
       May 10, 2017
    看戏模式,优越感是个危险物品
    JamesMackerel
        52
    JamesMackerel  
       May 10, 2017 via iPhone
    不是很明白,连文档都翻出来了说是 ub 了,还有什么可说的呢。
    visionsmile
        53
    visionsmile  
       May 10, 2017 via Android
    C++里 string literal 是 const char*
    crazyjin
        54
    crazyjin  
       Jun 21, 2017
    当年对 c 语言的指针了如指掌,现在忘得差不多了。
    s2 和 s1 是两个指针变量,和普通的变量没区别,代表的是栈里的一块内存。
    *s2 和*s1 是 s2 和 s1 这两个变量的值,是两个内存地址,分别指向“ xello ”和“ hello ”在内存中的首地址。
    “ hello"和“ xello ”是静态数据,编译后应该是放在数据段里的静态数据。
    s2=s1 ;指的是让 s2 存储 h 的地址。
    *s2=*s1; 意思是把"xello"中的 x 替换成 h ?这样肯定是不行的,xello 是静态的数据。
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   3586 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 130ms · UTC 10:59 · PVG 18:59 · LAX 03:59 · JFK 06:59
    ♥ Do have faith in what you're doing.