【问题标题】:Pointer alingment and array with length storage指针对齐和带有长度存储的数组
【发布时间】:2016-04-19 12:57:42
【问题描述】:

我发现了一个数组的以下实现,它的长度存储在“内部”。代码如下:

double* construct(const size_t nElements)
{
  double* arr = malloc(sizeof(double) * nElements + sizeof(int));
  int i = 0;

  *(int*)arr = nElements;
  ((int*)arr)++;

  for (i = 0; i < nElements; i++) arr[i] = 1.0 * i;      

  return arr;
}

void destroy(double* arr)
{
  free((int*)arr - 1);
}

void print(double* arr)
{
  int N = *((int*)arr - 1);
  int i = 0;

  printf("Supplied array of %d elements:\n", N);

  for (i = 0; i < N; i++) printf("%d : %f\n", i, arr[i]);      
}

int main(void)
{
  double* a = construct(10);

  print(a);    
  destroy(a);

  return 0;
}

这项技术背后的想法是分配一个带有额外空间的内存块来存储数组的长度,并且只为用户提供实际数据开始的指针。然而,我怀疑这个特定的实现在指针转换方面有些可疑(从 double 到 int*)。

这一般允许吗?

如果是,它在指针对齐方面是如何工作的?

有更好的方法吗?

更新: 澄清一下,我看到这段代码是一个“黑客”,我知道这很糟糕。我只是想知道什么会破坏以及为什么会破坏。

给定答案的小结: 线((int*)arr)++;

1) 格式错误,只能在 MSVS 下编译

2) 产生一个 sizeof(int) 偏移量,这会导致以下地址被双边界错位。

这很清楚,但提出了另一个问题:是否允许将自身从 double* 转换为 int*?对痛觉有任何影响吗?

【问题讨论】:

  • 这是一个可怕的黑客攻击。使用带有灵活数组成员的struct。
  • 这当然是有问题的。但是,您可以简单地拥有一个带有表示大小的整数的结构(为此使用size_t)和double 的灵活数组成员。
  • 双精度数组可能没有正确对齐。除此之外,代码似乎已定义。也许指针运算应该使用 unsigned char 完成。
  • 为什么还是double?
  • ((int*)arr)++; 格式不正确; ++ 只能应用于左值。也许你的意思是arr = (double *)((int *)arr + 1);

标签: c arrays pointers memory-management


【解决方案1】:

您发布的代码无效。原因很简单:malloc() 返回内存对齐机器上最大的通用类型,这对于double 来说已经足够了,但是返回的内存在单个int 之后用于存储doubles。在double 为 64 位且int 为 32 位(这很常见)的平台上,这不是有效代码,并且可能会崩溃或表现出比预期更差的性能。

【讨论】:

  • 能否请您详细说明为什么这段代码无效(我也很怀疑,但既不知道原因,也不知道技术细节)?
  • @HighPredator 因为跳过小于double 的int 意味着数组中的双精度现在被误用了,至少跳过整个double 的值或一般情况下:跳过与您分配的类型对齐的相同字节数(或 int 以较大者为准)
  • @BeyelerStudios [s]怎么会这样?[/s]我会改写:如果“双打”首先未对齐(例如,原始指针按 int 边界对齐)怎么办?或者这就是第一次分配给双指针的效果(分配后)?
  • @BeyelerStudios:doubles 不一定存在未对齐的访问。这取决于平台。
  • @HighPredator malloc() 返回适合所有类型对齐的内存。如果您在前面存储int,则可能不再存在。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-09
  • 1970-01-01
  • 2015-07-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-01
相关资源
最近更新 更多