DCL31-C. 在使用之前先声明标识符

DCL31-C. 在使用之前先声明标识符

C11标准强制要求指定类型标识符并禁止隐式函数声明。C90标准允许变量和函数的隐式类型(implicit typing)。结果时一些遗留代码使用了隐式类型。一些C编译器为了支持遗留代码允许了隐式类型,但是在新代码中不应该使用隐式类型。

不遵从规范的代码示例 (隐式int)

C语言不再允许在声明中缺失类型标识符。C语言标准第6.7.2节 [ ISO/IEC 9899:2011 ] 陈述如下:

At least one type specifier shall be given in the declaration specifiers in each declaration, and in the specifier-qualifier list in each struct declaration and type name.

这个不遵从规范的代码示例忽略了类型标识符:

extern foo;

一些C编译器实现 并不会给这个违反限制发出诊断信息。这些不遵从规范的C翻译器继续将这样的声明默认为类型int

遵从规范的解决方案 (隐式 int)

这个遵从规范的方案显示包含了一个类型标识符:

extern int foo;

不遵从规范的代码示例 (隐式函数声明)

隐式声明函数是不允许的;每个函数被调用之前必须被显式声明。在C90标准中,如果一个没有显式原型声明的函数被调用了,那么编译器会提供一个隐式声明。

C90标准[ISO/IEC 9899:1990]包括这个要求:

If the expression that precedes the parenthesized argument list in a function call consists solely of an identifier, and if no declaration is visible for this identifier, the identifier is implicitly declared exactly as if, in the innermost block containing the function call, the declaration extern int identifier(); appeared.

如果一个函数声明在函数调用时还不可见,那么C90的平台会认为函数有一个形如extern int identifier();的隐式声明。

这个声明意味着函数可以接收任意个数量和类型的参数并返回int类型。

在这个不遵从规范的示例代码中,如果malloc()没有被声明,C90的编译器可能会隐式的将malloc()声明为int malloc()。如果平台的int大小是32位,但是指针是64位的,那么返回的结果指针可能会被截断,最终返回一个32位的整型值。

#include 
/* #include  is missing */
  
int main(void) {
  for (size_t i = 0; i < 100; ++i) {
    /* int malloc() assumed */
    char *ptr = (char *)malloc(0x10000000);
    *ptr = 'a';
  }
  return 0;
}

遵从规范的解决方案 (隐式函数声明)

这个遵从规范的方案通过包含对应的头文件声明了malloc():

#include 
  
int main(void) {
  for (size_t i = 0; i < 100; ++i) {
    char *ptr = (char *)malloc(0x10000000);
    *ptr = 'a';
  }
  return 0;
}

更多关于函数声明的信息可参见 DCL07-C. Include the appropriate type information in function declarators.

不遵从规范的代码示例 (隐式返回类型)

不要声明一个隐式返回类型的函数。比如,如果一个函数要返回一个有意义的整型值,那么声明为返回int; 如果不需要返回有意义的值,声明为返回void

#include 
#include 
  
foo(void) {
  return UINT_MAX;
}
 
int main(void) {
  long long int c = foo();
  printf("%lld\n", c);
  return 0;
}

上面的代码中,因为编译器认为foo()返回int类型,所以UINT_MAX会被错误的转换为-1

遵从规范的解决方案 (隐式返回类型)

这个方案中foo()函数显示返回了unsigned int类型,结果是函数正确地返回了UINT_MAX.

#include 
#include 
 
unsigned int foo(void) {
  return UINT_MAX;
}
 
int main(void) {
  long long int c = foo();
  printf("%lld\n", c);
  return 0;
}

风险评估

因为隐式声明导致不严格的类型判别,可能会引入非预期和错误行为。在现有代码中发生忽略类型是很少见的,结果一般也不严重,可能会导致程序异常终止.

Rule Severity Likelihood Remediation Cost Priority Level
DCL31-C Low Unlikely Low P3 L3

你可能感兴趣的:(DCL31-C. 在使用之前先声明标识符)