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