为什么在MISRA:2012中需要功能原型?

时间:2017-10-05 07:58:44

标签: c misra

我想知道为什么MISRA要求功能原型:2012。在下面的示例中,两个原型并不是必需的。

#include <stdio.h>
#include <stdlib.h>

// >>> Truly useless in my opinion
void display(void);
int main(void);
// <<<

void display(void) {
    printf("Hello World!\n");
}

int main() {
    display();
    return EXIT_SUCCESS;
}

我可以阅读的here这样的基本原理对我来说不是很清楚。例如,如果main在声明之前尝试访问display,则编译器或静态分析器将引发错误:声明之前使用的函数显示。

换句话说,为这个MISRA规则创建偏差是个好主意吗?

2 个答案:

答案 0 :(得分:3)

void display(void);是一个函数转发声明。它有原型格式。

如发布的链接所示,函数 prototype 是一个函数声明,其中包含指定的所有参数的类型。如果没有参数,那么参数列表必须是(void)(无参数)而不是()(任何参数)。

确切的规则8.2说:

  

规则8.2函数类型应为 prototype 形式,并带有命名参数

提供的基本原理(阅读它,它非常好)提到这是为了避免旧的K&amp; R和C90程序,而不是所有参数都指定。只要函数声明中的参数类型不与函数定义中的参数类型冲突,C99在某种程度上仍允许这样做。

基本上,该规则试图禁止这些功能:

void func1 (x)  // K&R style
int x;
{}

void func2(x)  // sloppy style
{}

所有参数(如果有)必须指定类型和名称。

然而,我在MISRA-C中找不到任何要求你为每个函数编写函数声明的内容。这意味着您的示例代码在有或没有函数声明的情况下符合此MISRA规则。

虽然正如我在之前的回答中提到的那样,编写没有函数声明的.c文件(原型格式)是一种邋practice的做法。如果需要按特定顺序调用函数,则应通过程序设计,函数命名和注释/文档使其显而易见。不是它们碰巧在.c文件中声明的顺序。

在.c文件中声明函数的源代码行与该函数的行为/用途之间不应该存在紧密耦合。

相反,应该按照逻辑上有意义的顺序定义函数。编写.c文件的一种常用方法是将所有公共函数保存在.c文件顶部的.h文件中。然后让内部函数(具有static /内部链接的函数)位于底部。该模型需要所有内部函数的函数声明。另一种选择是将所有内部函数放在最顶层,将公共函数放在底部。只要你保持一致,要么就好了。

最重要的是如果.c文件中的函数定义被重新排序,它不应该破坏程序或导致编译器错误。确保这一点的最简单方法是始终为程序中的每个函数提供函数声明。

请注意,文件顶部的函数声明根本不是“真正无用”,因为它们提供了C文件中存在的所有函数的快速摘要。这是一种编写自我记录代码的方法。

请注意,作为特殊情况,C标准不允许使用main()原型。

请注意,此外,规则8.7和8.8禁止您使用void display(void)而不使用static,因为该功能仅用于一个翻译单元。

答案 1 :(得分:2)

如果你没有声明这个函数,任何函数调用都会为每个参数调用default argument promotions,因为它被认为该函数具有C89标准的语义。

Case 1:

考虑调用f(5),其中函数的参数是double类型。 f的代码将5视为double,而默认的arith促销仅传递整数。

包含声明的头文件丢失可能会使代码传递整数,实际上是将它们视为导致seg错误的指针的函数。这是一个具体的例子:

Case 2:

假设您要使用strtok函数,并且未将标题string.h包含在声明中 - char *strtok(char *str, const char *delim)

现在你错误地考虑了分隔符'A'而不是“A”。因此,如果您忘记了签名但请记住参数的含义,如果您不包含标题,代码将在没有警告的情况下进行编译,当然strtok的代码会考虑您的char'A'(以整数转换) (= 95))作为实际论点。代码认为它是一个指向字符串的指针,并将尝试从完成segfault的位置95访问指针。

Case 3:

这是另一个代码段的典型代码,即使你没有犯任何错误,它仍然是段错误。

char *subtoken;
subtoken = strtok(str, delim, &saveptr);

在这种情况下,函数strtok(缺少来自string.h的声明)被视为返回int,因此从int->char*进行隐式转换。如果int表示为32位且指针位于64位上,则显然suboken的值将是错误的并且将产生seg错误。