为什么使用大小为1的目标字符串时strcpy省略了源字符串中的第一个字符?

时间:2019-05-05 16:59:03

标签: c string strcpy

在C语言中,strcpy函数用于将源复制到目标字符串中。

但是,当我使用大小为1的目标char数组时,strcpy可以将源正确复制到目标中。但这也会更改源char数组。我想了解它在C语言中的工作原理。

我已经进行了一些有关如何在程序中正确使用strcpy的研究,但所有这些程序使用的目标大小都大于1。我在程序中使用的目标大小等于1。这就是问题所在。 / p>

char a[] = "String ABC";
char b[1];

strcpy(b, a);
int i;
// printf("%c\n", *(&(a[0])-1));

printf("%s\n",a);
printf("%s\n",b);

我希望输出为

String ABC
String ABC

但是我得到的输出是

tring ABC
String ABC

5 个答案:

答案 0 :(得分:3)

C不执行边界检查,并且可以让您超出缓冲区的边界。实际的行为是不确定的,但是在您的情况下,内存排列可能是这样的:

 b a
|-|S|t|r|i|n|g|A|B|C|\0|

strcpy()

之后
 b a
|S|t|r|i|n|g|A|B|C|\0|\0|

因此b包含'S'且没有nul终止符(因为没有空间),因此在打印时,它会遇到a中具有"tringABC"的内容。 / p>

根据编译器如何对相邻变量进行排序和对齐以及实现如何与重叠的strcpy()源和目标(也未定义)一起工作,可能会产生其他结果。

答案 1 :(得分:3)

问题是您将更长的字符串复制到1个字节的字符串中,导致行为不确定。

如果运行此程序:

#include<stdio.h>
#include<string.h>

int main(int argc, char *argv[])
{
    char a[] = "String ABC";
    char b[1];
    printf("%p\n", &a);
    printf("%p\n", &b);

    strcpy(b, a);
    int i;
    printf("%c\n", *(&(a[0])-1));
    printf("%c\n", a[0]);
    printf("%s\n",a);
    printf("%s\n",b);
    printf("%p\n", &a);
    printf("%p\n", &b);
}

您看到ba具有连续的地址,并且b存储在a之前的内存地址中。 strcpy最有可能将字符串复制到b,但是由于b没有分配来存储这么长的字符串,因此它将覆盖下一个连续的存储单元,似乎是a

让我用||表示存储字符的存储单元。假设-b-是存储一个char长字符串的单元格。 复制之前,您有

|-b-|---a memory allocation--|
|-b-|S|t|r|i|n|g| |A|B|C|D|\n|

现在将a复制到b中:第二个单元格是a中的一个单元格,其中现在包含t

  |--a memory allocation-|
|S|t|r|i|n|g| |A|B|C|D|\n|

这是我想的事情。但是请记住,将较长的字符串复制到较短的字符串将导致不确定的行为。

答案 2 :(得分:1)

您不能将a复制到b中,因为b中没有足够的空间。 strcpy函数将简单地写入数组末尾,这是未定义的行为。这意味着该程序可以以任何无法预测的方式运行(有时,如果您不走运,则意味着它可以按预期运行)。

换句话说:当您使用strcpy时,必须 确保目标缓冲区足够大,包括空终止符。在此特定示例中,这意味着b的长度至少必须为11个元素(字符串为10个,空终止符为1个)。

答案 3 :(得分:1)

好笑,我的编译器的行为有所不同:编译时会发出警告:

% gcc strcpy.c -O3
In file included from /usr/include/string.h:494:0,
                 from strcpy.c:1:
In function ‘strcpy’,
    inlined from ‘main’ at strcpy.c:8:5:
/usr/include/x86_64-linux-gnu/bits/string_fortified.h:90:10: warning:
         ‘__builtin___memcpy_chk’ writing 11 bytes into a region of size 1 overflows the
         destination [-Wstringop-overflow=]
   return __builtin___strcpy_chk (__dest, __src, __bos (__dest));
          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

当我运行程序时,它会中止:

% ./a.out                       
*** buffer overflow detected ***: ./a.out terminated

答案 4 :(得分:0)

正如@Acorn在回答中提到的那样,您看到的行为是未定义的行为,这意味着编译器可以自由生成任意代码。

但是,如果您想调查这里发生的事情(纯粹出于好奇),它可以帮助打印出阵列的地址。

#include <stdio.h>
#include <string.h>

int main(){
    char a[] = "String ABC";
    char b[1];

    strcpy(b, a);
    int i;
    // printf("%c\n", *(&(a[0])-1));

    printf("%s\n",a);
    printf("%s\n",b);

    printf("%p\n",a);
    printf("%p\n",b);
}

在我的机器上,输出如下。

ring ABC
String ABC
0x7ffc36f1b29d
0x7ffc36f1b29c

如您所见,两个数组指针仅相差一个。将源复制到目标位置时,已用源的最后N-1个字符覆盖了源数组的前N-1个字符,其中N是源数组中的字符数源,包括空终止符。