Ste*_*gel 32 c pointers function language-lawyer c11
考虑以下C程序:
int f() { return 9; }
int main() {
int (*h1)(int);
h1 = f; // why is this allowed?
return h1(7);
}
Run Code Online (Sandbox Code Playgroud)
根据C11标准,Sec.6.5.16.1,在简单的指配中,"下列之一应持有",列表中唯一相关的一项如下:
左操作数具有原子,限定或非限定指针类型,并且(考虑左值操作数在左值转换后将具有的类型)两个操作数都是指向兼容类型的限定或非限定版本的指针,左侧指向的类型具有全部右边指出的那种限定词;
此外,这是一个"约束",这意味着,一致的实现必须报告诊断消息(如果它被违反).
在我看来,在上述程序的赋值中违反了这种约束.赋值的两端都是函数指针.所以问题是,两种功能类型是否兼容?第二节回答了这个问题.6.7.6.3:
对于要兼容的两种函数类型,两者都应指定兼容的返回类型.146)此外,参数类型列表(如果两者都存在)应在参数数量和省略号终止符的使用中一致; 相应的参数应具有兼容的类型.如果一个类型具有参数类型列表而另一个类型由函数声明符指定,该函数声明符不是函数定义的一部分并且包含空标识符列表,则参数列表不应具有省略号终止符,并且每个参数的类型应为与应用默认参数促销产生的类型兼容.如果一个类型具有参数类型列表而另一个类型由包含(可能为空)标识符列表的函数定义指定,则两者应在参数数量上一致,并且每个原型参数的类型应与类型兼容这是因为默认参数促销应用于相应标识符的类型.
在这种情况下,h1的类型之一具有参数类型列表; 另一个,f,没有.因此,上述引文中的最后一句适用:特别是"两者应在参数数量上达成一致".显然h1需要一个参数.f怎么样?以下几点出现在上述之前:
函数声明符中的空列表是该函数定义的一部分,指定该函数没有参数.
所以很明显f取0个参数.因此,两种类型的参数数量不一致,两种函数类型不兼容,并且赋值违反约束,应该发出诊断.
但是,编译程序时gcc 4.8和Clang都不会发出警告:
tmp$ gcc-mp-4.8 -std=c11 -Wall tmp4.c
tmp$ cc -std=c11 -Wall tmp4.c
tmp$
Run Code Online (Sandbox Code Playgroud)
顺便说一句,如果f被声明为"int f(void)...",两个编译器都会发出警告,但根据我对上述标准的阅读,这不应该是必要的.
问题:
Q1:赋值"h1 = f;" 在上面的程序中违反了约束"两个操作数都指向兼容类型的限定或不合格版本"?特别:
Q2:表达式"h1 = f"中的h1类型是某些函数类型T1的指向T1的指针.究竟什么是T1?
Q3:表达式"h1 = f"中的f类型是某些函数类型T2的指向T2的指针.究竟什么是T2?
问题4:T1和T2兼容吗?(请引用标准或其他文件的相应部分以支持答案.)
Q1',Q2',Q3',Q4':现在假设f的声明改为"int f(void){return 9;}".再次回答问题1-4这个程序.
这两个缺陷报告解决了您的问题:
缺陷报告316说(强调我的前进):
6.7.5.3#15中函数类型兼容性的规则没有定义何时函数类型"由 包含(可能为空)标识符列表的函数定义指定 ",[...]
它有一个与你给出的类似的例子:
void f(a)int a;{}
void (*h)(int, int, int) = f;
Run Code Online (Sandbox Code Playgroud)
然后它继续说:
我相信标准的意图是函数定义只为了检查同一函数的多个声明的兼容性而指定一个类型 ; 当此处函数的名称出现在表达式中时,其类型由其返回类型确定,并且不包含参数类型的跟踪.但是,实施解释各不相同.
问题2:上述翻译单元是否有效?
委员会的答案是:
委员会认为Q1和2的答案是肯定的
这是在C99和C11之间,但委员会补充说:
我们无意修复旧式规则.但是,本文件中的观察结果似乎是正确的.
到目前为止,我可以告诉C99和C11在你在问题中引用的部分没有太大的不同.如果我们进一步研究缺陷报告317,我们可以看到它说:
我相信C的意图是带有空括号的旧式函数定义不会为函数提供包含翻译单元其余部分原型的类型.例如:
Run Code Online (Sandbox Code Playgroud)void f(){} void g(){if(0)f(1);}问题1:这样的函数定义是否为函数提供了包含翻译单元其余部分原型的类型?
问题2:上述翻译单元是否有效?
委员会的回应是:
问题#1的答案是否定的,问题#2的答案是肯定的.但是,没有约束违规,如果执行函数调用,它将具有未定义的行为.见6.5.2.2; p6.
这似乎取决于这样一个事实,即函数定义是定义类型还是原型是不明确的,因此意味着没有兼容性检查要求.这最初是旧式函数定义的意图,委员会不会进一步澄清,因为它已被弃用.
委员会指出,仅仅因为翻译单位有效并不意味着没有未定义的行为.
Ken*_*lke -1
对于没有声明参数的函数,编译器不会推断出任何参数/参数类型。下面的代码本质上是一样的:
int f()
{
return 9;
}
int main()
{
return f(7, 8, 9);
}
Run Code Online (Sandbox Code Playgroud)
我相信这与支持可变长度参数的基本方式有关,并且 () 基本上与 (...) 相同。仔细观察生成的目标代码会发现,f() 的参数仍然被推送到用于调用函数的寄存器上,但由于它们在函数定义中被引用,因此它们根本不在函数内部使用。如果你想声明一个不支持参数的参数,这样写会更合适:
int f(void)
{
return 9;
}
int main()
{
return f(7, 8, 9);
}
Run Code Online (Sandbox Code Playgroud)
由于以下错误,此代码将无法在 GCC 中编译:
In function 'main':
error: too many arguments to function 'f'
Run Code Online (Sandbox Code Playgroud)