2011-01-17 4 views

Antwort

44

In C erfordert dies einen Zeiger auf ein Array von 16 ganzen Zahlen:

void special_case(int (*array)[16]); 

Es mit genannt werden würde:

int array[16]; 
special_case(&array); 

In C++, können Sie eine Referenz auf ein Array verwenden können, auch, wie in Nawaz's Antwort gezeigt. (Die Frage fragt nach C im Titel, und ursprünglich nur erwähnt, C++ in dem Tags.)


Jede Version, die einige Varianten verwendet:

void alternative(int array[16]); 

endet als äquivalent zu:

void alternative(int *array); 

die in der Praxis jede Größe von Array akzeptieren wird.


die Frage gestellt wird - nicht special_case() wirklich eine andere Größe des Arrays verhindern, übergeben wird. Die Antwort ist ja'.

void special_case(int (*array)[16]); 

void anon(void) 
{ 

    int array16[16]; 
    int array18[18]; 
    special_case(&array16); 
    special_case(&array18); 
} 

Der Compiler (GCC 4.5.2 auf MacOS X 10.6.6, wie es geschieht) beklagt (warnt):

$ gcc -c xx.c 
xx.c: In function ‘anon’: 
xx.c:9:5: warning: passing argument 1 of ‘special_case’ from incompatible pointer type 
xx.c:1:6: note: expected ‘int (*)[16]’ but argument is of type ‘int (*)[18]’ 
$ 

Schalte auf GCC 4.2.1 - wie von Apple zur Verfügung gestellt - und Die Warnung lautet:

$ /usr/bin/gcc -c xx.c 
xx.c: In function ‘anon’: 
xx.c:9: warning: passing argument 1 of ‘special_case’ from incompatible pointer type 
$ 

Die Warnung in 4.5.2 ist besser, aber die Substanz ist die gleiche.

+0

Würde es tatsächlich verhindern, dass der Benutzer ein Array [17] oder Array [15] erstellt? wenn ich denke, was genau int [] ist, ist es im Hintergrund ein int Zeiger auf ein nicht dynamisches Array. Würde das Programm/der Compiler automatisch eine andere Länge ablehnen, oder würde es es akzeptieren und versuchen, es so gut wie möglich zu handhaben? –

+0

Das ist genau richtig, ich habe meine Antwort nach dem Test gelöscht. Ich dachte, ich könnte mit einer anderen Verwendung für Static (AUFS) knifflig werden und mit C99 'void foo gehen (int a [static 16]); aber ich habe gerade erkannt, dass garantiert * mindestens * 16 Mitglieder nicht * genau * wie gewünscht das OP – SiegeX

+0

Also, wenn ich ein Array übergeben, das nicht der Größe 16 ist, wird es nicht kompilieren? Lass uns sagen, ich habe int intArray [20]; Sonderfall (& intArray). Wird das kompilieren? – armanali

6

& ist notwendig, in C++:

void foo(int (&a)[16]); // & is necessary. (in C++) 

Hinweis: & ist notwendig, sonst kann man Array jeder Größe passieren!


Für C:

void foo(int (*a)[16]) //one way 
{ 
} 

typedef int (*IntArr16)[16]; //other way 
void bar(IntArr16 a) 
{ 
} 

int main(void) 
{ 
     int a[16]; 
     foo(&a); //call like this - otherwise you'll get warning! 
     bar(&a); //call like this - otherwise you'll get warning! 
     return 0; 
} 

Demo: http://www.ideone.com/fWva6

+2

Referenzen sind nicht Teil der C-Sprache. –

+1

@Stephen: Sie haben Recht - aber während der Titel nach C fragt, enthalten die Tags C++. –

+0

@ Jonathan: Sie sind Ritus, aber wenn wir über Konzepte wie diese C oder C++ nehmen ist es das gleiche .. ist es nicht .. jetzt habe ich C++ in der Que – sriks

2

Ich denke, der einfachste Weg, typsicher zu sein, um eine Struktur zu erklären wäre, dass das Array enthält, und übergeben Sie das:

struct Array16 { 
    int elt[16]; 
}; 


void Foo(struct Array16* matrix); 
11

Es gibt mehrere Möglichkeiten, arr zu deklarieren ay-Parameter der festen Größe:

void foo(int values[16]); 

akzeptiert jede pointer-to int, sondern die Array-Größe dient als Dokumentation

void foo(int (*values)[16]); 

einen Zeiger auf ein Array nimmt mit genau 16 Elementen

void foo(int values[static 16]); 

akzeptiert einen Zeiger auf das erste Element eines Arrays mit mindestens 16 Elementen

struct bar { int values[16]; }; 
void foo(struct bar bar); 

akzeptiert eine Struktur, die ein Array mit genau 16 Elementen enthält und diese als Wert übergeben.

+1

Im 'void foo (int Werte [static 16]); 'Fall erfordert der C-Standard nicht, dass der Compiler überprüft, ob das angegebene Argument korrekt ist. Und, AFAIK, versuchen das keine Compiler. Während es in der Theorie schön ist, bringt es Ihnen zur Zeit keine Sicherheit - es wird keine Warnung ausgegeben, wenn Sie es mit einem kleineren Array aufrufen. Hoffentlich werden Compiler besser werden. –

+0

Tatsächlich gibt Clang eine Warnung aus, wenn Sie ein Array übergeben, das zu kurz ist. Es gibt keine Warnung, wenn Sie '' & local_int'' übergeben, obwohl dies bedeutet, dass Sie ein Array der Länge 1 ausgeben. –

1

Sie haben bereits einige Antworten für C und eine Antwort für C++ erhalten, aber es gibt eine andere Möglichkeit, dies in C++ zu tun.

Wie Nawaz sagte, eine Reihe von N Größe passieren, können Sie dies in C++ tun:

const size_t N = 16; // For your question. 

void foo(int (&arr)[N]) { 
    // Do something with arr. 
} 

jedoch, wie von C++ 11, können Sie auch die std :: Array-Container, der mit einer natürlicheren Syntax übergeben werden kann (vorausgesetzt, die Template-Syntax ist vertraut).

#include <array> 

const size_t N = 16; 

void bar(std::array<int, N> arr) { 
    // Do something with arr. 
} 

Als Behälter, std :: Array ermöglicht meist die gleiche Funktionalität wie ein normaler C-Stil-Array, während auch eine zusätzliche Funktionalität hinzugefügt wird.

std::array<int, 5> arr1 = { 1, 2, 3, 4, 5 }; 
int arr2[5] = { 1, 2, 3, 4, 5 }; 

// Operator[]: 
for (int i = 0; i < 5; i++) { 
    assert(arr1[i] == arr2[i]); 
} 

// Fill: 
arr1.fill(0); 
for (int i = 0; i < 5; i++) { 
    arr2[i] = 0; 
} 

// Check size: 
size_t arr1Size = arr1.size(); 
size_t arr2Size = sizeof(arr2)/sizeof(arr2[0]); 

// Foreach (C++11 syntax): 
for (int &i : arr1) { 
    // Use i. 
} 
for (int &i : arr2) { 
    // Use i. 
} 

jedoch mein Wissen (die zugegebenermaßen zu der Zeit begrenzt ist), ist Pointer-Arithmetik nicht sicher mit std :: Array, wenn Sie die Member-Funktion Daten() verwenden, zuerst die tatsächlichen Arrays Adresse zu erhalten. Dies dient dazu, zu verhindern, dass zukünftige Änderungen an der Klasse std :: array Ihren Code unterbrechen, und weil manche STL-Implementierungen zusätzliche Daten zusätzlich zum eigentlichen Array speichern.


Beachten Sie, dass dies für neuen Code am nützlichsten sein würde, oder wenn Sie Ihren bereits vorhandenen Code umwandeln zu std :: Arrays anstelle von C-Stil-Arrays zu verwenden. Da es sich bei std :: arrays um Aggregattypen handelt, fehlen ihnen benutzerdefinierte Konstruktoren. Daher können Sie nicht direkt vom C-style-Array in std :: array wechseln (ohne einen Cast zu verwenden), aber das ist hässlich und kann in Zukunft Probleme verursachen).So wandeln sie, würden Sie stattdessen so etwas benötigen:

#include <array> 
#include <algorithm> 

const size_t N = 16; 

std::array<int, N> cArrayConverter(int (&arr)[N]) { 
    std::array<int, N> ret; 

    std::copy(std::begin(arr), std::end(arr), std::begin(ret)); 

    return ret; 
} 

Deshalb, wenn Ihr Code verwendet C-Stil-Arrays, und es wäre nicht durchführbar, es zu konvertieren std :: Arrays stattdessen zu verwenden, Sie würden besser mit C-artigen Arrays zu bleiben.

(Anmerkung: Ich Größen wie N angegeben, so dass Sie leichter den Code wiederverwenden können, wo immer Sie es brauchen.)


Edit: Es gibt ein paar Dinge, die ich vergessen zu erwähnen:

1) Die Mehrzahl der C++ - Standardbibliotheksfunktionen, die für den Einsatz auf Containern entwickelt wurden, sind implementationsunabhängig; Anstatt für bestimmte Container ausgelegt zu sein, operieren sie mit Iteratoren auf Reichweiten. (Das bedeutet auch, dass sie für std::basic_string und instantiations arbeiten davon, wie std::string.) Zum Beispiel std::copy hat den folgenden Prototyp:

template <class InputIterator, class OutputIterator> 
OutputIterator copy(InputIterator first, InputIterator last, 
        OutputIterator result); 
// first is the beginning of the first range. 
// last is the end of the first range. 
// result is the beginning of the second range. 

Während diese imposanten aussehen, Sie müssen in der Regel die Vorlage nicht angeben Parameter, und kann nur der Compiler das für Sie behandeln lassen.

std::array<int, 5> arr1 = { 1, 2, 3, 4, 5 }; 
std::array<int, 5> arr2 = { 6, 7, 8, 9, 0 }; 
std::string str1 = ".dlrow ,olleH"; 
std::string str2 = "Overwrite me!"; 

std::copy(arr1.begin(), arr1.end(), arr2.begin()); 
// arr2 now stores { 1, 2, 3, 4, 5 }. 

std::copy(str1.begin(), str1.end(), str2.begin()); 
// str2 now stores ".dlrow ,olleH". 
// Not really necessary for full string copying, due to std::string.operator=(), but possible nonetheless. 

Wegen auf Iteratoren verlassen, sind diese Funktionen auch mit C-Stil-Arrays kompatibel (wie Iteratoren eine Verallgemeinerung von Zeigern, alle Zeiger sind definitionsgemäß Iteratoren sind (aber nicht alle Iteratoren sind notwendigerweise Zeiger)). Dies kann nützlich sein, wenn Sie mit Legacy-Code arbeiten, da Sie damit vollen Zugriff auf die Bereichsfunktionen in der Standardbibliothek haben.

int arr1[5] = { 4, 3, 2, 1, 0 }; 
std::array<int, 5> arr2; 

std::copy(std::begin(arr1), std::end(arr1), std::begin(arr2)); 

Sie haben aus diesem Beispiel und die letzten bemerkt, dass std::array.begin() und std::begin() mit std::array austauschbar verwendet werden. Dies liegt daran, dass std::begin() und std::end() so implementiert sind, dass sie für jeden Container den gleichen Rückgabetyp aufweisen und denselben Wert zurückgeben, wie beim Aufrufen der Elementfunktionen begin() und end() einer Instanz dieses Containers.

// Prototype: 
template <class Container> 
auto begin (Container& cont) -> decltype (cont.begin()); 

// Examples: 
std::array<int, 5> arr; 
std::vector<char> vec; 

std::begin(arr) == arr.begin(); 
std::end(arr) == arr.end(); 

std::begin(vec) == vec.begin(); 
std::end(vec) == vec.end(); 

// And so on... 

C-Stil-Arrays haben keine Elementfunktionen, was die Verwendung von std::begin() und std::end() für sie. In diesem Fall sind die zwei Funktionen überladen, um anwendbare Zeiger zu liefern, abhängig vom Typ des Arrays.

// Prototype: 
template <class T, size_t N> 
T* begin (T(&arr)[N]); 

// Examples: 
int arr[5]; 

std::begin(arr) == &arr[0]; 
std::end(arr) == &arr[4]; 

Als allgemeine Faustregel gilt, wenn Sie sich nicht sicher sind, ob nicht irgendein bestimmtes Codesegment wird im C-Stil-Arrays verwenden, ist es sicherer, std::begin() und std::end() zu verwenden.

[Beachten Sie, dass während der Verwendung von std::copy() als Beispiel die Verwendung von Bereichen und Iteratoren in der Standardbibliothek sehr häufig ist. Die meisten, wenn nicht alle Funktionen, die für den Betrieb an Containern ausgelegt sind (oder genauer gesagt für alle Implementierungen der Container concept, wie-, std::vector und std::string), verwenden Bereiche, wodurch sie mit aktuellen und zukünftigen Containern sowie mit C kompatibel sind -artige Arrays. Es kann jedoch Ausnahmen von dieser weit verbreiteten Kompatibilität geben, die mir nicht bekannt sind.]

2) Wenn ein std :: -Array nach Wert übergeben wird, kann je nach Größe des Arrays ein beträchtlicher Overhead entstehen.Daher ist es normalerweise besser, sie als Referenz zu übergeben oder Iteratoren zu verwenden (wie die Standardbibliothek).

// Pass by reference. 
const size_t N = 16; 

void foo(std::array<int, N>& arr); 

3) Alle diese Beispiele gehen davon aus, dass alle Arrays in Ihrem Code die gleiche Größe sein, wie durch die Konstante N angegeben. Um Ihren Code mehr implementierungsunabhängig zu machen, können Sie entweder die Bereiche & Iteratoren selbst verwenden, oder wenn Sie Ihren Code auf Arrays fokussiert halten möchten, verwenden Sie Template-Funktionen. [Aufbauend auf this answer to another question.]

template<size_t SZ> void foo(std::array<int, SZ>& arr); 

... 

std::array<int, 5> arr1; 
std::array<int, 10> arr2; 

foo(arr1); // Calls foo<5>(arr1). 
foo(arr2); // Calls foo<10>(arr2). 

Wenn dies tun, können Sie sogar so weit gehen wie die Array-Mitglied Typ als auch als Templat, sofern der Code auf andere Typen als int arbeiten kann.

template<typename T, size_t SZ> 
void foo(std::array<T, SZ>& arr); 

... 

std::array<int, 5> arr1; 
std::array<float, 7> arr2; 

foo(arr1); // Calls foo<int, 5>(arr1). 
foo(arr2); // Calls foo<float, 7>(arr2). 

Ein Beispiel hierfür finden Sie unter here.

Wenn jemand irgendwelche Fehler sieht, die ich vielleicht übersehen habe, können Sie sie auf mich hinweisen, um sie zu reparieren, oder sie selbst reparieren. Ich glaube, ich habe sie alle erwischt, aber ich bin mir nicht 100% sicher.

Verwandte Themen