为什么strcpy会将更多字符复制到变量中?

时间:2014-08-31 05:08:27

标签: c strcpy calloc

我目前正在研究许多strcpy'和calloc的。然后我听说使用strncpy比较安全。所以我所做的是创建一个处理strcpy的函数。如下所示。

void safeStrncpy(char * dest, char * src){
    //copy string 
    if(sizeof(dest) >= strlen(src) + 1){
        strncpy(dest, src, strlen(src));
    }else{
        if(realloc(dest, (strlen(src)) + 1) == NULL){
            printf("error");    
        }else{
            strncpy(dest, src, strlen(src));
        }
    }
}

您会注意到我使用了sizeof(dest)。我真正想要用的部分是获取分配给dest的内存大小,以便我知道何时使用realloc。但后来我了解到你无法获得分配给dest的内存大小,所以我想到了一个解决方法。

char * l = calloc(10,sizeof(char));
printf("%s", strcpy(l,"asdfghjkldfghasdfghjkl"));

上面显示的代码将10个项目分配给指针l。我认为如果我这样做并复制了比分配的更多的字符,它只会复制适合大小的字符。 我期待l的价值是" asdfghjkld"。但令我惊讶的是,它并没有。它复制了整个字符串,这是" asdfghjkldfghasdfghjkl"。

现在,为什么要使用calloc才会被覆盖?在char指针声明,内存分配和strcpy期间在后台发生了什么? c会自动重新分配内存吗?我是否需要担心这种行为,可能是安全问题?

2 个答案:

答案 0 :(得分:1)

你问:

  

为什么strcpy会将更多字符复制到变量中?

documentation of strcpy

  

将source指向的C字符串复制到destination指向的数组中,包括终止空字符(并在该点停止)。

     

为避免溢出,目标指向的数组大小应足够长,以包含与源相同的C字符串(包括终止空字符),并且不应在内存中与源重叠。

如果目标的长度不足以包含源,则不会指定程序的行为方式。最有可能的是,您的实现只是写入超出范围的内存,这将导致未定义的行为。

这是一个可能的解决方案。

  1. 创建一个struct,其中包含char*和一个大小。

    typedef struct _MyString
    {
       char* data;
       size_t size;
    } MyString;
    
  2. safeStrncpy

    中使用它
    void safeStrncpy(MyString* dest, char * src){
      if(dest->size < strlen(src) + 1){
        dest->size = strlen(src)+1;
        dest->data = realloc(dest->data, dest->size);
      }
      strcpy(dest->data, src);
    }
    

答案 1 :(得分:0)

您展示的代码误用strncpy()。它也误用了sizeof()。如果通过打印消息报告错误,则应将错误打印为标准错误,即stderr(而不是标准输出)。最后,如果重新分配内存,调用代码不会知道这一点,这会导致realloc()在内存中移动数据时出现问题。目前的代码是:

void safeStrncpy(char * dest, char * src){
    //copy string 
    if(sizeof(dest) >= strlen(src) + 1){
        strncpy(dest, src, strlen(src));
    }else{
        if(realloc(dest, (strlen(src)) + 1) == NULL){
            printf("error");    
        }else{
            strncpy(dest, src, strlen(src));
        }
    }
}

  • sizeof(dest)是指针的大小(可能是4或8个字节)。您必须知道目标字符串的大小,因此您需要将其作为参数传递。
  • strncpy(dest, src, strlen(src))保证字符串不以空值终止。它告诉strncpy()只复制与字符串中一样多的非空字符。

我建议:

void safeStrncpy(char **p_buffer, size_t *p_bufsiz, const char *src)
{
    size_t newlen = strlen(src) + 1;
    if (*p_bufsiz < newlen)
    {
        char *n_buffer = malloc(newlen);
        if (n_buffer == 0)
        {
            fprintf(stderr, "Failed to allocate %zu bytes memory\n", newlen);
            return;
        }
        *p_buffer = n_buffer;
        *p_bufsiz = newlen;
    }
    memmove(*p_buffer, src, newlen);
}

因为我确切地知道字符串有多长,所以不需要使用strcpy(),它必须检查每个字符以查看它是否为空;使用memmove() - 或memcpy()如果您愿意,因为这次是安全的 - 可能会更快。

示例调用序列:

char *buffer = 0;
size_t bufsiz = 0;

char line[4096];

while (fgets(line, sizeof(line), stdin) != 0)
{
    safeStrncpy(&buffer, &bufsiz, line);
    …use buffer (which is of size bufsiz)…
}

free(buffer);  // All done

请注意,您应该记录*p_buffersrc一定不能重叠。