调用 strcpy 的结果与预期不同

Posted

技术标签:

【中文标题】调用 strcpy 的结果与预期不同【英文标题】:Result of calling strcpy is different than expected 【发布时间】:2015-02-17 05:56:48 【问题描述】:
#include <stdio.h>
#include <string.h>

int main()

   char src[]="123456";
   strcpy(src, &src[1]);
   printf("Final copied string : %s\n", src);

当我使用 Visual Studio 6 编译器时,它给了我预期的答案“23456”。

为什么这个程序在使用 gcc 4.7.2 编译时会打印“23556”?

【问题讨论】:

未定义的行为,使用 memmov() 代替 你是怎么知道它在哪里重叠的? &Src[1]="23456" 对!?!那么重叠在哪里? @PeterMiehle 是的,memmov 加上一个额外的e src 衰减为指向数组src 的第一个元素的指针。 &amp;src[1] 是指向数组第二个元素的指针。由于源字符串的长度不是 0,它们显然是重叠的。 这意味着我必须从另一个字符串“char dest[]="123456";”复制并执行“strcpy(src, &amp;dest[1]); 【参考方案1】:

strcpy(src, &amp;src[1]); 是未定义的行为:

C11 §7.24.2.3 strcpy 函数

strcpy函数复制s2指向的字符串(包括终止的null 字符) 到s1 指向的数组中。 如果复制发生在对象之间 重叠,行为未定义。

顺便说一句,memcpy 是相似的(但不是memmove)。见C FAQ: What's the difference between memcpy and memmove

【讨论】:

嗯...似乎类似于memcpy vs memmove 问题。 您在哪里可以找到这些信息?你怎么这么快就找到了?或者你写过自己的书吗:D? @Rizier123 我在附近有一份 C 标准草案的副本,如果这就是你要问的 :)。见Where do I find the current C or C++ standard documents?。 @Rizier123:在我的系统上,它在 glibc 手册页 (man 3 strcpy) 中有描述:字符串可能不重叠。 @Blood-HaZaRd:这是未定义的行为,实现可以为所欲为。【参考方案2】:

这是未定义的行为。请改用memmove 函数。 memmove 旨在允许源缓冲区和目标缓冲区重叠。

memmove(src, &src[1], strlen(&src[1]) + 1) ;  // + 1 for copying the terminating zero

【讨论】:

是的,内存重叠。 为了清晰起见,您可以使用src+1,而不是看起来很尴尬的&amp;src[1] @Jongware 你是对的,但我故意把它和原来的问题一样。【参考方案3】:

来自 ISO/IEC 9899:TC3 (c99)

7.21.2.3 strcpy 函数

概要

1

#include &lt;string.h&gt;

char *strncpy(char * restrict s1, const char * restrict s2, size_t n);

说明

2 strcpy 函数复制 s2 指向的字符串(包括终止的 null 字符) 到 s1 指向的数组中。 如果复制发生在对象之间 重叠,行为未定义。

所以你所做的只是未定义的行为;)

您还可以查看附件 J.2

说明未定义行为的情况并说明如何防止:

在以下情况下行为未定义:

[...]

- 尝试使用库将对象复制到重叠对象 功能,除非明确允许(例如,memmove)(第 7 条)。

【讨论】:

以上是关于调用 strcpy 的结果与预期不同的主要内容,如果未能解决你的问题,请参考以下文章

GLM 旋转矩阵与预期结果不同

我得到的结果与我对以下任务的预期不同

strcpy&memcpy理解

SQLite Instr 提供的值与预期不同

OpenMP 并行代码与串行代码的输出不同

码海拾遗:strcpy()strncpy()和strcpy_s()区别