web-dev-qa-db-ja.com

関数プロトタイプ(署名)の前方宣言に関してC ++がより制限されているのはなぜですか?

実際に関数を呼び出す関数の後に関数定義が宣言されている場合でも、関数シグネチャを使用する前に宣言することに関して、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
23
user8613421

古い(C99より前の)C標準では、「暗黙の関数宣言」と呼ばれるものがあり、C99以降削除されています。したがって、C90モードでコンパイルする場合、コンパイラはその「機能」をサポートする必要があります。一方、C++では、「暗黙の関数宣言」はありませんでした。したがって、GCCはエラーになります。あなたのコードは最新のC(C99以降)でも無効です。

より厳密なコンパイラスイッチを使用してコンパイルします(例:-std=c99 -Wall -Wextra -pedantic-errors)そしてすべての診断に注意を払ってください。

23
usr

私はいつもCの方が制限的だと思っていましたが、そうではないようです。

あなたはそれを後方に持っています。 C++ ではない Cのスーパーセットであるほぼすべての場所で、C++の方が制限が厳しいためです。 C++型システムは、C型システムよりも厳格であり、つまずいたもの(「暗黙の宣言」)などのレガシー機能が削除され、予約語がさらに多くなります。

C++にはCよりもはるかに多くのfeaturesがあるのは事実ですが、言語が持つ機能の数と制限がないことを混同しないでください。 ML/Haskell言語族の設計哲学の主要な側面は、多くの機能を提供することですが、多くの厳密さも提供することです。

16
zwol

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年バージョン」。次に、関数を呼び出す前に適切な関数宣言/定義が表示されていない場合、コンパイラエラーが発生します。

12
Lundin

Cコンパイラは、知らない関数の呼び出しを検出すると、戻り値とパラメータタイプを推測します。戻り値の型はintとして推測され、パラメーターの型は「通常のプロモーション」を適用した後に渡された値と同じであると推測されます。

だからあなたがただ電話するなら

double result = cube (1);

コンパイラは、関数「cube」にint型の引数が1つあると推測し、intを返します。

その「推測」が間違っているとどうなりますか?タフ。未定義の動作があり、コードがクラッシュするか、さらに悪化する可能性があります。

この「推測」により、Cでは合計(4、6)の呼び出しが許可され、実際の関数にはすべての正しい型(int型の2つの引数、戻り値にはin型)があるため、実際に機能します。しかし、これは明らかに非常に危険なことです。

非常に危険なため、C++は暗黙の宣言を行いません(つまり、C++コンパイラは引数の型を推測できません。そのため、コンパイルされません。

最近では、コンパイラが関数を宣言する必要がない言語がいくつかありますbeforeそれが使用されます。

1
gnasher729

多くの理由があります。それらの1つは、関数のオーバーロードです。

_void func(double);
// void func(int);

int main()
{
    func(1);
}
_

void func(int x);で行のコメントを外すと呼び出されます。そうでない場合は、_1_がdoubleにプロモートされ、void func(double)が呼び出されます。

1
Mikhail Maltsev