2010-03-08 19 views
10

Wie bekomme ich zuverlässig die Größe eines C-artigen Arrays? Das Verfahren scheint oft empfohlen zu werden sizeof zu verwenden, aber es funktioniert nicht in der foo Funktion, wo x in geben wird:Wie wird die Größe eines C-artigen Arrays zuverlässig ermittelt?

#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; 
} 

Antworten auf this question empfehlen sizeof aber sie sagen nicht, dass es (anscheinend ?) funktioniert nicht, wenn Sie das Array übergeben. Muss ich stattdessen einen Sentinel benutzen? (Ich glaube nicht, die Benutzer meiner foo Funktion kann immer ein Wächter am Ende zu setzen trauen. Natürlich, ich std::vector verwenden könnte, aber dann bekomme ich nicht die schöne Stenografie Syntax {1,2,3,4,5}.)

+1

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

+0

Wenn die Benutzer Ihrer Funktion nicht tun, was die Dokumente ihnen sagen, zu tun, ist ihr Problem ist nicht dein. Es ist unmöglich zu verhindern, dass die Ahnungslosen eine API missbrauchen. –

+0

'x' in' foo' ist ein Zeiger und kein Array. Siehe 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-array-in-der-c-Programmiersprache. Auch, dup: http://stackoverflow.com/questions/37538/c-how-do-i-determine-the-size-of-my-array – outis

Antwort

15

In C-Array-Parameter in C sind wirklich nur Zeiger, so sizeof() wird nicht funktionieren. Sie müssen entweder die Größe als einen anderen Parameter übergeben oder einen Sentinel verwenden - je nachdem, was für Ihr Design am besten geeignet ist.

Einige andere Optionen:

Einige andere Infos:

  • für C++, anstelle einen rohen Array Zeiger vorbei, Sie vielleicht die Parameter verwenden, um etwas zu haben, das das Array in einer Klasse hüllt Vorlage, die die Array-Größe verfolgt und Methoden zum sicheren Kopieren von Daten in das Array bereitstellt. Etwas wie STLSoft's array_proxy template oder Boost's boost::array könnte helfen. Ich habe zuvor eine array_proxy Vorlage zu einem schönen Effekt verwendet. Innerhalb der Funktion, die den Parameter verwendet, erhalten Sie std::vector ähnliche Operationen, aber der Aufrufer der Funktion kann ein einfaches C-Array verwenden. Es gibt kein Kopieren des Arrays - die Vorlage array_proxy kümmert sich darum, den Array-Zeiger und die Größe des Arrays fast automatisch zu packen.

  • einen Makro zur Bestimmung der Anzahl der Elemente in einem Array in C zu verwenden (wenn sizeof() helfen könnte - das heißt, dass Sie nicht mit einem einfachen Zeiger zu tun.): Is there a standard function in C that would return the length of an array?

3

Sie können entweder die Größe übergeben, einen Sentinel verwenden oder besser noch std :: vector verwenden. Auch wenn std :: vector initializer Listen fehlt, ist es immer noch leicht, einen Vektor mit einem Satz von Elementen zu konstruieren (wenn auch nicht ganz so schön)

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

Die std :: vector Klasse macht auch Fehler viel schwieriger machen, die ist sein Gewicht in Gold wert. Ein weiterer Vorteil ist, dass alle C++ damit vertraut sein sollten und die meisten C++ - Anwendungen einen std :: vector anstelle eines rohen C-Arrays verwenden sollten.

Als kurze Notiz, C++ 0x fügt Initializer lists

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

Sie auch Boost.Assign verwenden können, das Gleiche zu tun, obwohl die Syntax etwas gewunden ist.

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

oder

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

c bietet keine native Unterstützung für diese. Sobald ein Array außerhalb des deklarierten Bereichs übergeben wurde, ist seine Größe verloren.

Sie können übergeben Sie die Größe mit dem Array. Sie können sie sogar in einer Struktur bündeln, wenn Sie immer die Größe behalten wollen, obwohl Sie damit einen gewissen Bookkeeping-Overhead haben.

1

Wie wäre das? ..

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

-1: Das ist eine schreckliche Idee. – Brian

+1

@Brian: Es kann oder kann nicht eine schlechte Idee sein, abhängig vom Kontext. Warum denkst du, dass es schrecklich ist? Es ist eine gute Idee, denn es wird nicht kompilieren, wenn Sie einen Zeiger übergeben. Es ist schlecht, wenn die Funktion groß ist und mit vielen verschiedenen Array-Größen aufgerufen - es wird eine Reihe von Funktionen generieren. Eine Alternative könnte sein, dass sich dieses foo dann umdreht und ein private/internes foo_internal aufruft (int * x, size_t length); Es hängt von der Verwendung ab. Ich habe diese Technik mit String-Funktionen nützlich gefunden, die bei unsachgemäßer Verwendung sonst überlaufen. Ein Kommentar ohne Erklärung ist eine schreckliche Idee. – tony

0

Sie müssen die Größe zusammen mit dem Array übergeben, so wie es in vielen Bibliotheksfunktionen durchgeführt wird, zum Beispiel strncpy(), strncmp() usw. Leider ist dies nur so wie es in C funktioniert :-).

Alternativ könnten Sie Ihre eigene Struktur wie ausrollen:

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

und übergeben Sie ihn um den Code.

Natürlich können Sie immer noch std::list oder std::vector verwenden, wenn Sie mehr C++ -ish sein möchten.

4

A common idiom in GNU libstdc erwähnt ++ Dokumentation ist die lengthof Funktion:

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

Sie es als

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

Warnung verwenden können: dies funktioniert nur, wenn die Anordnung nicht decayed into a pointer hat.

1

Ein Array-Ausdruck wird implizit von "N-Element-Array von T" nach "Zeiger auf T" konvertiert, und sein Wert ist die Adresse des ersten Elements im Array, es sei denn, der Array-Ausdruck ist der Operand entweder des Operators sizeof oder der Adresse-of (&), oder wenn der Array-Ausdruck ein String-Literal ist, das zum Initialisieren eines anderen Arrays in einer Deklaration verwendet wird. Kurz gesagt, Sie können ein Array nicht an eine Funktion als Array übergeben; Was die Funktion empfängt, ist ein Zeigerwert, kein Array-Wert.

Sie müssen die Array-Größe als separaten Parameter übergeben.

Da Sie C++ verwenden, verwenden Sie Vektoren (oder einen anderen geeigneten STL-Container) anstelle von C-artigen Arrays. Ja, Sie verlieren die handliche Kurzschriftsyntax, aber der Kompromiss ist mehr als wert. Ernst.

2

Ich stimme auch zu, dass Corwins Methode oben sehr gut ist.

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

Ich glaube nicht, dass jemand einen wirklich guten Grund gab, warum dies keine gute Idee ist.
in Java, haben wir zum Beispiel Dinge wie schreiben:

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

in C++ sie es stattdessen wäre schön, noch einen Schritt weiter

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

zu sagen, und gehen wir nehmen konnte

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

Oder wenn das Probleme verursacht, nehme ich an, Sie könnten explizit schreiben:

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

Dann müssen wir gehen gerade in Haupt:

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

macht Sinn für mich :-)

0

Seit C++ 11 gibt es eine sehr bequeme Möglichkeit ist:

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