【问题标题】:Pointers to arrays and effective types指向数组和有效类型的指针
【发布时间】:2021-11-04 18:47:25
【问题描述】:

我对 C 中指向数组的指针的有效类型感到困惑。通过指向数组的指针访问单个成员是否仅在该成员的内存上或在包含的所有内存上赋予有效类型大批?标准在这方面是否明确?

int ( *foo )[ 10 ] = malloc( sizeof( *foo ) );
**foo = 123; //Or ( *foo )[ 0 ] = 123

//Is the effective type of the memory for (*foo)[ 0 – 9 ] now also int?
//Does the whole region now have an effective type?
//Or can this memory be used for other purposes?

这是一个实际的例子:

int (*foo)[ 10 ];
double *bar;

//Figure out the size an int padded to comply with double alignment
size_t padded_int_size =
( ( ( sizeof( int ) + alignof( double ) - 1 ) / alignof( double ) ) * alignof( double ) );

//Allocate memory for one padded int and 1000 doubles,
//which will in any case be larger than an array of 10 ints
foo = malloc( padded_int_size + sizeof( double ) * 1000 );

//Set our double pointer to point just after the first int
bar = (double*)( (char*)foo + padded_int_size );

//Do things with ( *foo )[ 0 ] or **foo
//Do things with bar[ 0 - 999 ]

上面的代码会调用未定义的行为吗?

我在网上搜索,发现大多数关于聚合类型和有效类型的讨论都涉及结构指针,而不是指向数组的指针。即便如此,对于设置单个结构成员是否只为该成员或该结构将包含的整个内存块赋予有效类型似乎存在分歧和困惑。

【问题讨论】:

  • 关于“这方面的标准是否明确?”:没有。
  • 想象一下将所有双精度数据存储在一个特殊内存池中的计算机,该内存池只能由 FPU 访问。 FPU 无​​法访问普通内存。从/到“普通内存”的传输需要特殊的机器代码指令。你的指针双关会起作用吗?
  • @0___________ 考虑到函数必须返回适合与 any 类型一起使用的 连续 内存,这样的计算机能否首先提供 malloc?如果是这样,并且计算机因此可以决定在分配后的某个时间为 malloced 块的某些段使用特殊内存,那么我认为您的问题本质上与我要问的问题相同:是否通过指向一个成员的指针访问一个成员聚合类型允许编译器对所有其他成员和/或整个块进行类型假设?

标签: c pointers language-lawyer pointer-to-array


【解决方案1】:
//Is the effective type of the memory for (*foo)[ 0 – 9 ] now also int?

如果你问的是整个区域的有效类型是否是(一个)int,那么我认为很难对此进行论证。我倾向于拒绝,但正如 Eric 在 cmets 中所说,这里的语言规范并不清楚。

如果您要问*foo 的有效类型现在是否为int[10],那么有一个更简单的论点。我倾向于说“是”。如果该位置被接受,则在*foo 尾部的九个int 大小的子区域有一个更强有力的论据,每个子区域都具有有效类型int,但这不是一个无懈可击的位置。

//Does the whole region now have an effective type?

见上文。

//Or can this memory be used for other purposes?

这不是唯一的选择。无论前面问题的答案如何,任何或所有分配的对象都可以用于其他目的。没有声明类型的对象的有效类型可以通过写入该对象来重新分配。

//Do things with ( *foo )[ 0 ] or **foo
//Do things with bar[ 0 - 999 ]

上面的代码会调用未定义的行为吗?

foobar 的值的计算和分配都很好。其余的至少部分取决于所做的事情。如果您通过foo 写入全部或部分空间,然后通过bar 读回相同的空间,则行为未定义。其他动作组合的定义可能不太明确。

【讨论】:

  • "如果您通过 foo 写入全部或部分空间,然后通过 bar 读取相同的空间,则行为未定义。" 必须使用指向类型为 X 且长度为 N 的数组的指针。但是,我知道它指向的数组只需要存储一个元素,并且仅在索引 0 处。我想知道我是否可以将其他数据存储在数组其余部分的内存中(如果我分配足够多,则可以存储),或者我是否必须将其视为死空间。跨度>
  • @JacksonAllan,您是在为高度空间受限的环境写作吗?或者您是否在任何时候都拥有足够多的这些数组,您只需要一个元素,它们在聚合中是很大的?因为 9 ints 的空间对于对程序的正确性和可移植性的信心来说是很小的代价,即使是 9000 ints 在许多现代环境中仍然相当微不足道。
  • 基本思想是用额外的编译时元数据标记指针。目的是一个以可用性为中心的通用容器库,其原则包括用户不必预先声明任何使用宏的东西——即文件顶部没有DEFINE_LIST(int,float,int_float_map)——并且除非声明一个容器。当容器只需要与一种类型相关联时,可以通过将其隐藏在指向该类型的指针(用户句柄)后面来满足这些原则。但是像哈希表这样的容器需要关联两种类型...
  • @JacksonAllan,就您引用的响应者似乎可以肯定他们的解释而言,我认为确定性是错误的。而且我还认为,这本身就是保持清晰的一个很好的理由。即使你是对的,把你的帽子挂在对规范的精确解释上也会招来麻烦。
  • @tstanisl,这将是 C17 第 6.5/6 段(在早期版本的语言规范中类似):“如果通过具有类型的左值将值存储到没有声明类型的对象中那不是字符类型,则左值的类型将成为该访问以及不修改存储值的后续访问的对象的有效类型”。通过memcpymemmove 进行写入也有类似的规定。没有声明类型的对象包括所有分配的对象及其子对象,我认为标准 C 没有指定任何其他此类对象。
猜你喜欢
  • 2012-05-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-28
  • 1970-01-01
  • 2020-10-11
  • 2020-02-22
相关资源
最近更新 更多