在C语言中实现字符串复制函数

在C语言中实现字符串复制函数,c,C,在最近的一次求职面试中,我被要求实现自己的字符串复制功能。我成功地编写了我认为在某种程度上有效的代码。然而,当我回到家再次尝试这个问题时,我意识到它比我想象的要复杂得多。下面是我想出的代码: #include <stdio.h> #include <stdlib.h> char * mycpy(char * d, char * s); int main() { int i; char buffer[1]; mycpy(buffer, "hello wo

在最近的一次求职面试中,我被要求实现自己的字符串复制功能。我成功地编写了我认为在某种程度上有效的代码。然而,当我回到家再次尝试这个问题时,我意识到它比我想象的要复杂得多。下面是我想出的代码:

#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;
}
#包括
#包括
char*mycpy(char*d,char*s);
int main(){
int i;
字符缓冲区[1];
mycpy(缓冲区,“hello world\n”);
printf(“%s”,缓冲区);
返回0;
}
char*mycpy(char*目的地,char*源){
如果(!destination | |!source)返回NULL;
char*tmp=目的地;
while(*destination!=NULL | |*source!=NULL){
*目的地=*来源;
目的地++;
源码++;
}
返回tmp;
}
我在网上查看了一些其他示例,发现由于C中的所有字符串都以null结尾,所以我应该先读取null字符,然后在退出之前将null字符附加到目标字符串


然而有一件事我很好奇,那就是记忆是如何被处理的。我注意到,如果我使用strcpy()库函数,我可以将10个字符的字符串复制到大小为1的字符数组中。这怎么可能?strcpy()函数是否以某种方式为目标分配了更多内存?

不,我认为是因为strcpy()不安全,并且会在它之后覆盖内存。您应该改用
strncpy()

不,您正在写入缓冲区,并覆盖(在本例中)堆栈的剩余缓冲区。这是非常危险的行为


通常,您应该始终创建提供限制的方法。在大多数C库中,这些方法在方法名称中用
n
表示。

C不像其他语言(C#、Java等)那样执行任何运行时边界检查。这就是为什么可以在数组末尾写入内容。但是,在某些情况下,您将无法访问该字符串,因为您可能会侵犯不属于您的内存,从而导致分段错误。K&R将是学习此类概念的一本好书。

正如其他答案所说,您正在覆盖缓冲区,因此为了便于测试,请将其更改为:

char buffer[ 12 ];
对于工作面试,他们可能希望:

char *mycpy( char *s, char *t )
{
    while ( *s++ = *t++ )
    {
        ;
    }
    return s;
}
strcpy()
函数完全放弃内存管理,因此需要在调用函数之前完成所有分配,然后在必要时释放。如果源字符串的字符数大于目标缓冲区的字符数,
strcpy()
将继续将超过缓冲区末尾的字符写入未分配的空间,或写入分配给其他对象的空间

这可能非常糟糕


strncpy()
的工作原理类似于
strcpy()
,只是它允许您传递一个描述缓冲区大小的附加变量,因此函数在达到此限制时将停止复制。这更安全,但仍然依赖于调用程序来正确分配和描述缓冲区——如果您提供的长度错误,它仍然可以超过缓冲区的末尾,从而导致相同的问题。

好的面试问题有多个层次,应聘者可以展示不同层次的理解

在语法“C语言”层,以下代码来自经典的Kernighan和Ritchie著作(“C编程语言”):


在采访中,您确实可以指出该功能不安全,最明显的是
*dest
上的缓冲区不够大。此外,可能存在重叠,即如果
dest
指向
src
缓冲区的中间,则会有无止境的循环(这最终会造成内存访问故障)。

通常,在可能的情况下,最好使用
const
修饰符,例如源参数

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和目标具有相同的数据。最好不要重新运行任何数据,而是让目标作为输出参数。你能重写同样的吗。

下面的版本适合我。但我不确定这是否是糟糕的设计:

while(source[i] != '\0' && (i<= (MAXLINE-1)))
{
dest[i]=source[i];
++i;
}

while(source[i]!='\0'&&(i)你应该知道你正在写入的缓冲区有多大。
strncpy
不是“魔法”。这里,例如
char buffer[1];
然后尝试复制一个更大的缓冲区是个问题。你通常会知道缓冲区的大小。
char buffer[1]
如果您使用
strncpy(),则尝试复制更大的缓冲区不会有问题
,因为您可以将其作为大小传递,并且不会溢出。我的观点是,没有多少次您可以复制可能被截断的字符串。例如,在这种情况下,您可以知道源字符串的长度,并使用正确的大小分配目标字符串。大多数情况下建议使用
strncpy()
,因为“它是安全的”,虽然在技术上是正确的,但不是正确的做法
不一定以null终止目的地,这会使目的地变得无用。
strncpy
并不比strcpy更安全。最严重的错误是,当它失败时,它会给你一些不是C字符串的东西(即,不是以null终止的)。下次你尝试用它做一些严格的事情时(比如strcmp或strlen),您将访问未定义的行为区域。至少添加缓冲区长度范围检查,以及目标是否在源中(或者您将覆盖所有内存,因为不再有尾随0)
while(source[i] != '\0' && (i<= (MAXLINE-1)))
{
dest[i]=source[i];
++i;
}