C标准的哪个部分允许这个代码编译?
我dynscat()
一些代码,编译器警告(合法),函数dynscat()
没有被声明 – 别人的想法是一个可接受的编码标准 – 所以我跟踪了函数的定义(很简单)和哪个头声明它(没有; Grrr!)。 但是我希望能够findqqparse_val
的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()
函数是静态的; extern声明平息了这段代码的编译器警告。 dynscat()
函数声明完全丢失; 再加上它平息警告。
通过显示的代码片段,很明显只有variables的地址被使用,所以在一个层次上有意义的是,结构的细节是不知道的。 是variablesextern struct t_dynstr *p_parseval;
,你不会看到这个问题; 这将是100%的预期。 如果代码需要访问结构的内部,那么就需要结构定义。 但是我总是希望如果你声明variables是一个结构(而不是指向结构的指针),编译器会想知道结构的大小 – 但显然不是。
我试图挑起GCC抱怨,但它不,甚至GCC 4.7.1:
gcc-4.7.1 -c -Wall -Wextra -std=c89 -pedantic surprise.c
该代码已经在AIX,HP-UX,Solaris和Linux上编译了十年,所以它不被GCC所接受。
题
这是由C标准(主要是C99或C11,但C89也可以)吗? 哪一部分? 还是我碰到一个奇怪的案例,它在所有移植的机器上运行,但是没有得到标准的正式批准?
看起来像是一个采取不完整types的对象的地址的情况。
使用指向不完整types的指针是完全正常的,每当你使用void指针时(但没有人告诉过你:-)
另一种情况是,如果你声明类似的东西
extern char a[];
你可以分配到一个元素,这是不足为奇的吗? 尽pipe如此,这是一个不完整的types,编译器会告诉你一个sizeof
的操作数。
你所拥有的是一个不完整的types(ISO / IEC 9899:1999和2011 – 所有这些参考文献在§6.2.5¶22中都是一样的):
未知内容的结构或联合types(如第6.7.2.3节所述)是不完整的types。
一个不完整的types仍然可以是一个左值:
§6.3.2.1¶1(左值,数组和函数指示符)
左值是具有对象types或不为空的不完整types的expression式; …
因此,它就像任何其他的一元&
一个左值。
你的线
extern struct t_dynstr qqparse_val;
是对象的外部声明,而不是定义。 作为一个外部对象,它“有联系”,即外部联系。
标准说:
如果一个对象的标识符被声明为没有链接,则该对象的types应该在它的声明符的末尾完成,…
这意味着,如果它有联系,types可能是不完整的。 所以在执行&qqparse_val
之后没有问题。 因为对象types不完整,所以你不能做的是sizeof(qqparse_val)
。
声明是必要的,以“引用”某事。 定义是“使用”某些东西的必要条件。 声明可以提供一些有限的定义,如“int a [];” 困扰我的是:
int f(struct _s {int a; int b;} *sp) { sp->a = 1; }
gcc警告'struct _s'是在参数列表中声明的。 并规定“其范围仅限于此定义或声明,…”。 但是它不会在参数列表中的“sp-> a”上发生错误。 在编写“C”parsing器时,我必须决定定义范围的结束位置。
专注于第一行:
extern struct t_dynstr qqparse_val;
它可以分为创buildtypes和variables的单独步骤,从而产生这样的等效线对:
struct t_dynstr; /* declaration of an incomplete (opaque) struct type */ extern struct t_dynstr qqparse_val; /* declaration of an object of that type */
第二行看起来就像原来的一样,但现在是指由于第一行已经存在的types。
第一行工作,因为这是多么不透明的结构完成。
第二行工作,因为你不需要一个完整的types做一个extern声明。
这个组合(第二行没有第一行)是可行的,因为把一个types声明和一个variables声明结合在一起工作。 所有这些都使用相同的原则:
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
跟在一个types声明之后,就像你试图把types本身“extern”这是无稽之谈。 但这不是这个意思。 尽pipe地理分离, extern
也适用于qqparse_val
。
这是我的想法相对于标准(C11)。
6.5.3.2节:地址和间接操作符
约束
第一段 :一元&运算符的操作数应该是一个函数标识符,一个[]或一元运算符的结果,或者一个左值,它指定一个不是位域的对象,并且不用寄存器存储类说明符。
第2段 :一元运算符的操作数应该有指针types。
在这里,我们没有指定对象的任何要求,除了它是一个对象(而不是一个位域或寄存器)。
另一方面,让我们看看sizeof。
6.5.3.4 sizeof和_Alignof运算符
约束
第1段: sizeof运算符不能用于具有函数types或不完整types的expression式,不适用于这种types的括号内的名称或指定位域成员的expression式。 _Alignof运算符不能应用于函数types或不完整types。
在这里,标准明确要求对象不是不完整的types。
因此,我认为这是一个没有被明确否认的情况。