在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
答案 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);
}
您看到b
和a
具有连续的地址,并且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
是源数组中的字符数源,包括空终止符。