【问题标题】:What does memcpy do exactly in this program?memcpy 在这个程序中到底做了什么?
【发布时间】:2012-10-16 12:30:40
【问题描述】:

我正在编写一个从标准输入获取输入的程序。第一个输入将是一个整数,表示要从标准输入读取的字符串数。 我只是将字符串逐个字符读入动态分配的内存中,并在字符串结束后显示它。
但是当字符串大于分配的大小时,我使用realloc 重新分配内存。但即使我使用 memcpy,该程序也可以工作。不使用 memcpy 是未定义的行为吗?但是示例Using Realloc in C 没有使用memcpy。那么哪一种是正确的方法呢?下面显示的程序是否正确?

/* ss.c
 * Gets number of input strings to be read from the stdin and displays them.
 * Realloc dynamically allocated memory to get strings from stdin depending on
 * the string length.
 */

#include <stdio.h>
#include <stdlib.h>

int display_mem_alloc_error();

enum {
    CHUNK_SIZE = 31,
};

int display_mem_alloc_error() {
    fprintf(stderr, "\nError allocating memory");
    exit(1);
}

int main(int argc, char **argv) {
    int numStr;                  //number of input strings
    int curSize = CHUNK_SIZE;    //currently allocated chunk size
    int i = 0;                   //counter
    int len = 0;                 //length of the current string
    int c;                       //will contain a character
    char *str = NULL;            //will contain the input string
    char *str_cp = NULL;         //will point to str
    char *str_tmp = NULL;        //used for realloc

    str = malloc(sizeof(*str) * CHUNK_SIZE);
    if (str == NULL) {
        display_mem_alloc_error();
    }    
    str_cp = str;   //store the reference to the allocated memory

    scanf("%d\n", &numStr);   //get the number of input strings
    while (i != numStr) {
        if (i >= 1) {   //reset
            str = str_cp;
            len = 0;
        }
        c = getchar();
        while (c != '\n' && c != '\r') {
            *str = (char *) c;
            printf("\nlen: %d -> *str: %c", len, *str);
            str = str + 1;
            len = len + 1;
            *str = '\0';
            c = getchar();
            if (curSize/len == 1) {
                curSize = curSize + CHUNK_SIZE;
                str_tmp = realloc(str_cp, sizeof(*str_cp) * curSize);
                if (str_tmp == NULL) {
                    display_mem_alloc_error();
                }
                memcpy(str_tmp, str_cp, curSize);    // NB: seems to work without memcpy
                printf("\nstr_tmp: %d", str_tmp);
                printf("\nstr: %d", str);
                printf("\nstr_cp: %d\n", str_cp);
            }
        }
        i = i + 1;
        printf("\nEntered string: %s\n", str_cp);
    }
    return 0;
}

/* -----------------
//input-output
gcc -o ss ss.c
./ss < in.txt

// in.txt
1
abcdefghijklmnopqrstuvwxyzabcdefghij

// output
// [..snip..]
Entered string:
abcdefghijklmnopqrstuvwxyzabcdefghij
-------------------- */

谢谢。

【问题讨论】:

  • 但是在 C 中使用 Realloc 的示例确实使用 memcpy。但是程序没有 memcpy 也能正常工作。它们都在说同样的事情。

标签: c string malloc memcpy realloc


【解决方案1】:

您的程序不太正确。您需要删除对memcpy 的调用以避免偶尔出现难以诊断的错误。

来自realloc man page

realloc() 函数改变指向的内存块的大小 通过 ptr 到 size 字节。 范围内内容不变 从地区开始到新旧最低 尺寸

因此,您无需在realloc 之后调用memcpy。事实上,这样做是错误的,因为您之前的堆单元可能已在 realloc 调用中被释放。如果它被释放,它现在指向具有不可预测内容的内存。

【讨论】:

    【解决方案2】:

    C11 standard (PDF),第 7.22.3.4 节第 2 段:

    realloc 函数释放 ptr 指向的旧对象,并返回一个指向具有 size 指定大小的新对象的指针。 新对象的内容应与释放前旧对象的内容相同,直到新旧大小中的较小者。新对象中超出旧对象大小的任何字节都具有不确定的值。

    简而言之,memcpy 是不必要的,而且确实是错误的。错误有两个原因:

    • 如果reallocfreed 您以前的内存,那么您正在访问不属于您的内存。
    • 如果realloc 刚刚扩大了你以前的内存,你给memcpy 两个指向同一区域的指针。 memcpy 在其两个输入指针上都有一个 restrict 限定符,这意味着如果它们指向同一个对象,则它是未定义的行为。 (旁注:memmove 没有这个限制)

    【讨论】:

    • +1 实际上,你永远无法知道realloc() 是扩大现有块还是将内容移动到新块,因为将旧指针用于任何事情都是非法的,即使将其与新的,以确定发生了什么。 Nick Lewycky 有一个很好的例子来说明这一点:blog.regehr.org/archives/759#comment-4648
    【解决方案3】:

    Realloc 扩大为您的字符串保留的内存大小。如果可以在不移动数据的情况下放大它,那么它们将保留在原处。如果不能,则 malloc 更大的内存占用,并自己 memcpy 包含在先前内存占用中的数据。

    总之realloc之后不用调用memcpy是正常的。

    来自手册页:

    realloc() 函数试图改变分配的大小 to by ptr 到大小,并返回 ptr。如果没有足够的空间 扩大ptr指向的内存分配,realloc()创建一个新的 分配,尽可能多地复制 ptr 指向的旧数据 到新的分配,释放旧的分配,并返回一个指针 分配的内存。如果 ptr 为 NULL,realloc() 与调用相同 到 malloc() 的大小字节。如果 size 为零且 ptr 不为 NULL,则一个新的, 分配最小大小的对象并释放原始对象。什么时候 扩展使用 calloc(3) 分配的区域,realloc(3) 不保证 tee 额外的内存也是零填充的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-01-10
      • 2012-03-30
      • 2023-03-14
      • 2020-12-23
      • 2012-03-07
      • 1970-01-01
      • 2018-04-19
      • 1970-01-01
      相关资源
      最近更新 更多