【问题标题】:Change two-dimensional dynamic array (realloc in c)改变二维动态数组(c中的realloc)
【发布时间】:2017-10-09 07:27:00
【问题描述】:

我有一个动态二维数组,大小为高*宽。

我想使用 realloc 来改变数组大小。

这是我的代码,但它不起作用。

我该怎么办?帮帮我!!

int main(){
      char **img;
      img = (char**)malloc(sizeof(char*)*height);

      for(i=0;i<height;i++){
        img[i] = (char*) malloc(sizeof(char)*width);
      }

      resize(height*2, width*2, img);
}

void resize(int height, int width, char **img){
      int i;
      img = (char**)realloc(img, sizeof(char)*height);
      for(i=0;i<h;i++){
         img[i] = (char*)realloc(img[i], width);
      }

}

【问题讨论】:

  • 首先,我建议您对在c 中模拟传递引用 进行一些研究。继续,imgnew 元素在重新分配之后会是什么?提示:它们未初始化,不能按原样传递给realloc。最后,你永远不应该将你传递给realloc的指针赋值给realloc,以防realloc失败。
  • 不是二维数组。并且逻辑是有缺陷的,首先free() 不再需要的单个数组(如果有的话),然后调整指针数组的大小,然后分配新需要的单个数组并调整以前存在的数组的大小。当然,检查 each malloc() / realloc() 调用中的错误。最后,修改局部变量会修改 copy,例如,您必须完成后返回img
  • 一般来说,“但它不起作用。”是不是合适的问题描述。
  • h 未在任何地方定义

标签: c arrays malloc realloc


【解决方案1】:

有两个主要问题。首先,realloc 可能会将内存块移动到新位置。因此realloc 有一个返回值,如果不需要移动,则指向“旧”内存块,如果需要移动,则指向新块,如果发生错误,则返回 NULL。但是您在resize 中忽略了这个事实,因为它无法更改调用者传入的指针对象。我建议调整原型,使resize 返回一个(可能是新的)指针,就像realloc 一样。

其次,在重新分配每一行时,数组中可能存在未初始化的值,可能指向“某处”。重新分配这样一个未初始化的值是未定义的行为。我建议将“新”行设置为NULL,以便realloc 之后可以正常运行。因此,有必要知道“旧”高度,因为您没有机会将定期初始化的指针与“垃圾”指针区分开来。

查看修改后的代码。希望对您有所帮助。

char** resize(int oldHeight, int oldWidth, int height, int width, char **img){
    int i;
    img = realloc(img, sizeof(char)*height);
    for (int i=oldHeight; i<height; i++)
        img[i] = NULL;

    for(i=0;i<height;i++){
        img[i] = realloc(img[i], width);
        for (int col=oldWidth; col < width; col++) {
            img[i][col] = 0;
        }
    }

    return img;
}

int main(){
    int height = 10;
    int width = 20;

    char **img;
    img = malloc(sizeof(char*)*height);

    for(int i=0;i<height;i++){
        img[i] = malloc(sizeof(char)*width);
    }

    img = resize(height, width, height*2, width*2, img);
}

【讨论】:

    【解决方案2】:

    除了你

    • 不处理二维数组和
    • 如果新高度小于旧高度和代码泄漏内存
    • 您不需要在 C 中强制转换 void-pointers 和
    • 所有ints 都应该是size_ts

    这里有两个主要的bug,作为代码

    • 传递了错误的大小来重新分配外部数组。它将height 乘以sizeof (char) 而不是sizeof (char*)
    • 在通过内部调整大小循环将realloc() 传递给realloc() 之前,无法使用NULL 初始化指向“外部”数组的附加指针realloc

    因此,假设新高度大于或等于旧高度的最小调整可能看起来像

    void resize(size_t height, size_t height_current, size_t width, char **img){
      int i;
      img = (char**)realloc(img, sizeof(char*)*height);
      for(i=height_current;i<height;i++){
         img[i] = NULL;
      }
      for(i=0;i<width;i++){ // correct copypasta mistake here
         img[i] = (char*)realloc(img[i], width); 
      }
    }
    

    更好的版本可能是这样的

    void resize(size_t height, size_t height_current, size_t width, size_t char **img)
    {
      if (height != height_current)
      {
        if (height < height_current)
        {
          for (size_t i = height; i < height_current; ++i)
          {
             free(img[i]);
          }
        }
    
        img = realloc(img, height * sizeof *img);
    
        if (height > height_current)
        {
          for (size_t i = height_current; i < height; ++i)
          {
             img[i] = NULL;
          }
        }
      }
    
      for (size_t i = 0; i < width; ++i)
      {
         img[i] = realloc(img[i], width * sizeof *img[i]);
      }
    }
    

    这样称呼它:

    resize(height*2, height, width*2, img);
    

    真的还想为所有对malloc()realloc() 的调用添加错误检查,因为它们很可能会失败!

    【讨论】:

    • “最小调整”并不能解决img-pointer的变化问题;你在第二个版本中混合了宽度和高度。
    • @StephanLechner:通过添加 cmets 和代码修复。对不起。
    • for(i=width_current;i&lt;width;i++){ img[i] = NULL;} 必须处理 height,而不是处理 width
    • @StephanLechner: 唷 .. 手动保持两个分支同步通常的痛苦...... ;-)
    【解决方案3】:

    要正确执行此操作,您必须在调整大小之前至少知道行数。一种可能性是定义一个 struct 包含附加信息(一种 OOP 方法,将所有相关数据放在一起),如以下示例(还存储列数,只是为了完整性,此处未测试代码):

    #include <stdlib.h>
    #include <string.h>
    
    typedef struct Lookup Lookup;
    
    struct Lookup
    {
        size_t rows;
        size_t cols;
        char **data;
    };
    
    static void Lookup_destroy(Lookup *self)
    {
        if (!self) return;
        for (size_t r = 0; r < self->rows; ++r)
        {
            free(self->data[r]);
        }
        free(self->data);
        free(self);
    }
    
    static Lookup *Lookup_create(size_t rows, size_t cols)
    {
        Lookup *self = malloc(sizeof *self);
        if (!self) return 0;
    
        self->rows = rows;
        self->cols = cols;
        self->data = malloc(rows * sizeof *(self->data));
        if (!self->data)
        {
            free(self);
            return 0;
        }
        memset(self->data, 0, rows * sizeof *(self->data));
        for (size_t r = 0; r < rows; ++r)
        {
            self->data[r] = malloc(cols * sizeof *(self->data[r]));
            if (!self->data[r])
            {
                Lookup_destroy(self);
                return 0;
            }
        }
    
        return self;
    }
    
    static Lookup *Lookup_resize(Lookup *self, size_t rows, size_t cols)
    {
        if (!self) return Lookup_create(rows, cols);
    
        // free rows that are no longer needed, if any:
        for (size_t r = rows; r < self->rows; ++r)
        {
            free(self->data[r]);
            self->data[r] = 0;
        }
    
        // reallocate array of rows:
        char **newdata = realloc(self->data, rows * sizeof *newdata);
        if (!newdata)
        {
            Lookup_destroy(self);
            return 0;
        }
    
        // update row array and row count: 
        self->data = newdata;
        size_t oldrows = self->rows;
        self->rows = rows;
    
        // initialize new rows to NULL, if any:
        if (rows > oldrows)
        {
            memset(self->data + oldrows, 0,
                    (rows - oldrows) * sizeof *(self->data));
        }
    
        // reallocate individual rows:
        for (size_t r = 0; r < rows; ++r)
        {
            char *newrow = realloc(self->data[r], cols * sizeof *newrow);
            if (!newrow)
            {
                Lookup_destroy(self);
                return 0;
            }
            self->data[r] = newrow;
        }
    
        // update col count:
        self->cols = cols;
    
        return self;
    }
    

    请注意realloc() 的结果是如何始终首先存储到临时变量中的,这是正确处理错误所必需的。这段代码只是在出现任何错误时丢弃整个对象——当然你可以用更多的代码做不同的事情。

    您可以像这样在代码中使用它:

    int main(){
          Lookup img;
          img = Lookup_create(height, width);
          // check for NULL here
    
          img = Lookup_resize(img, height*2, width*2);
          // and check for NULL here
    }
    

    【讨论】:

      猜你喜欢
      • 2013-12-16
      • 2011-01-11
      • 1970-01-01
      • 2016-01-05
      • 1970-01-01
      • 1970-01-01
      • 2011-01-28
      • 1970-01-01
      • 2018-08-07
      相关资源
      最近更新 更多