2015-01-12 6 views
31

でCにおける違法++が、法的:ポインタ「(* P)[]」私は、これは(Cではなく、法的な)C++で違法であることが判明C

#include <stdio.h> 
#include <stdlib.h> 
#define ARRAY_LENGTH(A) (sizeof(A)/sizeof(A[0])) 

int accumulate(int n, const int (*array)[]) 
{ 
    int i; 
    int sum = 0; 
    for (i = 0; i < n; ++i) { 
     sum += (*array)[i]; 
    } 
    return sum; 
} 

int main(void) 
{ 
    int a[] = {3, 4, 2, 4, 6, 1, -40, 23, 35}; 
    printf("%d\n", accumulate(ARRAY_LENGTH(a), &a)); 
    return 0; 
} 

それはコンパイルgcc -std=c89 -pedanticを使用しても問題なく、g++を使用してコンパイルできません。私はこれらのエラーメッセージを取得g++を使用して、それをコンパイルしようとすると:

main.cpp:5:37: error: parameter 'array' includes pointer to array of unknown bound 'int []' 
int accumulate(int n, int (*array)[]) 
            ^
main.cpp: In function 'int main()': 
main.cpp:18:50: error: cannot convert 'int (*)[9]' to 'int (*)[]' for argument '2' to 'int accumulate(int, int (*)[])' 
    printf("%d\n", accumulate(ARRAY_LENGTH(a), &a)); 

私は長い間私のCコードでこれを使用していると私はそれがC++で違法だったとは思いもしませんでした。私にとっては、これは、関数が手作業で知られていない配列の大きさをとることを文書化する便利な方法のようです。

なぜこれが正当なCですが、無効なC++であるかを知りたいのですが。私はまた、C++委員会がそれを取り除くことを決定した(そして、Cとのこの互換性を破る)ことが何であったのだろうかと思います。

なぜこの法的Cコードが違法なC++コードですか?

+1

C++の分割時に存在していたCのバージョンに、不特定のサイズの配列がありましたか?当時はそれらをポインタとして宣言しなければならず、 '[] 'を使うことができるのは後で追加されたものだと思います。 – Barmar

+0

C++はC89から分割されていて、この例は 'gcc -std = c89 -pedantic'を使っても問題なくコンパイルできますので、後で追加したとは思いません。 – wefwefa3

+0

'n 'をテンプレートパラメータ('テンプレート ')に変換し、それを配列型(' int(* array)[n] ')で使用するとコードが動作するはずです。また、配列へのポインタの代わりに配列への参照を使用することも可能です(そして大抵の場合はもっと簡単です):int(&array)[n] '。それから 'accumulate(&a)'を呼び出して、コンパイラに 'n'を推測させてください! ;) – leemes

答えて

34

Dan Saks wrote about this in 1995、C++標準化までのリード時:

委員会は結合不明でアレイに ポインタ又は参照を受け入れるこのような機能は、 宣言マッチングとオーバーロード解決を複雑にすることを決定C++のルール 委員会は、そのような機能にはほとんど効用がなく、 はまれであるため、禁止するのが最も簡単なことに同意しました。したがって、 C++ドラフトは今述べ:

パラメータのタイプは Tの結合未知の配列にTまたは参照結合未知の 配列にフォームのポインタの種類が含まれている場合、プログラムであります形成されていない。

+7

最後の委員会で採択された[CWG issue 393の解決策](http://www.open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#393)によって禁止が解除されました。 –

29

C++には「互換型」という概念はありません。 Cでは、これは変数の完全に有効な再宣言である:Cで

extern int (*a)[]; 
extern int (*a)[3]; 

、これは、同じ機能の完全に有効な再宣言である:Cで

extern void f(); 
extern void f(int); 

、これは、実装固有でありますしかし、一般的に同じ変数の有効な再宣言:それのいずれかを持っていない++

enum E { A, B, C }; 
extern enum E a; 
extern unsigned int a; 

C。 C++では、型は同じか、異なっていますが、型が異なる場合、型の違いにはほとんど関心がありません。同様

int main() { 
    const char array[] = "Hello"; 
    const char (*pointer)[] = &array; 
} 

は、C++で有効な、しかしCで無効である:array[]にもかかわらず、不特定長さの配列へのポインタとして宣言されている長さ6 pointerの配列として宣言されていますこれは別のタイプです。 const char (*)[6]からconst char (*)[]への暗黙的な変換はありません。

それゆえ、不特定の長さの配列へのポインタを取る関数は、C++ではかなり役に立たず、プログラマーの間違いはほとんど間違いです。具体的な配列インスタンスから開始する場合は、ほとんどの場合、すでにサイズが指定されているため、型の不一致があるため、関数に渡すためにアドレスを取得することはできません。

そして、あなたの例では、不特定の長さの配列へのポインタの必要はどちらかありません。また、C++で有効であることを起こるC、でそれを書くための通常の方法では、呼び出される

int accumulate(int n, int *array) 
{ 
    int i; 
    int sum = 0; 
    for (i = 0; i < n; ++i) { 
     sum += array[i]; 
    } 
    return sum; 
} 

ですaccumulate(ARRAY_LENGTH(a), a)

+1

変換を許可する[EWGの問題](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4325.html#118)があります。 –

+2

@ T.C。ああ、それは知ってうれしいです。私はそれが許可されたら、たぶん一方の方向にしか許されないだろうと思う。 'char(*)[6]'から 'char(*)[]'への暗黙的な変換は安全ですが、 'char(*)[]'から 'char(*)[6]'への暗黙的変換は。 Cでは、変換(型は単に互換性があります)もないので、 'int main(){int array [6];}のようなコードを書くことができます。 int(* ptr1)[] = &array; int(* ptr2)[100] = ptr1; } '通常は、コンパイラの警告も出ません。 – hvd

関連する問題