我不理解C99标准中的以下未定义行为:
函数定义中调整后的形参类型不是对象类型(6.9.1)
根据标准,函数参数在两种情况下可以调整:
- 数组被调整为指针,
- 和函数被调整为指向函数的指针。
在第二种情况下,函数的调整后的形参确实不是对象(据我所知,标准是区分对象和函数的):
标识符可以表示一个对象;一个函数;的标记或成员结构、联合…
你能澄清这一点并提供一个这样的UB的例子吗?
C标准的第一个引用是不正确的。听起来像
-函数定义中调整后的形参类型不是完整对象类型(6.9.1)
你省略了complete
这个词。
例如,在与定义类型不同的函数声明中,可以指定不完整的对象类型,如
void f( size_t, size_t, int [][*] );
在这个函数声明中,第三个形参的声明不是一个完整的对象类型,因为数组元素的大小是未知的。
下面是一个示范程序
#include <stdio.h>
void f( size_t, size_t, int [][*] );
void f( size_t m, size_t n, int a[][n] )
{
for ( size_t i = 0; i < m; i++ )
{
for ( size_t j = 0; j < n; j++ )
{
a[i][j] = n * i + j;
}
}
}
void g( size_t, size_t, int [][*] );
void g( size_t m, size_t n, int a[][n] )
{
for ( size_t i = 0; i < m; i++ )
{
for ( size_t j = 0; j < n; j++ )
{
printf( "%d ", a[i][j] );
}
putchar( 'n' );
}
}
int main(void)
{
size_t m = 2, n = 3;
int a[m][n];
f( m, n, a );
g( m, n, a );
return 0;
}
输出为
0 1 2
3 4 5
程序中这两个函数声明
void f( size_t, size_t, int [][*] );
和
void g( size_t, size_t, int [][*] );
有一个不完全对象类型的形参声明。
你不能使用这样的声明,在同一类型是它的定义,例如
void f( size_t m, size_t n, int a[][*] )
{
// ...
}
因为在将第三个形参调整为指针后,编译器无法确定指针类型。即指针将具有不完整对象类型int ( * )[]
。
正如评论中指出的那样,标准中的文本在C11中进行了更正。它现在读(c11j .2):
-函数定义中调整的形参类型不是完整的对象类型(6.9.1)。
这更有意义。
然而,我想不出在函数定义的形参中使用不完整对象类型的例子可以在编译时没有错误。我所能想到的是,也许有些编译器允许未使用的形参具有不完整的对象类型。
正如@Lundin在评论中指出的,附录J是资料性的,而不是标准的规范性部分。在标准的规范性部分中引用的6.9.1节的文本也进行了更正。6.9.1/7最后一句的最后一句由"结果类型应为对象类型">改为"结果类型应为完整对象类型">。
@Lundin还指出,在函数定义中,调整后的参数类型为不完全类型违反了C11 6.7.6.3/4 (C99 6.7.5.3/4)的约束:
调整后,作为函数定义一部分的函数声明符的形参类型列表中的形参不能具有不完全类型。
在"约束条件"下列出的
因此需要对程序进行翻译以产生至少一个诊断。