如果T'是不完整类型,是否将T指针转换为T'指针并返回产生原始指针?

时间:2016-07-20 09:49:24

标签: c pointers casting undefined-behavior

请考虑以下摘录,该摘录改编自the Tor source code

/* This can be a malloc wrapper with minimal initialization. */
other_t *make_other(void);

/* This struct is never defined. */
struct undef;
typedef struct undef undef_t;

undef_t *make_undef(void)
{
    other_t *other = make_other();
    return (undef_t*)other;
}

假设程序中的所有undef_t指针都是other_t个指针,并进一步假设所有使用undef_t*的程序在使用之前将它们转换为other_t*

根据C99标准的第6.3.2.3节,如果return未正确对齐为other的指针,则undef_t语句中的强制转换会调用未定义的行为,但如果它,将make_undef的结果转换回other_t*会产生make_other返回的原始指针。但是,undef_t是未定义的类型,我找不到任何关于这些的对齐规则。这些转换是否仍然像定义undef_t并且具有正确的对齐方式一样有效?

1 个答案:

答案 0 :(得分:3)

我不认为某种类型是否不完整会有所不同。

根据C11 6.2.5.22,只有三种可以实例化的不完整类型:

  1. 未知大小的数组类型
  2. 未知内容的结构类型
  3. 未知内容的联合类型
  4. 此外,根据C11 6.2.5.28:

      

    所有指向结构类型的指针都应具有相同的表示形式   对齐要求彼此。所有指向联合类型的指针   应具有与每个相同的表示和对齐要求   其他

    因此,对于结构或联合的指针的表示和对齐要求是已知的,无论类型是否完整,因为您仍然知道它是指向某种结构或联合的指针。

    对于数组,因为我们从C11 6.3.2.1.3中知道:

      

    一个类型为"类型"的数组的表达式被转换为   表达式为"指向类型"这指向了最初的   数组对象的元素

    然后我们可以得出结论,指向int的指针和指向int数组的指针必须具有相同的对齐要求,因为您可以使用它们来引用同一个对象。换句话说,所有指向int数组的指针都具有相同的对齐要求,无论大小是否已知。

    因此,如果你知道你有一个指向结构的指针,或者你知道你有一个指向联合的指针,或者你知道你有一个指向某个指定类型数组的指针,那么你就知道对齐要求是什么了。类型不完整的事实并不重要。实际上,如果这不是真的,那么不完整的类型就没用了,因为你永远无法可靠地使用它们的指针。

    在您的情况下,翻译单元中的undef_tother_t {em} {em>} ,因此您知道您的类型不完整,因此,您知道它的对齐要求,因此类型不完整的事实不会产生歧义。当然,如果你作为程序员使用他们而不必为自己找到这个,那么你仍然会遇到问题,例如typedef是一个指针union和other_t是指向结构的指针。但这只是在指针之间进行转换的一个正常问题,它可能具有不同的对齐要求 - 由于一个或多个指向类型不完整,所以没有任何关于这个问题的问题变得更加困难。

    编辑 - 根据评论进一步阐明:

    A"指向不完整类型的指针"只能指向一个完整类型的对象,因为根据定义显然不能实例化不完整的类型(为了简洁起见,我将undef_t任意数量的内存和malloc()的可能性放在一边指向不完整类型的指针)。

    为了能够拥有和传递指向聚合和复合类型的指针而不需要提供类型定义,存在指向不完整类型的指针。换句话说,当您没有足够的信息来实例化特定类型时,但您确实有足够的信息可以指向一个类型。不透明类型在此基础上工作,其中所有实际工作都在类型定义 可用的函数中完成,而使用这些类型的代码只需要能够存储对象的地址,它可以传递给这些函数。

    在对齐方面,指针的表示可以根据它指向的对象的对齐要求而改变。例如,正如注释中所指出的,如果所有int必须存储在8字节边界上,那么指针表示可能不会存储3个最低有效位,因为它们总是为零。但是,如果您然后尝试将char指针转换为int指针并返回,则可能会丢失信息,因为char指针必须能够指向单个字节,并且在此假设转换为int *时,该信息将丢失。

    类似地,可能会出现一些小结构可能在4字节边界上对齐,而在32字节边界上对齐较大的结构。但是对于结构指针的对齐要求必须都是相同的,正是因为这些指向不完整类型的指针。在您使用指向不完整结构类型的指针的地方,您没有可用的类型定义(如果您这样做,它将是一个完整的类型),因此您不知道您是否可以例如,忽略最低有效5位,或仅忽略最低有效3位。因此,指向不完整结构类型的指针的对齐要求必须足够宽松,以便它们可以正确地保持任何可想到的结构类型的位置。为了使这些更容易传递,我们可以从上面的引用中看到C需要所有指向结构类型的指针才能具有相同的对齐要求。例如,某些结构实际上仍然可以在32字节边界上 对齐,但是不能允许指向该结构的指针充分利用它,并且它必须能够保持任何结构的位置类型。

    但是,例如,如果所有结构类型都在不小于4字节的边界上对齐,那么对于结构指针(包括指向不完整结构类型的指针)来说,忽略最不重要的3位是完全正常的,因为你可以在确保您可以安全地表示任何结构的位置的同时这样做。事实上,C要求所有指向结构类型的指针具有相同的对齐要求,并且要求所有指向联合类型的指针具有相同的对齐要求,但要求指针的对齐要求结构类型与指向联合类型的指针的对齐要求相同,表示指向结构的指针可能依赖于最小4字节边界,但是指向联合的指针依赖于最小8字节边界。在这种情况下,您无法安全地将指向不完整结构类型的指针转​​换为指向不完整联合类型的指针,然后再返回。

    这就是为什么说不正确,正如另一个答案目前所做的那样,"对于不完整类型没有对齐要求"。但它确实意味着不完整的类型对于原始问题没有问题,因为正如已经解释的那样,当你有一个不完整的类型时,你至少知道它是一个结构,一个联合,还是一个数组,您需要完全了解对齐要求。