【问题标题】:Realloc with an array of pointers in a structRealloc 在结构中具有指针数组
【发布时间】:2018-07-02 21:01:16
【问题描述】:

在 C89 中,以下代码可以正常工作:

typedef struct{
    int size;
    int **ptr;
}foo;

int main()
{
    foo *var;

    var = malloc(sizeof(foo) + 2 * sizeof(int*));

    int a = 3, b = 6;

    var->ptr[0] = &a;
    var->ptr[1] = &b;

    printf("%d %d\n", (*var->ptr[0]), (*var->ptr[1]));

    return 0;
}

执行时显示:3 6

但如果我替换

var = malloc(sizeof(foo) + 2 * sizeof(int*));

var = malloc(sizeof(foo));
var = realloc(var, sizeof(foo) + 2 * sizeof(int*));

它在没有任何警告的情况下编译,但在以下行出现分段错误:

var->ptr[0] = &a;

我真的不知道我做错了什么,所以我很感激任何意见。我是初学者,如果我的错误很明显,我深表歉意。

谢谢。

【问题讨论】:

  • 你永远不会初始化 ptr 字段。
  • @DevSolar:嗯,实际上他确实为ptr 分配了内存(这就是+ 2 * sizeof(int*) 的用途),他只是没有分配ptr 指向那个内存跨度>
  • 两个版本都是错误的,第一个版本表现出特别糟糕的未定义行为形式 - 它似乎工作正常
  • 你到底想做什么?您是否想要该结构的单个连续内存块,包括整数数组。或者你只是想让那个结构指向数组
  • 我想知道为什么新手发布他们的问题,然后不留下来回答试图帮助他们的人提出的任何问题。

标签: c pointers struct realloc


【解决方案1】:

我会说这在 C89 中也是错误的。

malloc(sizeof(foo) + 2 * sizeof(int*));

分配的比你需要的多,但不是你期望的那样。

+-------+------------+---------+--------+
|  size | ptr        |  int*   | int*   |
+-------+------------+---------+--------+

|  sizeof foo        | 2 * sizeof(int*) |

如您所见,var->foo 仍然未初始化,无论有多少额外字节 你已经分配了,所以

var->ptr[0] = &a;
var->ptr[1] = &b;

会失败。

你应该做的是:

var = malloc(sizeof *var);

var->ptr = malloc(2 * sizeof *var->ptr);

var->ptr[0] = &a;
var->ptr[1] = &b;

然后它看起来像这样

                                     | int |     | int |

                                     +-----+     +-----+
                                     |  a  |     |  b  |
                                     +-----+     +-----+
                                         ^         ^
                                         |         |
                                         |         |
+-------+------------+  points to     +---------+--------+
|  size | ptr        | ------------>  | int*    | int*   |
+-------+------------+                +---------+--------+

|  sizeof foo        |                | 2 * sizeof(int*) |

不要忘记检查malloc 的返回值并释放内存 之后。

编辑

可行的方法是:

var = malloc(sizeof(foo) + 2 * sizeof(int*));
var->ptr = (int**) ( ((char*) var) + sizeof *var );

您设置var->pointer 以指向struct 的内存。尽管 这可能有效,我认为这很丑陋,我会拒绝提交 有这样的同事。

【讨论】:

  • 谢谢,非常明确的答案。出于某种原因,我认为我不能直接在结构内的变量上使用 malloc ,但我显然错了。感谢所有回答的人,尽管有些人似乎没有意识到我想存储 a 和 b 的地址而不是它们的值,但许多答案也很有用。
【解决方案2】:

在 Windows 10 的 Visual Studio 中,您的两个示例都不起作用。这是因为结构在这两种情况下都未初始化。

箭头操作:

foo->bar

是以下的简写:

(*foo).bar

因此,如果“foo”结构变量未初始化,取决于编译器,结构内的指针将指向内存中的随机位置。

所以,当你这样使用箭头运算符时:

var->ptr[0] = &a;

你所做的是:

(*var).ptr[0] = &a;

因此,基本上,您尝试将“a”的地址写入内存中您不应该访问的位置。这就是您遇到分段错误的原因。

但是,如果您已经对其进行了初始化:

int goodmemorylocation = 0;
var->ptr = &goodmemorylocation; // You change the location of your pointer to an accessible location in memory

那么您的代码在我的系统中适用于这两种情况。

也许,在您的系统中,您使用 malloc 的第一个示例以某种方式指向了您内存中的正确地址。这就是它起作用的原因。

【讨论】:

    【解决方案3】:

    问题是您没有分配ptr 指向任何东西,所以它指向随机内存,并且ptr[index] 的任何访问都是未定义的行为

    像乔纳森的回答那样更改ptr 的声明是最安全的选择,例如:

    typedef struct{
        int size;
        int* ptr[1]; // or 0 if your compiler supports it
    }foo;
    
    int main()
    {
        foo *var;
    
        var = malloc(offsetof(foo, ptr));
        if (var) {
            var->size = 0;
            ...
        }
    
        foo *newvar = realloc(var, offsetof(foo, ptr) + (2 * sizeof(int*)));
        if (newvar) {
            var = newvar;
            var->size = 2;
    
            int a = 3, b = 6;
    
            var->ptr[0] = &a; // <-- OK!
            var->ptr[1] = &b; // <-- OK!
    
            printf("%d %d\n", *(var->ptr[0]), *(var->ptr[1]));
        }
    
        free(var);
        return 0;
    }
    

    但是,如果出于某种原因,您无法更改 ptr 的声明(例如,foo 与现有 API 一起使用),那么您可以改为:

    typedef struct{
        int size;
        int **ptr;
    }foo;
    
    int main()
    {
        foo *var = malloc(sizeof(foo));
        if (var) {
            var->size = 0;
            var->ptr = NULL; // <-- add this!
            ...
        }
    
        foo *newvar = realloc(var, sizeof(foo) + (2 * sizeof(int*)));
        if (newvar) {
            var = newvar;
            var->size = 2;
            var->ptr = (int**)(var + 1); // <-- add this!
    
            int a = 3, b = 6;
    
            var->ptr[0] = &a; // <-- OK!
            var->ptr[1] = &b; // <-- OK!
    
            printf("%d %d\n", *(var->ptr[0]), *(var->ptr[1]));
        }
    
        free(var);
        return 0;
    }
    

    在后一种情况下,确保每次想要(重新)分配var 时都执行相同的ptr 分配。我会将该逻辑包装在一组辅助函数中,例如:

    typedef struct{
        int size;
        int **ptr;
    }foo;
    
    foo* createFoo(int size)
    {
        foo *var = malloc(sizeof(foo) + (size * sizeof(int*)));
        if (var) {
            var->size = size;
            var->ptr = (int**)(var + 1);
        }
        return var;
    }
    
    foo* resizeFoo(foo **var, int newSize)
    {
        foo *newvar = realloc(*var, sizeof(foo) + (newSize * sizeof(int*)));
        if (newvar) {
            newvar->size = newSize;
            newvar->ptr = (int**)(newvar + 1);
            *var = newvar;
        }
        return newvar;
    }
    
    int main()
    {
        foo *var = createFoo(0);
        ...
    
        if (resizeFoo(&var, 2)) {
            int a = 3, b = 6;
    
            var->ptr[0] = &a; // <-- OK!
            var->ptr[1] = &b; // <-- OK!
    
            printf("%d %d\n", *(var->ptr[0]), *(var->ptr[1]));
        }
    
        free(var);
        return 0;
    }
    

    【讨论】:

      【解决方案4】:

      我认为你的声明是错误的。以下行假设ptr 指向一些内存,而实际上它根本没有被初始化!

      var->ptr[0] = &a;
      

      如果你的编译器支持它,它不应该更像这样吗:

      typedef struct{
          int size;
          int* ptr[0];
      }foo;
      

      否则,将ptr[0] 更改为ptr[1],并根据结构已包含一个元素的事实调整您的计算。

      注意:一旦写入错误的地址,结果是不确定的。你有一个问题,但它只出现在一种情况下。在这两种情况下问题仍然存在。

      【讨论】:

      • 你应该注意到他的代码存储的是a的地址而不是a的值。
      • @pm100:没错。
      【解决方案5】:

      你需要修复结构:

      typedef struct{
          int size;
          int *ptr;
      }foo;
      

      还有代码:

       var = malloc(sizeof(foo));
       var->ptr = malloc(2 * sizeof(int));
      
       int a = 3, b = 6;
       // store a and b in array, not their addresses
       var->ptr[0] = a;
       var->ptr[1] = b;
      

      假设您希望数组中有 2 个条目。当然,忽略我没有测试 malloc() 的结果这一事实。

      如果要调整数组大小,只需重新分配 var-&gt;ptr 而不是 var

      【讨论】:

      • 因为他不知道自己在做什么——我们应该问他:-)
      • 我怀疑你这样做了,他将局部变量的地址存储在该结构中。我问 - 让我们看看他说什么
      猜你喜欢
      • 1970-01-01
      • 2018-03-25
      • 1970-01-01
      • 2012-07-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-02
      • 1970-01-01
      相关资源
      最近更新 更多