【问题标题】:Why is C++ more restrictive regarding forward declaration of function prototypes (signatures)? [closed]为什么 C++ 对函数原型(签名)的前向声明更加严格? [关闭]
【发布时间】:2018-03-20 14:25:43
【问题描述】:

我注意到 C++ 在使用之前声明函数签名方面比 C 更严格,即使函数定义是在实际调用它们的函数之后声明的?

我一直认为 C 的限制性更强,但似乎并非如此。

为什么在制定 C++ 编程语言标准时理念发生了变化?

例如,以下代码在gcc 命令上运行编译良好,但在尝试使用g++ 编译时输出错误

#include<stdio.h>

int main()
{
    int a=sum(4,6);
    printf("%d",a);
    return 0;
}

int sum(int a,int b)
{
    return a+b;
}

错误是

‘sum’ was not declared in this scope

【问题讨论】:

  • 首先需要记住的是,C 和 C++ 是两种完全不同的语言。一旦你记住,不比较两者应该很容易,即使它们在很多事情上都有相似的语法。
  • "compiles fine" 是 "compiles with warnings" - 这对我来说不符合 "fine"
  • 因为 Bjarne Stroustrup 认为这是 K&R C 和 C90 的弱点。哎呀,即使是 ISO C 委员会也认为这是一个弱点,并在 C99 中使事情变得更加严格。
  • 这不符合现代 C 语言。请设置编译器的警告级别,它会告诉你。
  • 是的,它仍然可能意味着它不合格。 C 编译器只需要抱怨违反约束。此外,任何更现代的 gcc 都应该抱怨此代码,可能您的代码仍固定为 C89。从那时起,C 有两个修订版 C99 和 C11。

标签: c++ c function gcc compiler-errors


【解决方案1】:

在较早的(C99 之前)C 标准中,有一种称为“隐式函数声明”的东西,自 C99 以来已被删除。 因此,如果您在 C90 模式下编译,编译器必须支持该“功能”。而在 C++ 中,从未出现过“隐式函数声明”。所以 GCC 出错了。您的代码在现代 C(C99 或更高版本)中也无效。

使用更严格的编译器开关(例如-std=c99 -Wall -Wextra -pedantic-errors)进行编译并注意所有诊断。

【讨论】:

  • @cmaster 你编译的方式不对,别问我是怎么管理的。 gcc -std=c11 -pedantic-errorserror: implicit declaration of function 'sum' [-Wimplicit-function-declaration]
  • @cmaster 否。C 标准只要求编译器必须为违反标准提供“诊断消息”。 C 标准中不存在术语“错误”和“警告”。
  • 啊,我的立场是正确的。对不起。
  • 对于新代码和当前的 GCC,我推荐-std=gnu11 -Wall -Wextra -Wpedantic -Wstrict-prototypes -Wwrite-strings。使用-std=c 模式而不是-std=gnu 模式确实不会捕获额外的可移植性问题;它只会禁用您可能想要使用的库功能(使用适当的 ifdeffage)并启用诸如三元组之类的错误功能。还有更多-W 开关可能有用,但您必须考虑每个开关是否适合您的代码。不要不要使用 -Werror-pedantic-errors 没有记录在不修改 Makefile 的情况下关闭它们的方法。
  • @Joshua 这并不意味着它不起作用。它适用于返回 int 的函数。只是不是那些返回指针的。但标准从不保证后者。
【解决方案2】:

我一直认为 C 的限制性更强,但似乎并非如此。

你有它倒退。在几乎所有 C++不是 C 超集的地方,这是因为 C++ 的限制性更强。 C++ 类型系统比 C 类型系统更严格,删除了您遇到的遗留功能(“隐式声明”),还有更多保留字等。

确实,C++ 比 C 有更多的特性,但你不能将语言的特性数量与缺乏限制混为一谈。 ML/Haskell 语言家族的设计理念的一个主要方面是提供大量的功能,但也提供大量的严格性。

【讨论】:

  • 感谢您提供的信息。所以 C 更古老、更原始,也许正因为如此,它的限制也更少。但是 C++ 提供与 C 相同级别的低级访问,对吗?访问低级功能的数量不是与限制较少有关吗?我自己也搞糊涂了:(
  • @JenniferAnderson - Noooo。 Zwol 从未暗示过这一点。 C 不一样!
  • 不,两者都没有比另一个更严格。它们只是不同,有不同的规则。
  • @JenniferAnderson C 当然更简单(我认为原始是一种侮辱),但它肯定不是“老”。两种语言都在积极开发中。
  • @JenniferAnderson 我认为低级访问是一个特性(在 C 和 C++ 中以不同的方式可用),而不是缺乏严格性。严格性是关于语言的整体设计,而不是你能做或不能做的任何具体事情。可以在严格语言(Ada、Rust)和非严格语言(汇编)中进行低级访问。注:我不会将 C 或 C++ 描述为严格或非严格。
【解决方案3】:

C 最初允许在未定义的情况下从程序中调用函数,允许“稍后定义”它们。然后,如果你没有定义函数,编译器只是简单地制定了一个调用约定,例如“好吧,我不知道这个函数返回什么,所以让我们猜测它返回 int”。可以对参数做出类似的假设......您可以选择为其定义类型。旧的“K&R 风格”C 函数看起来像

int func (a, b)
  int a;
  int b;
{
   ...
}

要强制参数的类型,您必须使用所谓的“原型格式”,前向声明如下:

int func (int a, int b); // function prototype since the parameter types are explicit

所有隐式函数声明行为都是危险的废话,并导致致命的错误。然而,这种危险的行为在 1990 年的标准化中只是部分地被淘汰了。如果没有可见的原型,编译器仍然可以对函数做出隐含的假设。 (例如,如果你忘记包含 stdlib.h,这就是为什么 malloc 过去会完全崩溃的原因。)

这就是您的代码编译的原因,您使用的是旧版本的 gcc(4.x 或更早版本),默认为 -std=gnu90,它使用 1990 年的 C 标准 + 非标准扩展。较新版本的 gcc,5.0 或更高版本,默认为 -std=gnu11,这是当前的 C 标准 (C11) + 非标准扩展。

C++ 从未允许这种行为,C 也修复了它,在 1999 年使用 C99 标准。即使你有一个旧的 gcc 编译器,你也应该能够使用gcc -std=c99 -pedantic-errors 进行编译,这意味着“实际上遵循C 标准,1999 年版本”。如果在调用函数之前没有看到正确的函数声明/定义,则会出现编译器错误。

【讨论】:

    【解决方案4】:

    有很多原因。其中之一是函数重载:

    void func(double);
    // void func(int);
    
    int main()
    {
        func(1);
    }
    

    如果我取消注释带有void func(int x); 的行,它将被调用,否则1 将被提升为double 并且void func(double) 将被调用。

    【讨论】:

    • 不在 C 中。C 没有重载,因此如果您取消注释 func() 的第二个声明,那么符合标准的 C 编译器将至少发出诊断。如果编译器仍然创建可执行文件,则 C 不会定义其行为。
    • 没错!添加新功能通常需要添加限制。在 C 中,您可以将类型命名为 class,但在 C++ 中则不能,因为它是保留关键字。
    【解决方案5】:

    当 C 编译器看到对它不知道的函数的调用时,它会猜测返回值和参数类型应该是什么。返回类型被猜测为int,参数类型被猜测为与应用“通常的促销”后传入的值相同。

    所以如果你只是打电话

    double result = cube (1);
    

    编译器猜测函数“cube”有一个 int 类型的参数,并返回 int。

    如果“猜测”错了会发生什么?艰难的。您有未定义的行为,您的代码可能会崩溃或更糟。

    由于这种“猜测”,调用 sum (4, 6) 在 C 中是允许的,并且因为实际函数具有所有正确的类型(两个 int 类型的参数,返回值具有类型 in)它实际上可以工作。但这显然是一件非常危险的事情。

    因为它太危险了,C++ 没有隐式声明(即 C++ 编译器不允许猜测参数类型。这就是它不编译的原因。

    现在有一些语言,编译器不需要在使用函数之前声明函数

    【讨论】:

      猜你喜欢
      • 2012-01-19
      • 2012-07-15
      • 2015-09-04
      • 2014-09-15
      • 2017-03-08
      • 1970-01-01
      • 2011-06-13
      • 1970-01-01
      • 2012-02-17
      相关资源
      最近更新 更多