V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
amiwrong123
V2EX  ›  程序员

strcpy 中,遇到内存重叠,为什么结果是这样?

  •  
  •   amiwrong123 · 2022-01-02 01:31:14 +08:00 · 1907 次点击
    这是一个创建于 817 天前的主题,其中的信息可能已经有所发展或是发生改变。
    #include <stdio.h>
    #include <string.h>
    
    int main()
    {
    	char s[10] = "hello"; 
    	printf("%s\n", strcpy(s, s + 1));
    	printf("%s\n", s);
    
    	return 0;
    }
    

    打印结果是:

    ello
    ello
    

    这个我理解,把ello\0往前复制了。

    #include <stdio.h>
    #include <string.h>
    
    int main()
    {
    	char s[10] = "hello"; 
    	printf("%s\n", strcpy(s + 1, s));
    	printf("%s\n", s);
    
    	return 0;
    }
    

    打印结果却是:

    helll
    hhelll
    

    这个hhelll我就不理解了。不应该是hhello

    本来想试一下 strcpy 的行为,自己写个实现。但现在它的行为我不理解了。

    vs2019 试的,需要按照 https://blog.csdn.net/oguro/article/details/52685662 添加宏才能运行。

    第 1 条附言  ·  2022-01-02 10:16:28 +08:00
    谢谢各位,理解了。我本意只是根据这些基本函数的行为,自己写一遍实现练习一下。

    https://paste.ubuntu.com/p/H2Fc9gKkSK/
    上面写了 考虑内存重叠时,我写的 strcpy 函数,欢迎各位指正
    第 2 条附言  ·  2022-01-02 10:34:04 +08:00
    说错了,不是指正,应该是交流。毕竟这是 未定义行为,我只是按照我自己想法写了一个
    10 条回复    2022-01-02 15:51:06 +08:00
    fishCatcher
        1
    fishCatcher  
       2022-01-02 01:44:02 +08:00 via iPhone
    这是个未定义行为,所以你怎么实现都可以,
    yulon
        2
    yulon  
       2022-01-02 01:51:11 +08:00   ❤️ 3
    你要问这种有冲突的问题,当然只能得到有冲突的结果。

    strcpy 作为一个拷贝函数,自然是不会分配额外内存,不然直接给你返回一个新字符串不就完了。

    那么在有限的内存里,它有可能用算法解决内存重叠,也有可能解决不了。

    猜测你是 32 位程序,内部实现是以机器字为单位来拷贝,也就是 4 个字节,拷贝一次之后,第 5 个字节已经变了,自然就多出来一个 l 。

    建议别浪费时间在这种问题上,而且写出这种程序非常恐怖。
    wudicgi
        3
    wudicgi  
       2022-01-02 02:30:06 +08:00   ❤️ 1
    yulon 已经把需要说的都说了,极有可能就是按 4 字节处理出来的结果,因为 s 在栈里分配,地址会是 4 字节对齐的
    如果有兴趣可以试试 s[10] = "_hello" 时 strcpy(s + 2, s + 1) 的情况,结果应该不一样
    不过其实深究这个意义不大,首先考虑把 strcpy() 换成 strncpy(), strncpy_s() 之类的函数,其次必要时自己加额外判断

    另外,看到你想自己写个 strcpy() 的实现,
    如果是 for 循环简单的逐字节复制,怕是这时 strcpy() 会直接运行到访问地址无读写权限的情况,
    把从 s 起始位置开始的所有的能读写的字节都改成 'h'

    标准库的实现会不一样,有的库可能先 strlen() 算好长度再调用 memcpy(), 这样不会有一直处理下去的问题,
    但具体结果如何得看具体代码了,一般会有不少优化的,毕竟 memcpy() 很常用
    wevsty
        4
    wevsty  
       2022-01-02 02:58:59 +08:00
    不要纠结这些标准库函数内部怎么实现的,看你换个编译器,甚至换个版本就会变了。
    dangyuluo
        5
    dangyuluo  
       2022-01-02 03:21:27 +08:00
    > The behavior is undefined if the strings overlap.
    2i2Re2PLMaDnghL
        6
    2i2Re2PLMaDnghL  
       2022-01-02 04:49:05 +08:00   ❤️ 3
    未定义行为,电脑直接爆炸也是逻辑上允许的。
    直接按照标准去写就行,标准大于约定。
    thedrwu
        7
    thedrwu  
       2022-01-02 06:12:41 +08:00 via Android
    未定义的。你的 code reviewer 会把这种情况打回来重写。
    kokutou
        8
    kokutou  
       2022-01-02 09:35:02 +08:00 via Android
    不要研究未定义行为。。。
    除非你是搞编译器之类底层的。。。
    codehz
        9
    codehz  
       2022-01-02 10:04:54 +08:00 via Android   ❤️ 1
    (这一系列函数中,只有 memmove 是保证重叠的时候正确的)
    agagega
        10
    agagega  
       2022-01-02 15:51:06 +08:00 via iPhone
    只有 memmove 可以重叠
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   5305 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 40ms · UTC 05:59 · PVG 13:59 · LAX 22:59 · JFK 01:59
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.