在C语言中初始化循环数据。这段代码是否符合任何标准的C代码?

16

我想知道是否可以将全局变量初始化为指向自身:

#include <stdio.h>
struct foo { struct foo *a, *b; } x = { &x, &x };
int main()
{
    printf("&x = %p, x.a = %p, x.b = %p\n", &x, x.a, x.b);
    return 0;
}

这段代码可以在 gcc 下编译并执行(所有三个指针打印结果相同)。

我想知道:

  1. 这可靠吗?
  2. 这是标准的吗?
  3. 这是可移植的吗?

编辑: 仅澄清一下,我怀疑在其自身初始化程序中是否可以使用 x 的地址。


有一个问题:%p 期望一个 void*,但参数是 struct foo* - Daniel Fischer
@DanielFischer,C语言允许从任何其他指针进行静默转换到void *,并且允许从void *进行转换以使得函数如malloc可以在没有显式转换的情况下工作,是这样吗? - Matt
1
它确实会在赋值时发生,但是当将东西传递给 printf 时,不会发生这种转换(变长参数,根本没有类型检查)。因此,printf 得到的是 struct foo*,但格式字符串需要 void*。如果您提高警告级别,编译器应该会警告您。严格来说,这甚至是未定义的行为,尽管它在大多数常见平台上都能正常工作(标准并不保证所有指针具有相同的大小和结构,但通常它们是相同的)。 - Daniel Fischer
1
@DanielFischer 非常有趣。我一直认为指针的大小保证是相同的(除了C++中的成员函数指针,但这不是C++)。 - Matt
很不幸,它们并没有。在一些老旧的平台上,指针的大小是不同的(有些平台拥有不同大小的近指针和远指针)。这是我很快找到的链接:https://dev59.com/E3RC5IYBdhLWcg3wJNgN#399999。 - Daniel Fischer
还有这个:https://dev59.com/D2865IYBdhLWcg3wLrlE#3941817 - Daniel Fischer
2个回答

8
以上所有内容都是正确的。您初始化了几个指针,它们都持有相同的地址,这与您初始化它们的地址相同。
更有趣的是,x.a 也保证指向自身(即,结构的第一个元素保证在结构的开头,因此将指向结构的指针转换为第一个元素的类型,保证指向那个第一个元素)。

我所质疑的是在初始化程序中是否可以获得 x 的地址。另外,你说得对,x.a 指向了与其本身相同的位置,但类型不同,x.a 是一个 struct foo * 应该指向一个 struct foo ,这与其本身不是同一种类型。 - Matt
@Matt:是的,地址是一个rvalue(本质上是一个常量),所以从程序的角度来看,这与类似于int x = 1234;的东西没有太大区别。 - Jerry Coffin

8

这是标准的C代码。

这个强大的标准允许它(重点在于此):

(C99,6.2.1p7)“结构体、联合体和枚举标记具有作用域,在类型说明符中声明标记后出现的位置之后开始。每个枚举常量的作用域在其定义枚举器出现在枚举器列表中之后开始。任何其他标识符的作用域,在其声明符完成后立即开始。

请注意,为了说明6.2.1p7的最后一句话,Derek M. Jones的书籍“ The New C Standard”使用类似于您的示例的例子。

struct T {struct T *m;} x = /* declarator complete here. */ {&x};

谢谢,这正是我正在寻找的答案! - Matt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接