在C中为结构指针数组成员分配地址

Posted

技术标签:

【中文标题】在C中为结构指针数组成员分配地址【英文标题】:Assigning an address to a struct pointer array member in C 【发布时间】:2011-04-26 10:29:54 【问题描述】:

在一些指针算术上遇到了相当大的麻烦。我认为我得到了概念(指针变量指向内存地址,普通变量指向数据)但我相信我的问题在于语法(*, &, (*), *(), 等)

我想要做的是构建自定义结构的动态数组(即指向堆结构的指针数组),我的接口提供了两种方法,“ad_to_obj_array”(它需要添加对象和可以为空的数组为空)和“obj_array_dustbin”(它只需要处理数组,也处理内容,堆对象)。前者如下图。

对象的细节并不重要(并且结构已经被重命名),但我对一般问题的解决方案如下,如果您能发现错误,我将不胜感激。编译器抱怨左值无效,我尝试将 RHS 指针中的地址分配给指向堆结构的指针数组中的指针值:

#define NUM_ELEM(x) (sizeof (x) / sizeof (*(x)))

obj* add_to_obj_array(obj* new_obj, obj* array)

  int number_of_elements = 0;
  if (array != NULL)
  
    number_of_elements = NUM_ELEM(array);
  

  obj* new_array = NULL;

  /* note: I am expecting sizeof(new_obj) to return the size of an obj* 
     to go into the array of pointers. */
  if ( NULL ==
       (new_array = (obj*)malloc((number_of_elements + 1)* sizeof(new_obj))) )
  
    /* memory request refused :( */
    return NULL;
  

  /* copy the old array pointers into the new array's pointer slots: */
  int i;
  for (i = 0; i < number_of_elements; i++)
  
    &(new_array[i]) = &(array[i]);
  

  /* add the new item to the end (assign pointer value directly): */
  new_array[number_of_elements] = new_obj;

  if (number_of_elements > 0)
  
    free(&array);
  

  return new_array;

现在,我尝试了以下违规行的排列:

  &(new_array[i]) = &(array[i]);
  *(new_array[i]) = &(array[i]);
  new_array[i] = &(array[i]);

并且都给出了一种或另一种编译器错误。我相当确定右侧是旧数组的第 i 个元素的地址,但是当数组的元素是指向结构的指针时,如何分配给新的第 i 个元素?

编辑 - 请注意,上面的宏 NUM_ELEM 不起作用;它总是会返回 1。请参阅下面的@Merlyn Morgan-Graham 的回答了解原因。

【问题讨论】:

【参考方案1】:

根据您的描述,您一开始就错了,所以当您开始复制内容时,您无能为力。

现在,您已将new_array(可能还有array)定义为指向obj 的指针。结果如下所示:

在这种情况下,您有一个指向动态分配的对象数组的指针。当/如果你扩大分配,你需要自己复制所有的对象。

根据您的描述:“(即指向堆结构的指针数组)”,您想要的是一个指针数组。如果您想自动分配该指针数组,您的定义将如下所示:

obj *array[NUMBER];

我的猜测是这不是你想要的。据推测,您也想动态分配该数组。看起来像这样:

在这种情况下,new_arrayarray 都需要定义为指向obj 的指针。然后,您将分配一个指针数组(即,指向任意数量的 objs 的指针)并让每个点指向 obj

obj **new_array;

// allocate an array of pointers with space to point at more items:    
new_array = malloc(sizeof(obj *) * new_elements);

// copy the pointers to the current items to the new array:
for (i=0; i<current_elements; i++)
    new_array[i] = array[i];

这样做的好处是,当您进行复制时,您只复制指针,而不是对象本身。特别是对于大型对象,这可以节省大量工作量。权衡是使用一个元素要经过两个间接级别而不是一个,因此引用可能会更慢(尽管很少非常慢,尤其是在相对高性能的处理器上)。

正如@rerun 已经指出的那样,无论哪种情况,您都可能希望使用realloc。特别是,这可能能够“就地”扩展分配,并避免经常复制数据。当然,这并不能保证,但至少你给了它一个机会;如果你malloc 并且每次都复制,你甚至消除了优化的可能性。

【讨论】:

+1,你的答案有图片。我想我现在不会再试图用(除了)词来回答了。 :D +1 用于时髦的图表。不过,“point point”说起来很奇怪,即使它是准确的:) 如果我有足够的声望来投票,我会 :) 谢谢,这已经一针见血了。被发现认为指针是指针,您可以将任何旧地址放入其中,而忘记了目标类型的重点!所以它是一个 obj**。我还将看一下 realloc():听起来它会频繁地对小型指针数组进行优化。谢谢! @tehwalrus: void* 是“任意指针”类型。指针的大小都是一样的,你可以随意转换它们,但是最好通过一个好的接口来记录你的意图,而不是尝试通过在你的实现中进行(混淆)转换来简化你的代码。【参考方案2】:

你有两个数组 new_array[i] = array[i] 不能满足你的需要。

您是否将realloc 视为可能的解决方案。

【讨论】:

+1,如果数组是相同的类型,那么这应该总是有效的。这假设您想要对指针进行浅拷贝。如果你想要一个深拷贝(复制底层池),你将不得不做的不仅仅是分配指针。 这是一个obj 的数组,但不是obj *。我不认为这是 tehwalrus 的意图。 @Jeff M 是的,它是一个结构数组,但如果他想要一个浅拷贝,它应该可以工作。 最终的问题是数组不是基于描述的正确类型。你有什么修复了这条线,但没有解决真正的问题。杰里很好地解决了这个问题,我认为考虑到了 tehwalrus 的实际意图。 谢谢,看起来 realloc 会产生一些进一步的优化。帽子一角! :)【参考方案3】:

只需分配值。 new_array[i] = array[i].

您可能遇到的问题是,obj* 实际上是一个指针 数组,obj 本身必须是一个指针类型:

typedef struct

  int value1;
 obj_pool;

typedef obj_pool* obj;

int main(int argc, char* argv[])

  obj_pool pool1;
  pool1.value1 = 5;
  obj array[] =  &pool1 ;
  array[0]->value1 = 16;
  return 0;

编译后会遇到的另一个问题是sizeof(array) == sizeof(obj*)NUM_ELEM(array)总是返回相同的值。这意味着您必须将 size_t array_size 参数传递给您的函数。

【讨论】:

顺便说一句,我不建议您使用 typedef =P 我建议您使用 obj**,就像常见的 C 语言一样。见杰里的回答。他的图表很漂亮。 为什么 NUM_ELEM 会失败? (我只是从网上抓取它,正如我所说,我还没有测试过它..)当然,如果 obj* blah 被分配了 malloc 作为数组指针,当你调用 sizeof 它返回的内存量是分配,而不是指针本身的大小?如果不是,我将需要使用持久变量(静态,在 c 中?)来跟踪数组大小,这是更混乱的代码。 !!看来您是正确的, NUM_ELEM 完全无法正常工作。我将不得不手动跟踪(grumble grumble)。 @tehwalrus:不是我理想的这个概念的链接,但是 - ***.com/questions/1975128/…。基本上,数组在传递给函数时会衰减为指针。处理这个问题的方法是: 1. 传递数组和大小参数。 2. 在数组中有一个标记值(例如,数组的最后一个元素必须始终设置为NULL,类似于用作字符串的空终止字符数组)。 3. 传递一个指向数组中第一个元素的指针和一个指向最后一个元素的指针,并在循环中使用current != end 谢谢,有趣的一点。我实际上构建了一个新的 struct obj_array,其中包含一个 obj**“array”和一个 int“count”;并且只需让此方法接收 obj_array* 并在最后增加 count 成员。如果我可以对其进行模板化/通用化,我会将其发布到网络上的某个地方,也可能会编写一个 ruby​​ 脚本来生成基于头文件的“数组”结构和方法。我们会看看我在项目结束时还剩下多少时间! :)【参考方案4】:

在您的代码中,数组元素不是结构上的指针,它们是结构对象。 this 数组 obj** 数组的元素是结构 obj 上的指针。

#define NUM_ELEM(x) (sizeof (x) / sizeof (*(x)))

void add_to_obj_array(obj* new_obj, obj** array)

  int number_of_elements = 0;
  if (array != NULL)
  
    number_of_elements = NUM_ELEM(array);
  

  // expand array with one more item
  array = (obj**)realloc(array, (number_of_elements + 1) * sizeof(new_obj));

  if (array == NULL )
  
    /* memory request refused :( */
    return;
  

  // Put new item at the last place on the array
  array[number_of_elements] = new_obj;

所以这里我们使用了矩阵(obj 结构指针上的指针)。当我们添加新元素时,我们只需将现有数组扩展为一个位置,然后在该位置放置新的结构对象。不需要返回值,因为我们对对象的指针进行操作,所有的更改都是在实际对象上完成的,而不是在它们的副本上。

【讨论】:

以上是关于在C中为结构指针数组成员分配地址的主要内容,如果未能解决你的问题,请参考以下文章

C 语言结构体 ( 结构体中嵌套一级指针 | 分配内存时先 为结构体分配内存 然后再为指针分配内存 | 释放内存时先释放 指针成员内存 然后再释放结构头内存 )

如何在c中为指向char的指针数组分配内存

C 语言结构体 ( 结构体中嵌套二级指针 | 为 结构体内的二级指针成员 分配内存 | 释放 结构体内的二级指针成员 内存 )

C++结构体指针数组如何分配空间,用new

C语言结构体里的成员数组和指针

c结构体里的数组与指针