2015-01-12 2 views
31

Ich habe gerade herausgefunden, dass dies in C++ illegal ist (aber legal in C):Zeiger auf Array von nicht näherer Größe "(* p) []" illegal in C++, aber legal in 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; 
} 

Es kompiliert ohne Probleme mit gcc -std=c89 -pedantic aber nicht kompilieren mit g++. Wenn ich versuche, es zu kompilieren g++ mit erhalte ich diese Fehlermeldung:

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)); 

Ich habe für eine lange Zeit in meinem C-Code mit dieser und ich hatte keine Ahnung, dass es in C++ illegal war. Für mich scheint dies eine nützliche Möglichkeit zu sein, zu dokumentieren, dass eine Funktion ein Array benötigt, dessen Größe vorher nicht bekannt ist.

Ich möchte wissen, warum dies legal C ist, aber C++ ungültig. Ich frage mich auch, was das C++ - Komitee dazu gebracht hat, es wegzunehmen (und diese Kompatibilität mit C zu brechen).

Warum ist dieser legale C-Code aber illegaler C++ - Code?

+1

Hat die Version von C, die bei der Trennung von C++ existierte, Arrays nicht definierter Größe? Ich denke, dass man sie damals als Zeiger deklarieren musste, und die Verwendung von '[]' war eine spätere Ergänzung. – Barmar

+0

C++ wurde von C89 getrennt und das Beispiel kompiliert ohne Probleme mit 'gcc -std = c89 -pedantic', also glaube ich nicht, dass es eine spätere Addition war. – wefwefa3

+0

Beachten Sie, dass Ihr Code funktionieren sollte, wenn Sie 'n' in einen Template-Parameter (' template ') umwandeln und diesen im Array-Typ verwenden (' int (* array) [n] '). Beachten Sie auch, dass es sogar (und meistens einfacher) möglich ist, einen Verweis auf ein Array anstelle eines Pointers auf ein Array zu verwenden: 'int (& array) [n]'. Dann rufe es mit "accumulate (& a)" auf und lass den Compiler "n" für dich ableiten! ;) – leemes

Antwort

34

Dan Saks wrote about this in 1995, während der Spitze bis zu C++ Standardisierung:

Die Ausschüsse entschieden, dass Funktionen wie diese, die einen Zeiger oder eine Referenz auf ein Array mit unbekannter gebunden akzeptieren, Erklärung Anpassungs- und Überladungsauflösung erschweren Regeln in C++. Die Komitees stimmten darin überein, dass, da solche Funktionen wenig nützlich und ziemlich ungewöhnlich sind, es am einfachsten wäre, sie einfach zu verbieten. Damit nun der C++ Entwurf bestimmt:

Wenn der Typ eines Parameters eines Typs der Form Zeiger auf Array mit unbekannter von T oder Verweis auf Array mit unbekannter von T gebunden gebunden enthält, ist das Programm schlecht geformt.

+7

Das Verbot wurde durch [die Entschließung der CWG-Ausgabe 393] (http://www.open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#393) entfernt, die bei der letzten Ausschusssitzung angenommen wurde. –

29

C++ nicht über C Begriff der "kompatiblen Typ". In C, das ist eine absolut gültige Neudeklaration einer Variablen:

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

in C, das ist eine absolut gültige Neudeklaration der gleichen Funktion:

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

in C, dies ist durch die Implementierung spezifische, aber normalerweise eine gültige Deklaration derselben Variablen:

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

C++ hat nichts davon. In C++ sind Typen entweder gleich oder unterschiedlich, und wenn sie unterschiedlich sind, gibt es wenig Bedenken, wie unterschiedlich sie sind.

ähnlich

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

gilt in C, aber ungültiger in C++: array trotz der [], als ein Array von Länge deklariert ist 6. pointer wird als ein Zeiger auf ein Feld von nicht spezifizierten Länge erklärt , das ist ein anderer Typ. Es gibt keine implizite Konvertierung von const char (*)[6] zu const char (*)[].

Aus diesem Grund sind Funktionen, die Zeiger auf Arrays nicht näher spezifizierter Länge verwenden, in C++ ziemlich nutzlos und mit ziemlicher Sicherheit ein Fehler des Programmierers. Wenn Sie von einer konkreten Array-Instanz ausgehen, haben Sie fast immer bereits die Größe, daher können Sie ihre Adresse nicht übernehmen, um sie an Ihre Funktion zu übergeben, da Sie eine Typabweichung haben würden.

Und es gibt keine Notwendigkeit für Zeiger auf Arrays nicht näher bezeichneter Länge in Ihrem Beispiel entweder die normale Art und Weise, dass in C zu schreiben, was auch geschieht in C++ gültig ist, ist

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

aufgerufen werden als accumulate(ARRAY_LENGTH(a), a).

+1

Es gibt ein offenes [EWG-Problem] (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4325.html#118), um die Konvertierung zu ermöglichen. –

+2

@ T.C. Ah, das ist schön zu wissen. Ich denke, wenn es erlaubt wird, wird es wahrscheinlich nur in eine Richtung erlaubt sein. Eine implizite Umwandlung von 'char (*) [6]' nach 'char (*) []' ist sicher, aber eine implizite Umwandlung von 'char (*) []' nach 'char (*) [6]' ist nicht möglich . Da es in C keine Konvertierung gibt (die Typen sind einfach kompatibel), können Sie Code schreiben wie 'int main() {int array [6]; int (* ptr1) [] = &array; int (* ptr2) [100] = ptr1; } ', die in der Regel keine Compiler-Warnung erhält, geschweige denn einen Fehler. – hvd