【问题标题】:Why can't I realloc this string?为什么我不能重新分配这个字符串?
【发布时间】:2016-04-06 19:59:14
【问题描述】:

我有一个这样的结构:

typedef struct TEXT {
    char *text;
    struct TEXT *next;
} TEXT;

在某些功能中,我有类似的东西:

TEXT *line = (TEXT *) malloc(sizeof(TEXT));
line->text = NULL; // was "\0" before
char current = getchar();
while (current != '\n') {
    AddChar(&(line->text), current); // Was AddChar(line->text, current);
    current = getchar();
}

AddChar 函数是这样的:

void AddChar(char **text, char c) { //was char *text
    *text = (char *) realloc(*text, strlen(*text)+2); //was text = ...
    char char_array[2] = {c, '\0'); 
    strcat(*text, char_array); //was strcat(text, char_array);
}

不幸的是,程序崩溃了。

据我了解,strlen 无法计算出如果 text == NULL,则长度应为 0...

无论如何,有了这个 AddChar 函数,一切正常:

void AddChar(char **text, char c) {
    if (*text == NULL) {
        *text = (char *) malloc(2);
        (*text)[0] = c;
        (*text)[1] = '\0';
    }
    else {
        *text= (char *) realloc(*text, sizeof(*text)+2);
        char char_array[2] = { c , '\0' };
        strcat(*text, char_array);
    }
}

.

.

我也有问题

void AddChar(char *text, char c) {
    text = "something";
}

不改变 line->text,但将 *text 更改为 **text 解决了这个问题。

【问题讨论】:

  • 因为你不能重新分配字符串文字。
  • 不要将malloc 和朋友的结果投射到 C 中!
  • 不要在标题中添加“[已解决]”。要表明您的问题已解决,请接受答案。如果现有答案都没有解决您的问题,您可以发布并接受您自己的答案。
  • strlen() 在空指针上具有未定义的行为。它不返回 0,因为没有字符串。
  • 请注意,getchar() 返回一个 int,您需要测试 EOF 以及 '\n'

标签: c string pointers struct null-terminated


【解决方案1】:

只有NULL 初始化指针或malloc 系列函数(malloccallocrealloc)返回的指针可以传递给另一个malloc 系列函数。 line->text 使用字符串文字 "\0" 初始化,因此 line->text 不能传递给 realloc 函数。
另请注意,您不能修改字符串文字。

【讨论】:

    【解决方案2】:

    其中一个问题是你试图重新分配你自己没有分配的东西。

    另一个问题是您尝试在 AddChar 函数内重新分配一个局部变量,并期望它对调用函数有任何影响,但它没有。

    将参数传递给函数时,按值传递,这意味着该值被复制到函数中的局部参数变量中,该变量仅在函数内部,并且更改不会更改调用函数时使用的原始变量。您需要的是传递 C 不支持的参数 by reference,但您可以通过使用指针 emulate 它。在您的情况下,使用指向使用地址运算符& 的指针的指针。

    【讨论】:

    • 我以为我只是传递一个指针(char*),而不是复制整个字符串
    • @Jecke 当你像这样传递一个指针时,pointer 被复制。这导致你有 两个 指针变量,最初指向相同的内存,但是一旦你在函数内部重新分配,你就有两个不同的指针,每个指针指向不同的位置。
    • 我明白了。但是为什么重新分配后指针会改变?在这个例子中:tutorialspoint.com/c_standard_library/c_function_realloc.htm str 的地址在 realloc 之前和之后是一样的。
    • @Jecke realloc 函数可能返回相同的指针。或者它可能返回一个完全不同的指针。或者它可能返回NULL(你不处理,我并不是说你应该检查它,不要将结果直接分配给你重新分配的指针如果realloc 失败,可能意味着您会丢失原始指针)。
    • 好的,我明白了。我使用了 char **text 和 &(line->text),现在效果更好(如果我明确写 *text = "something",它会改变),但我仍然无法处理 realloc。
    【解决方案3】:

    据我了解,strlen 无法计算出如果 text == NULL,则长度应为 0...

    另一方面,section 7.1.4, paragraph 1 C 标准规定 NULL 是 strlen 的无效值:

    如果函数的参数有无效值(例如函数域外的值,或程序地址空间外的指针,或空指针,或者当相应参数不是 const 限定时指向不可修改存储的指针)或具有可变数量参数的函数不期望的类型(提升后),行为未定义。


    虽然我们讨论的是无效值strcat 会期望它的两个参数 都包含字符串。这意味着它们都必须包含以第一个 '\0' 字符结尾的字符序列。您已经通过在表示第二个参数的数组中添加一个 '\0' 字符来证明这一点,但是您能证明在表示您的第一个参数的数组中有一个 '\0' 字符吗?


    *text = (char *) realloc(*text, strlen(*text)+2);
    

    除了空指针之外,x = realloc(x, ...); 模式的任何内容都是错误的。有关这方面的更多信息,请参阅Proper usage of realloc


    *text= (char *) realloc(*text, sizeof(*text)+2);
    

    至于这个,一开始它似乎对你有用,但我可以向你保证,它已经坏了。重要的是要意识到您打算分配字符,而不是char *s,因此sizeof(*text)(即sizeof (char *))在这里无效。

    通常这会分配 6 到 10 个字符。如果溢出该缓冲区,则行为未定义。那是什么意思?好吧,定义未定义的行为是一个悖论,但未定义行为的典型后果范围从某些系统的“根本没有”和“它工作正常”到其他系统的“segfaults”和“heartbleed”;但它不是便携式的。


    我也有问题

    void AddChar(char *text, char c) {
        text = "something";
    }
    

    不改变 line->text,但将 *text 更改为 **text 解决了这个问题。

    我很好奇你认为AddChar(NULL, 42); 会做什么,当AddChar 被这样定义时......你认为它会将空指针常量NULL 分配给字符串文字是"something"

    C 不支持按引用传递。当你传递一个参数(例如,一个指针,比如一个数组表达式隐式转换为一个指针)时,幕后发生的事情是声明了一个新变量(在这种情况下,参数@ 987654341@ 是一个变量)并将该值的副本(在本例中为指针)分配给该变量。这称为“按值传递”。

    当你传递一个指针,并修改指针指向的东西(例如,使用*pointer = ...pointer[x] = ...)时,你正在模拟传递引用;您正在修改变量引用的对象,而不是修改变量本身。


    哦,还有一件事,尽管链接到“正确使用realloc”,you should not cast the return value of malloc (or `realloc) in C

    【讨论】:

      【解决方案4】:

      不同意 OP 的最终“无论如何,使用这个 AddChar 函数,一切正常:”因为它没有分配足够的内存。

      建议

      void AddChar(char **text, char c) {
          size_t length = strlen(*text); 
          *text = realloc(*text, length + 2);  // proper size calculation
          if (*text == NULL) {  // check if successful
            abort(-1);
          } 
          (*text)[length] = c;
          (*text)[length + 1] = '\0';
      }
      

      【讨论】:

        猜你喜欢
        • 2014-12-06
        • 1970-01-01
        • 2015-09-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-12-29
        • 2021-08-03
        相关资源
        最近更新 更多