所有结构标识符都自动向前声明

时间:2015-06-09 07:37:34

标签: c struct c99 incomplete-type ansi-c

在回答warning: assignment from incompatible pointer type for linklist array时,我发现任何使用struct关键字的未声明标识符都被视为前向声明的标识符。

例如,program below编译良好:

/* Compile with "gcc -std=c99 -W -Wall -O2 -pedantic %" */
#include <stdio.h>

struct foo 
{
    struct bar *next;  /* Linked list */
};


int main(void) {
    struct bar *a = 0;
    struct baz *b = 0;
    struct foo c = {0};

    printf("bar -> %p\n", (void *)a);
    printf("baz -> %p\n", (void *)b);
    printf("foo -> %p, %zu\n", (void *)&c, sizeof c); /* Remove %zu if compiling with -ansi flag */
    return 0;
}

我的问题:哪条规则指导C编译器将未声明的struct identifier视为前向声明的不完整struct类型?

4 个答案:

答案 0 :(得分:10)

标准说(6.2.5.28

  

所有指向结构类型的指针都应具有相同的表示和对齐要求。

这意味着编译器知道如何表示任何结构的指针,甚至是那些尚未定义的结构。
您的程序只处理指向此类结构的指针,所以没关系。

答案 1 :(得分:7)

在6.2.5类型和6.7.2.3标签中描述。

struct identifier是一种对象类型。

  

6.2.5类型

     
      
  1. 存储在对象中或由函数返回的值的含义由   用于访问它的表达式的类型。 (声明为对象的标识符是   最简单的表达;类型在标识符的声明中指定。)类型   被分为对象类型(描述对象的类型)和函数类型(类型   描述功能)。 在翻译单元内的各个点处,对象类型可以是   不完整(缺乏足够的信息来确定该类型物体的大小)或   完成(有足够的信息)。 37)
  2.         

    37)整个翻译单元中的类型可能不完整或完整,或者可能会改变状态   翻译单元内的不同点。

         
        
    1. 未知大小的数组类型是不完整类型。它已完成,标识符为   该类型,通过在后面的声明中指定大小(具有内部或外部链接)。   未知内容的结构或联合类型(如6.7.2.3中所述)是不完整类型。对于该类型的所有声明,通过声明相同的结构或完成来完成   union标记及其定义内容稍后在同一范围内。
    2.   

  

6.7.2.3标签

     
      
  1. 具有相同范围和结构的结构,联合或枚举类型的所有声明   使用相同的标签声明相同的类型。无论是否有标签或什么   该类型的其他声明属于同一个翻译单元,类型不完整129)   直到定义内容的列表的右括号后立即完成   此后。
  2.         

    129)只有在不需要该类型对象的大小时才能使用不完整类型。它不是   例如,当typedef名称被声明为结构或联合的说明符时,或者   当声明返回结构或联合的指针或函数时。(参见不完整的类型   在6.2.5中。)在调用或定义这样的函数之前,必须完成规范。

答案 2 :(得分:6)

除了2501提供的答案,以及您对它的评论“在我的情况下,甚至没有前瞻性声明”,以下内容。

任何使用struct tag都算作结构类型的(前向)声明,如果之前没有声明的话。虽然更正式的方式是说这只是一种类型,因为C标准没有提到“结构类型的前向声明”,只需完成和incomplete structure types (6.2.5p22)

6.7.2 Type specifiers告诉我们 struct-or-union-specifier 类型说明符6.7.2.1 Structure and union specifiers paragraph 1告诉我们转 struct 标识符 struct-or-union-specifier

假设您有一个链接列表声明,例如

struct node {
    struct node *next;
    int element;
};

然后,这种不完整类型的“隐含前向声明”对于这种结构起作用至关重要。毕竟,在终止分号处输入struct node is only complete类型。但是你需要引用它才能声明next指针。

此外,struct node声明(不完整类型)可能超出范围,就像任何其他声明一样。例如,如果您有一些原型

,就会发生这种情况
int function(struct unknown *parameter);

其中struct unknown在声明结束时立即超出范围。任何进一步声明的struct unknown都与此不同。 6.2.5p22

的内容暗示了这一点
  

未知内容的结构或联合类型(如6.7.2.3中所述)   是一种不完整的类型。对于所有声明,它已完成   通过声明相同的结构或联合标记及其定义来输入类型   以后的内容在同一范围内

这就是为什么gcc警告这个:

foo.c:1:21: warning: 'struct unknown' declared inside parameter list
foo.c:1:21: warning: its scope is only this definition or declaration, which is probably not what you want

你可以通过在它之前添加一个额外的前向声明来解决这个问题,这会使范围更早开始(因此会在之后结束):

struct unknown;
int function(struct unknown *parameter);

答案 3 :(得分:2)

我认为使用不完整结构类型的最优雅的用例是这样的:

struct foo 
{
    struct bar *left;
    struct bar *right;
};
struct bar
{
    int something;
    struct foo *next;
};

即。双递归,其中b和b指向a。 此类情况可能是此功能包含在原始C语言规范中的原因。

最初的问题是是否所有结构标识符都自动转发。我认为最好说所有不完整的结构定义都被自动视为前向声明

编辑:关于文档的评论,让我们看看C语言圣经:Kerninghan&amp; Ritchie - C编程语言,“6.5自引用结构”部分说:

  

偶尔,人们需要一种自我参照结构的变体:   两个相互引用的结构。处理这个问题的方法是:

struct t {
    ...
    struct s *p;   /* p points to an s */
};
struct s {
    ...
    struct t *q;   /* q points to a t */
};

我同意,有可能以另一种方式实现,但我认为这是C语言作者的良好动机,我同意他们这是实现这一点的优雅方式。