C标准的哪个部分允许编译此代码?

时间:2012-08-30 15:12:25

标签: c

我修错了一些代码,并且编译器警告(合法地)函数dynscat()未被声明 - 别人对可接受的编码标准的想法 - 所以我追踪了函数定义的位置(很容易) )以及哪个标题声明了它(无; Grrr!)。但我期望找到extern qqparse_val extern struct t_dynstr qqparse_val; extern void dynscat(struct t_dynstr *s, char *p); extern void qqcat(char *s); void qqcat(char *s) { dynscat(&qqparse_val, s); if (*s == ',') dynscat(&qqparse_val, "$"); } 声明所需的结构定义细节:

qqcat()

原始代码中的dynscat()函数是静态的; extern声明会对此代码片段的编译器警告进行平息。 extern struct t_dynstr *p_parseval;函数声明完全丢失了;再次,添加它会消除警告。

通过显示代码片段,很明显只使用了变量的地址,因此在一个层面上有意义的是,结构的细节未知是无关紧要的。如果是变量gcc-4.7.1 -c -Wall -Wextra -std=c89 -pedantic surprise.c ,你就不会看到这个问题;这将是100%预期。如果代码需要访问结构的内部,那么将需要结构定义。但我总是期望如果你声明变量是一个结构(而不​​是结构的指针),编译器会想知道结构的大小 - 但显然不是。

我曾试图激怒GCC抱怨,但事实并非如此,即使是GCC 4.7.1:

{{1}}

代码已经在AIX,HP-UX,Solaris,Linux上进行了十年的编译,因此它不被GCC特定的接受。

问题

这是否允许C标准(主要是C99或C11,但C89也会这样做)?哪个部分?或者我只是碰到了一个奇怪的案例,该案例适用于移植到的所有机器,但未被标准正式批准?

6 个答案:

答案 0 :(得分:15)

你所拥有的是一种不完整的类型(ISO / IEC 9899:1999和2011 - 所有这些参考文献都是相同的 - §6.2.5¶22):

  

未知内容的结构或联合类型(如§6.7.2.3中所述)是不完整的   类型。

不完整的类型仍然可以是左值:

§6.3.2.1¶1(左值,数组和函数指示符)

  

左值是具有对象类型或除void之外的不完整类型的表达式; ...

因此,它就像任何其他具有左值的一元&一样。

答案 1 :(得分:8)

看起来像是一个不完整类型的对象地址。

使用指向不完整类型的指针是完全理智的,你每次使用指针到虚空时都会这样做(但是没有人告诉你: - )

另一种情况是你宣布像

这样的东西
extern char a[];

您可以分配a的元素,这对您来说并不奇怪吗?尽管如此,它仍然是一个不完整的类型,编译器会在您将此类标识符作为sizeof的操作数后立即告诉您。

答案 2 :(得分:5)

你的行

extern struct t_dynstr qqparse_val;

是对象的外部声明,而不是定义。作为外部对象,它“具有联系”即外部联系。

标准说:

  

如果声明对象的标识符没有链接,则对象的类型应在其声明符的末尾完成,...

这意味着如果它具有链接,则类型可能不完整。所以之后做&qqparse_val没有问题。

答案 3 :(得分:4)

必须声明“引用”某事。 定义是“使用”某些东西的必要条件。 声明可以提供一些有限的定义,如“int a [];” 让我感到困惑的是:

int f(struct _s {int a; int b;} *sp)
{
    sp->a = 1;
}

gcc警告'struct _s'在参数列表中声明。并声明“其范围仅限于此定义或声明,......”。但是,它不会在“sp-> a”上给出错误,该错误不在参数列表中。在编写'C'解析器时,我必须决定定义范围的结束位置。

答案 4 :(得分:3)

专注于第一行:

extern struct t_dynstr qqparse_val;

它可以分为创建类型和变量的单独步骤,从而产生这对等效的行:

struct t_dynstr; /* declaration of an incomplete (opaque) struct type */
extern struct t_dynstr qqparse_val; /* declaration of an object of that type */

第二行看起来和原作一样,但现在它指的是由于第一行已经存在的类型。

第一行有效,因为这就是不透明结构的完成方式。

第二行有效,因为您不需要完整的类型来执行extern声明。

组合(第二行在没有第一行的情况下工作)起作用,因为将类型声明与变量声明组合在一起工作。所有这些都使用相同的原则:

struct { int x,y; } loc; /* define a nameless type and a variable of that type */
struct point { int x,y; } location; /* same but the type has a name */
union u { int i; float f; } u1, u2; /* one type named "union u", two variables */

看起来有点好笑,extern后面紧跟着一个类型声明,就像你正试图让这个类型本身“extern”这是无稽之谈。但这并不意味着什么。 extern适用于qqparse_val,尽管它们存在地理分离。

答案 5 :(得分:2)

这是我对标准(C11)的想法。

  

第6.5.3.2节:地址和间接运算符

     

<强>约束

     

第1段:一元&amp;的操作数运算符应该是函数指示符,[]或一元*运算符的结果,或者是一个左值,它指定一个不是位字段的对象,并且不用寄存器存储类说明符声明。

     

第2段:一元*运算符的操作数应具有指针类型。

这里,我们没有对对象指定任何要求,除了它是一个对象(而不是一个位域或寄存器)。

另一方面,让我们来看看sizeof。

  

6.5.3.4 sizeof和_Alignof运算符

     

<强>约束

     

第1段: sizeof运算符不应该应用于具有函数类型或不完整类型的表达式,这种类型的带括号的名称,或者指定位的表达式 - 现场成员。 _Alignof运算符不应用于函数类型或不完整类型。

这里,标准明确要求对象不是不完整的类型。

因此,我认为这是一个未被明确拒绝的案例。