C标准的哪一部分允许编译此代码?
我正在修复一些代码的bug,编译器(合法地)警告说没有声明函数C标准的哪一部分允许编译此代码?,c,C,我正在修复一些代码的bug,编译器(合法地)警告说没有声明函数dynscat(),这是其他人关于可接受的编码标准的想法,因此我跟踪了函数的定义位置(足够简单)和声明它的头(none;Grrr!)。但是我希望发现结构定义的细节对于qqparse\u val的extern声明是必要的: extern struct t_dynstr qqparse_val; extern void dynscat(struct t_dynstr *s, char *p); extern void qqcat(cha
dynscat()
,这是其他人关于可接受的编码标准的想法,因此我跟踪了函数的定义位置(足够简单)和声明它的头(none;Grrr!)。但是我希望发现结构定义的细节对于qqparse\u val
的extern
声明是必要的:
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()
函数声明完全丢失;再次,添加它可以平息警告
通过显示代码片段,很明显只使用了变量的地址,因此在某种程度上,结构的细节未知并不重要。是变量extern struct_dynstr*p_parseval代码>,你不会看到这个问题;这是100%的预期。如果代码需要访问结构的内部,那么就需要结构定义。但我一直认为,如果声明变量是一个结构(而不是指向该结构的指针),编译器会想知道结构的大小,但显然不是
我曾试图挑起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也可以)?哪个部门?或者我只是偶然发现了一个奇怪的例子,它可以在它移植到的所有机器上工作,但没有得到标准的正式认可?看起来像是一个获取不完整类型对象地址的例子
使用指向不完整类型的指针是完全合理的,每次使用指向void的指针时都会这样做(但没有人告诉过您:-)
另一种情况是,如果您声明类似
extern char a[];
您可以为a
的元素赋值也就不足为奇了,对吧?尽管如此,它仍然是一个不完整的类型,一旦您将这样一个标识符设置为sizeof
的操作数,编译器就会告诉您以下是我对标准(C11)的看法
第6.5.3.2节:地址和间接运算符
约束
第1段:一元运算符和运算符的操作数应为函数指示符、[]或一元运算符*的结果,或为左值,用于指定非位字段且未使用寄存器存储类说明符声明的对象
第2段:一元*运算符的操作数应为指针类型
在这里,除了对象(而不是位字段或寄存器)之外,我们不对对象指定任何要求
另一方面,让我们看看sizeof
6.5.3.4运营商的规模和规模
约束
第1段:sizeof运算符不得应用于具有函数类型或不完整类型的表达式、此类类型的括号名称或指定位字段成员的表达式。运算符的_Alignof不得应用于功能类型或不完整类型
在这里,标准明确要求对象不是不完整的类型
因此,我认为这是一种不被明确拒绝的情况。您拥有的是一种不完整的类型(ISO/IEC 9899:1999和2011-所有这些参考在这两种情况下都是相同的-§6.2.5¨22):
未知内容的结构或联合类型(如§6.7.2.3所述)不完整
类型
不完整的类型仍然可以是左值:
§6.3.2.1¨1(左值、数组和函数指示符)
左值是具有对象类型或除void之外的不完整类型的表达式
因此,它就像其他任何一元&
一样具有左值。您的行
extern struct t_dynstr qqparse_val;
是对象的外部声明,而不是定义。作为一个外部对象,它“有联系”,即外部联系
标准上说:
如果一个对象的标识符是在没有链接的情况下声明的,那么该对象的类型应该在其声明符的末尾完成
这意味着,如果它有链接,则类型可能不完整。因此,事后执行&qparse\u val
没有问题。由于对象类型不完整,您无法执行的操作将是sizeof(qqparse_val)
。关注第一行:
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 */
第二行看起来与原始行一样,但现在它指的是由于第一行而已经存在的类型
第一行是有效的,因为不透明结构就是这样做的
第二行之所以有效,是因为不需要完整的类型来进行外部声明
组合(第二行可以不使用第一行)之所以有效,是因为将类型声明与变量声明组合在一起通常是有效的。所有这些都使用相同的原则:
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\u val
,尽管其地理位置不同。需要声明才能“提及”某事物。
“使用”某物需要一个定义。
声明可能会提供一些有限的定义,如“int A[];”
什么使我感到难受
int f(struct _s {int a; int b;} *sp)
{
sp->a = 1;
}