2015-01-12 9 views
31

ho appena scoperto che questo è illegale in C++ (ma legale in C):puntatore a vettore di dimensioni non specificato "(* p) []" illegale in C++, ma legale 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; 
} 

compila senza problemi usando gcc -std=c89 -pedantic ma non riesce a compilare usando g++. Quando provo a compilare utilizzando g++ ottengo questi messaggi di errore:

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

Ho utilizzato questo nel mio codice C per molto tempo e non avevo idea che fosse illegale in C++. Per me questo sembra un modo utile per documentare che una funzione prende un array le cui dimensioni non sono conosciute prima della mano.

Voglio sapere perché questo è C legale ma C++ non valido. Mi chiedo anche che cosa abbia fatto decidere al comitato del C++ di portarlo via (e rompere questa compatibilità con C).

Quindi, perché questo codice C legale ma codice C++ illegale?

+1

La versione di C esistente quando C++ è stata suddivisa ha array di dimensioni non specificate? Penso che dovessi dichiararli come indicatori in quei giorni, e poter usare '[]' fu un'aggiunta successiva. – Barmar

+0

Il C++ è stato diviso da C89 e l'esempio si compila senza problemi usando 'gcc -std = c89 -pedantic' quindi non penso che sia stata un'aggiunta successiva. – wefwefa3

+0

Nota che il tuo codice dovrebbe funzionare se converti 'n' in un parametro template (' template ') e lo usi nel tipo array (' int (* array) [n] '). Si noti inoltre che è persino possibile (e il più delle volte più semplice) utilizzare un riferimento all'array anziché al puntatore all'array: 'int (& array) [n]'. Quindi chiamalo con 'accumulate (& a)' e lascia che il compilatore deduca 'n' per te! ;) – leemes

risposta

34

Dan Saks wrote about this in 1995, durante la testa fino a C++ normalizzazione:

I comitati deciso che funziona come questa, che accettano un puntatore o riferimento ad un array con sconosciuta vincolato, complicano dichiarazione corrispondente e la risoluzione di sovraccarico regole in C++. Le commissioni hanno convenuto che, dal momento che tali funzioni hanno poca utilità e sono abbastanza rari, sarebbe più semplice vietarle. Quindi, il progetto C++ ora afferma:

Se il tipo di un parametro comprende un tipo di puntatore modulo per matrice di Sconosciuto bound T o riferimento alla matrice di Sconosciuto vincolato di T, il programma è mal formati.

+7

Il divieto è stato rimosso dalla [risoluzione del problema CWG 393] (http://www.open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#393), adottata nell'ultima riunione della commissione. –

29

C++ non ha idea di C di "tipo compatibile". In C, questo è un ridichiarazione perfettamente valido di una variabile:

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

in C, questo è un ridichiarazione perfettamente valida la stessa funzione:

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

in C, questo è specifico dell'implementazione, ma in genere una ridichiarazione valida della stessa variabile:

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

C++ non ha nulla di tutto ciò. In C++, i tipi sono uguali o diversi, e se sono diversi, non c'è molta preoccupazione in quanto sono diversi.

Analogamente,

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

è valido in C, ma non valido in C++: array, nonostante la [], viene dichiarata come array di lunghezza 6. pointer viene dichiarata come puntatore ad un array di lunghezza indeterminata , che è un tipo diverso. Non c'è conversione implicita da const char (*)[6] a const char (*)[].

Per questo motivo, le funzioni che puntano a matrici di lunghezza non specificata sono praticamente inutili in C++ e quasi certamente un errore da parte del programmatore. Se si inizia da un'istanza dell'array concreto, si ha quasi sempre la dimensione, quindi non è possibile prendere il suo indirizzo per passarlo alla funzione, perché si avrebbe una mancata corrispondenza di tipo.

E non v'è alcuna necessità di puntatori ad array di lunghezza non specificato nel vostro esempio sia: il modo normale di scrivere che in C, che risulta essere valido anche in C++, è

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

ad essere chiamato come accumulate(ARRAY_LENGTH(a), a).

+1

C'è un problema [EWG] aperto (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4325.html#118) per consentire la conversione. –

+2

@ T.C. Ah, è bello saperlo. Immagino che se fosse permesso, probabilmente sarà permesso solo in una direzione. Una conversione implicita da 'char (*) [6]' a 'char (*) []' è sicura, ma una conversione implicita da 'char (*) []' a 'char (*) [6]' non è . Perché in C, non c'è nemmeno alcuna conversione (i tipi sono semplicemente compatibili), puoi scrivere codice come 'int main() {int array [6]; int (* ptr1) [] = &array; int (* ptr2) [100] = ptr1; } 'che in genere non riceve alcun avvertimento sul compilatore, per non parlare di un errore. – hvd