【问题标题】:How to allocate memory dynamically when array is declared with 1 element使用 1 个元素声明数组时如何动态分配内存
【发布时间】:2014-12-14 02:24:51
【问题描述】:

考虑如下结构:

typedef struct { int arrCount; int arr[1]; } SampleStruct, *PSampleStruct;

我知道arr 是一个 int 数组,需要在运行时动态分配内存,arrCount 需要保存元素的计数。但是,当我尝试使用 malloc 分配 10 个元素的内存时,编译器会抛出 arr must be a modifiable value 的错误。我无法确定如何为这样的数组分配内存。另外,我在很多 Windows 头文件中都看到过这样的例子,但是当我尝试实现它时,我完全迷失了。

【问题讨论】:

  • 代替int arr[1],使用int *arr。然后将 malloc 用于 arr
  • @Neo 为 arr 使用 int 指针/单独分配将 arr 的生命周期与 SampleStruct 的生命周期分开,并且在非连续内存中。正确的方法是在单个 malloc/alloca 中分配一个指向 SampleStruct 的指针。
  • @Sanhadrin 您的“正确”方式并不严格符合 C,因为您最终访问的对象超出了 sizeof 对象。单独分配没有什么“不正确”的。我的回答提供了两者。 (并且使用偏移量“正确”地进行单一分配。)
  • 谢谢吉姆。我指的是你在下面写的。
  • @Jim Balter 当标准本身定义不明确时,严格遵守标准在许多情况下是完全没有必要的,这就是其中之一。数组和指针的定义行为使其在功能上正确且可在地球上每个远程使用的编译器之间移植。 C 标准委员会说“这可行,但感觉很恶心”,并指出当实现要求使其完全可预测时,它是未定义的行为,这意味着您必须盲目地遵循标准才能看到任何问题。或者 - 这在哪个编译器上不起作用?

标签: c windows visual-c++ dynamic-memory-allocation


【解决方案1】:

虽然arr[1] 不是指针,但PSampleStruct 是。你会做这样的事情:

PSampleStruct ss10arr = malloc(sizeof (*ss10arr) + (sizeof(ss10arr->arr) * 9)) 这将使arr[0]arr[9] 有效。

请注意,这只有效,因为arr 位于结构的end

另请注意,如果您尝试访问后续元素,某些编译器选项(例如带有 glibc 和 gcc 的 -D_FORTIFY_SOURCE)会报错,因为它会将其检测为溢出。

【讨论】:

  • 虽然我明白了你的意思,但你真的认为ss10arr 可以在你的代码右侧使用,而变量还没有声明吗?只是一个想法。
【解决方案2】:

以这种方式做事时,您通常不会分配数组本身 - 您使用 malloc/alloca 分配结构,其大小为 sizeof(SampleStruct) + arr[] 的分配大小 - 然后是 arr[]用于索引分配的内存。

请参阅GCC's description of zero-length arrays - 对于长度为 1 的数组,它应该是相同的,但在内存使用的意图上更清楚。

【讨论】:

  • 标准 C 没有零长度数组。
  • @Jim Balter 我已经说过了 - 模式是完全相同的,减去从 malloc 中取出一个字节。它强化了前面的观点,并深入探讨了它,而无需重新覆盖它。
  • 您的答案中显然没有声明 C 没有零长度数组。而“一个byte”就大错特错了。
  • C99 允许零长度数组,或“灵活”数组;至少根据gcc.gnu.org/onlinedocs/gcc/Zero-Length.html,尽管我不确定 VC 或 C++/VC++ 对此的支持程度如何。不过,出于习惯,我会尽量保持 C89 兼容
  • @mnunberg 灵活数组不是零长度数组;正如你的引文所说,他们有不完整的类型。但它们是这里应该使用的(如果 OP 使用的是 C99 或更高版本的编译器,微软愚蠢地不提供)。
【解决方案3】:
SampleStruct* pstruct = malloc(offsetof(SampleStruct, arr) + 10 * sizeof *pstruct->arr);

将分配一个指向 arr 中具有 10 个元素的 SampleStruct 的指针。这几乎适用于所有实现,但并不严格符合。为了严格遵守,改变

int arr[1];

int* arr;

然后做:

SampleStruct* pstruct = malloc(sizeof *pstruct);
pstruct->arr = malloc(10 * sizeof *pstruct->arr);

编辑:如果您使用的是 C99(或更高版本)编译器(遗憾的是,Microsoft 不是),您可以使用 灵活数组。改变

int arr[1];

int arr[];

然后就做

SampleStruct* pstruct = malloc(sizeof *pstruct + 10 * sizeof *pstruct->arr);

【讨论】:

  • 这里为什么需要offsetof
  • @mnunberg offsetof 正确排除了 arr[1] 的大小。它比sizeof *pstruct - sizeof pstruct->arr 更简单。 (或者从答案中的计数中减去 1,其中包含语法错误。)
  • 我明白为什么offsetof 变体可能更好地传达意图(尽管我认为这是个人喜好问题)。在任何情况下offsetof 变体会产生与malloc(sizeof *foo + ((sizeof foo->arr) * count-1)) 相比不同的结果?
  • @mnunberg 是的......你的表达式被错误地插入并产生错误的值。良好的编程实践不仅仅是“个人偏好”的问题。如果你修复它......有人将它更改为 arr[0] (gcc) 或 arr[] (C99)......然后它不会分配足够的内存,从而导致一个微妙的错误和 UB。使用 offsetof 是 DRY 原则的一个实例......它避免了知道 arr 的维度,这只是一个工件。
  • 至少有一个完全没有根据的反对票来自 Adam Palaniuk,他是出于恶意(见下文)。
猜你喜欢
  • 2018-12-28
  • 2022-11-12
  • 2018-09-17
  • 1970-01-01
  • 2021-07-14
  • 1970-01-01
  • 1970-01-01
  • 2017-07-21
  • 1970-01-01
相关资源
最近更新 更多