在最近的一次面试中,我被要求实现自己的字符串复制功能。我设法编写了我认为有效的代码。然而,当我回到家中再次尝试这个问题时,我意识到它比我想象的要复杂得多。这是我提出的代码:
#include <stdio.h>
#include <stdlib.h>
char * mycpy(char * d, char * s);
int main() {
int i;
char buffer[1];
mycpy(buffer, "hello world\n");
printf("%s", buffer);
return 0;
}
char * mycpy (char * destination, char * source) {
if (!destination || !source) return NULL;
char * tmp = destination;
while (*destination != NULL || *source != NULL) {
*destination = *source;
destination++;
source++;
}
return tmp;
}
我在网上看了一些其他的例子,发现因为C中的所有字符串都是以null结尾的,所以我应该读取空字符,然后在退出之前将空字符附加到目标字符串。
然而,我很好奇的一件事是如何处理内存。我注意到如果我使用了strcpy()库函数,我可以将一个10个字符的字符串复制到一个大小为1的char数组中。这怎么可能? strcpy()函数是否以某种方式为目标分配了更多内存?
答案 0 :(得分:9)
良好的面试问题有几个层次,候选人可以向其展示不同层次的理解。
在句法“C语言”层上,以下代码来自经典的Kernighan和Ritchie书('The C programming language'):
while( *dest++ = *src++ )
;
在一次采访中,你确实可以指出这个功能并不安全,最值得注意的是*dest
上的缓冲区不够大。此外,可能存在重叠,即如果dest
指向src
缓冲区的中间,则会有无限循环(最终会产生内存访问错误)。
答案 1 :(得分:2)
不,我认为strcpy()
不安全,并且在它之后覆盖了内存。您应该使用strncpy()
代替。
答案 2 :(得分:2)
正如其他答案所说,你正在覆盖缓冲区,所以为了你的测试改变它:
char buffer[ 12 ];
对于面试,他们可能希望:
char *mycpy( char *s, char *t )
{
while ( *s++ = *t++ )
{
;
}
return s;
}
答案 3 :(得分:1)
不,你正在写缓冲区并覆盖(在这种情况下)堆栈的其余部分超过缓冲区。这是非常危险的行为。
通常,您应始终创建提供限制的方法。在大多数C库中,这些方法在方法名称中用n
表示。
答案 4 :(得分:0)
C不像其他语言(C#,Java等)那样执行任何运行时边界检查。这就是为什么你可以写出数组末尾的东西。但是,在某些情况下,您将无法访问该字符串,因为您可能正在侵占不属于您的内存,从而导致出现分段错误。 K&amp; R是学习这些概念的好书。
答案 5 :(得分:0)
strcpy()
函数完全放弃了内存管理,因此所有分配都需要在调用函数之前完成,并在必要时释放。如果源字符串的字符数多于目标缓冲区,则strcpy()
将继续将缓冲区的末尾写入未分配的空间,或者写入为其他内容分配的空间。
这可能非常糟糕。
strncpy()
与strcpy()
的工作方式类似,不同之处在于它允许您传递一个描述缓冲区大小的附加变量,因此该函数将在达到此限制时停止复制。这样更安全,但仍然依赖于调用程序来正确分配和描述缓冲区 - 如果提供的错误长度,它仍然可以超过缓冲区的末尾,从而导致相同的问题。
答案 6 :(得分:0)
char * mycpy (char * destination, char * source) {
if (!destination || !source) return NULL;
char * tmp = destination;
while (*destination != NULL || *source != NULL) {
*destination = *source;
destination++;
source++;
}
return tmp;
}
在上面的复制实现中,您的tmp和目标具有相同的数据。更好的是你不要后退任何数据,而是让目的地成为你的out参数。你能改写一下吗?
答案 7 :(得分:0)
以下版本适合我。我不确定它是不是很糟糕的设计:
while(source[i] != '\0' && (i<= (MAXLINE-1)))
{
dest[i]=source[i];
++i;
}
答案 8 :(得分:-1)
一般情况下,尽可能使用const
修饰符是个好主意,例如 source 参数。