2010-03-08 2 views
10

Come posso ottenere in modo affidabile la dimensione di un array in stile C? Il metodo spesso consigliato sembra essere quella di utilizzare sizeof, ma non funziona nella funzione foo, dove x viene passato:Come ottenere in modo affidabile la dimensione dell'array in stile C?

#include <iostream> 

void foo(int x[]) { 
    std::cerr << (sizeof(x)/sizeof(int)); // 2 
} 

int main(){ 
    int x[] = {1,2,3,4,5}; 
    std::cerr << (sizeof(x)/sizeof(int)); // 5        
    foo(x); 
    return 0; 
} 

Risposte alle this question consiglia sizeof ma non dicono che (a quanto pare ?) non funziona se si passa l'array in giro. Quindi, devo usare invece una sentinella? (Non credo che gli utenti della mia funzione foo possono sempre essere attendibile per mettere una sentinella alla fine. Certo, avrei potuto usare std::vector, ma poi non ho ricevuto la bella sintassi abbreviata {1,2,3,4,5}.)

+1

http://stackoverflow.com/questions/1810083/c-pointers-pointing-to-an-array-of-fixed-size – AnT

+0

Se gli utenti della tua funzione non fanno ciò che i documenti dicono loro di fare, questo è il loro problema non è tuo. È impossibile impedire al clueless di usare impropriamente un'API. –

+0

'x' in' foo' è un puntatore, piuttosto che una matrice. Vedi http://stackoverflow.com/questions/232691/how-can-i-get-the-size-of-an-array-from-a-pointer-in-c, http://stackoverflow.com/questions/1975128/sizeof-un-array-in-the-c-linguaggio di programmazione. Inoltre, dup: http://stackoverflow.com/questions/37538/c-how-do-i-determine-the-size-ofmy-array – outis

risposta

15

I parametri dell'array C in C sono in realtà solo indicatori in modo che sizeof() non funzionino. È necessario passare la dimensione come un altro parametro o utilizzare una sentinella, a seconda di quale sia il più appropriato per la progettazione.

Alcune altre opzioni:

alcune altre informazioni:

  • per C++, invece di passare un puntatore prime, si potrebbe desiderare di avere l'uso dei parametri qualcosa che avvolge la matrice in una classe modello che tiene traccia delle dimensioni dell'array e fornisce metodi per copiare i dati nell'array in modo sicuro. Qualcosa come STLSoft's array_proxy template o Boost's boost::array potrebbe aiutare. Ho usato un modello array_proxy con un bell'effetto prima. All'interno della funzione che utilizza il parametro, si ottengono operazioni simili a std::vector, ma il chiamante della funzione può utilizzare un semplice array C. Non c'è copia della matrice: il modello array_proxy si occupa di impacchettare quasi automaticamente il puntatore dell'array e le dimensioni dell'array.

  • una macro da utilizzare in C per determinare il numero di elementi di un array (per quando sizeof() potrebbe aiutare - vale a dire, non che fare con un semplice puntatore.): Is there a standard function in C that would return the length of an array?

3

Puoi passare la dimensione, usare una sentinella o ancora meglio usare std :: vector. Anche se std :: vector manca liste di inizializzazione è ancora facile da costruire un vettore con una serie di elementi (anche se non così bello)

static const int arr[] = {1,2,3,4,5}; 
vector<int> vec (arr, arr + sizeof(arr)/sizeof(arr[0])); 

La classe std :: vector fa anche commettere errori molto più difficile, che vale il suo peso in oro. Un altro vantaggio è che tutto il C++ dovrebbe avere familiarità con esso e la maggior parte delle applicazioni C++ dovrebbe usare un vettore std :: piuttosto che un array C grezzo.

Come una breve nota, C++ 0x aggiunge Initializer lists

std::vector<int> v = {1, 2, 3, 4}; 

è anche possibile utilizzare Boost.Assign a fare la stessa cosa, anche se la sintassi è un po 'più complicata.

std::vector<int> v = boost::assign::list_of(1)(2)(3)(4); 

o

std::vector<int> v; 
v += 1, 2, 3, 4; 
2

c fornisce alcun supporto nativo per questo. Una volta che una matrice viene passata dal suo ambito dichiarato, la sua dimensione viene persa.

È possibile passare la dimensione con l'array. Puoi anche raggrupparli in una struttura se devi sempre mantenere le dimensioni, anche se avrai un po 'di overhead di bookkeepping con quello.

1

Che ne dici di questo? ..

template <int N> 
void foo(int (&x)[N]) { 
    std::cerr << N; 
}
+0

-1: questa è un'idea orribile. – Brian

+1

@Brian: Può essere o meno una cattiva idea, a seconda del contesto. Perché pensi che sia orribile? È una buona idea perché NON COMPILA se si passa un puntatore. Non è corretto se la funzione è grande e viene chiamata con molte dimensioni di array diverse: genererà un sacco di funzioni. Un'alternativa potrebbe essere per questo pippo per poi girare intorno e chiamare un privato/interno foo_internal (int * x, size_t length); Dipende dall'uso. Ho trovato questa tecnica utile con le funzioni di stringa che tendono altrimenti a traboccare se usate impropriamente. Un commento senza spiegazione è un'idea orribile. – tony

0

È necessario passare la dimensione insieme con l'array, proprio come si è fatto in molte funzioni di libreria, per esempio strncpy(), strncmp() ecc Siamo spiacenti, questo è solo il modo in cui funziona in C :-).

In alternativa si potrebbe stendere la propria struttura come:

struct array { 
    int* data; 
    int size; 
}; 

e passarlo intorno al vostro codice.

Ovviamente è ancora possibile utilizzare std::list o std::vector se si desidera essere più C++ -ish.

4

Un common idiom di cui GNU libstdC++ documentazione è la funzione lengthof:

template<typename T, unsigned int sz> 
inline unsigned int lengthof(T (&)[sz]) { return sz; } 

è possibile utilizzarlo come

int x[] = {1,2,3,4,5}; 
std::cerr << lengthof(x) << std::endl; 

Attenzione: questo funziona solo quando l'array non ha decayed into a pointer.

1

Un'espressione di matrice avrà il suo tipo convertito implicitamente da "matrice di elementi N di T" a "puntatore a T" e il suo valore sarà l'indirizzo del primo elemento nell'array, a meno che l'espressione di matrice non sia l'operando degli operatori sizeof o address-of (&) o se l'espressione dell'array è una stringa letterale utilizzata per inizializzare un'altra matrice in una dichiarazione. In breve, non è possibile passare una matrice a una funzione come una matrice; ciò che riceve la funzione è un valore puntatore, non un valore di matrice.

È necessario passare la dimensione dell'array come parametro separato.

Poiché si utilizza C++, utilizzare i vettori (o qualche altro contenitore STL adatto) anziché gli array in stile C. Sì, perdi la comoda sintassi abbreviata, ma il compromesso vale più di quello. Sul serio.

2

Sono anche d'accordo sul fatto che il metodo di Corwin di cui sopra sia molto buono.

template <int N> 
void foo(int (&x)[N]) 
{ 
    std::cerr << N; 
} 

non credo che nessuno ha dato una buona ragione per cui questo non è una buona idea.
in Java, per esempio, possiamo scrivere cose come:

int numbers [] = {1, 2, 3, 4}; 
for(int i = 0; i < numbers.length(); i++) 
{ 
    System.out.println(numbers[i]+"\n"); 
} 

in C++ sarebbe bello invece di dire

int numbers [] = {1, 2, 3, 4}; 
int size = sizeof(numbers)/sizeof(int); 
for(int i = 0; i < size; i++) 
{ 
    cout << numbers[i] << endl; 
} 

Potremmo prendere un ulteriore passo avanti e andare

template <int N> 
int size(int (&X)[N]) 
{ 
    return N; 
} 

O se ciò causa problemi, suppongo che potresti scrivere esplicitamente:

template < int N > 
int size(int (&X)[N]) 
{ 
    int value = (sizeof(X)/sizeof(X[0])); 
    return value; 
} 

Allora non ci resta che andare in principale:

int numbers [] = {1, 2, 3, 4}; 
for(int i = 0; i < size(numbers); i++) 
{ 
    cout << numbers[i] << endl; 
} 

ha senso per me :-)

0

Poiché C++ 11, c'è un modo molto conveniente:

static const int array[] = { 1, 2, 3, 6 }; 
int size = (int)std::distance(std::begin(array), std::end(array))+1;