wenig bekannt: Der unäre Plus-Operator kann als "Zerfallsoperator" verwendet werden: Gegeben int a[10]; int b(void);, dann +aist ein int-Zeiger und +bein Funktionszeiger. Nützlich, wenn Sie es an eine Vorlage übergeben möchten, die eine Referenz akzeptiert.
Johannes Schaub - Litb
3
@litb - parens würde dasselbe tun (z. B. (a) sollte ein Ausdruck sein, der als Zeiger ausgewertet wird), oder?.
Michael Burr
21
std::decayab C ++ 14 wäre eine weniger obskure Möglichkeit, ein Array über unary + zu zerlegen.
Legends2k
21
@ JohannesSchaub-litb da diese Frage sowohl C und C ++ markiert ist, würde ich das klarstellen , obwohl +aund +bin C ++ legal ist, ist es in C (C11 6.5.3.3/1 „Der Operand des unären illegal ist +oder -Betreiber hat hat arithmetischer Typ ")
MM
5
@lege Richtig. Aber ich nehme an, das ist nicht so wenig bekannt wie der Trick mit unary +. Der Grund, warum ich es erwähnte, war nicht nur, weil es verfällt, sondern weil es Spaß macht, damit zu spielen;)
Johannes Schaub - litb
Antworten:
283
Es wird gesagt, dass Arrays in Zeiger "zerfallen". Ein als deklariertes C ++ - Array int numbers [5]kann nicht erneut angezeigt werden, dh Sie können es nicht sagen numbers = 0x5a5aff23. Noch wichtiger ist, dass der Begriff Zerfall den Verlust von Typ und Dimension bedeutet. numbersZerfall int*durch Verlust der Dimensionsinformationen (Anzahl 5) und der Typ ist nicht int [5]mehr. Suchen Sie hier nach Fällen, in denen der Zerfall nicht auftritt .
Wenn Sie ein Array als Wert übergeben, kopieren Sie tatsächlich einen Zeiger - ein Zeiger auf das erste Element des Arrays wird in den Parameter kopiert (dessen Typ auch ein Zeiger auf den Typ des Array-Elements sein sollte). Dies funktioniert aufgrund der verfallenden Natur des Arrays. Einmal verfallen, sizeofgibt es nicht mehr die Größe des gesamten Arrays an, da es im Wesentlichen zu einem Zeiger wird. Aus diesem Grund wird es (unter anderem) bevorzugt, als Referenz oder Zeiger zu übergeben.
Drei Möglichkeiten, ein Array 1 zu übergeben :
void by_value(const T* array)// const T array[] means the samevoid by_pointer(const T (*array)[U])void by_reference(const T (&array)[U])
Die letzten beiden geben die richtigen sizeofInformationen, die erste nicht, da das Array-Argument verfallen ist, um dem Parameter zugewiesen zu werden.
1 Die Konstante U sollte zur Kompilierungszeit bekannt sein.
by_value übergibt einen Zeiger auf das erste Element des Arrays. im Kontext von Funktionsparametern T a[]ist identisch mit T *a. by_pointer übergibt dasselbe, außer dass der Zeigerwert jetzt qualifiziert ist const. Wenn Sie einen Zeiger auf das Array übergeben möchten (im Gegensatz zu einem Zeiger auf das erste Element des Arrays), lautet die Syntax T (*array)[U].
John Bode
4
"mit einem expliziten Zeiger auf dieses Array" - das ist falsch. Wenn aes sich um ein Array von handelt char, aist es vom Typ char[N]und zerfällt in char*; aber &aist vom Typ char(*)[N]und wird nicht verfallen.
Pavel Minaev
5
@FredOverflow: Wenn USie also Änderungen vornehmen müssen, müssen Sie nicht daran denken, sie an zwei Stellen zu ändern, oder riskieren Sie stille Fehler ... Autonomie!
Leichtigkeitsrennen im Orbit
4
"Wenn Sie ein Array nach Wert übergeben, kopieren Sie wirklich einen Zeiger." Das macht keinen Sinn, da Arrays nicht nach Wert übergeben werden können, Punkt.
Juanchopanza
103
Arrays sind im Grunde die gleichen wie Zeiger in C / C ++, aber nicht ganz. Sobald Sie ein Array konvertiert haben:
constint a[]={2,3,5,7,11};
in einen Zeiger (der ohne Casting funktioniert und daher in einigen Fällen unerwartet auftreten kann):
constint* p = a;
Sie verlieren die Fähigkeit des sizeofOperators, Elemente im Array zu zählen:
assert(sizeof(p)!=sizeof(a));// sizes are not equal
Diese verlorene Fähigkeit wird als "Zerfall" bezeichnet.
Arrays sind im Grunde nicht dasselbe wie Zeiger. Sie sind ganz andere Tiere. In den meisten Kontexten kann ein Array so behandelt werden, als wäre es ein Zeiger, und ein Zeiger kann so behandelt werden, als wäre es ein Array, aber das ist so nah wie möglich.
John Bode
20
@ John, bitte verzeihen Sie meine ungenaue Sprache. Ich habe versucht, die Antwort zu finden, ohne mich in einer langen Hintergrundgeschichte festzumachen, und "im Grunde ... aber nicht ganz" ist eine so gute Erklärung wie nie zuvor auf dem College. Ich bin sicher, dass jeder, der interessiert ist, ein genaueres Bild von Ihrem positiv bewerteten Kommentar erhalten kann.
System PAUSE
"funktioniert ohne Casting" bedeutet dasselbe wie "implizit passieren", wenn es um Typkonvertierungen geht
MM
47
Der Standard sagt Folgendes (C99 6.3.2.1/3 - Andere Operanden - L-Werte, Arrays und Funktionsbezeichner):
Außer wenn es sich um den Operanden des Operators sizeof oder des Operators unary & handelt oder um ein Zeichenfolgenliteral, das zum Initialisieren eines Arrays verwendet wird, wird ein Ausdruck mit dem Typ '' Array vom Typ '' in einen Ausdruck mit dem Zeiger vom Typ '' konvertiert Geben Sie '' ein, das auf das Anfangselement des Array-Objekts zeigt und kein l-Wert ist.
Dies bedeutet, dass der Array-Name so gut wie immer, wenn er in einem Ausdruck verwendet wird, automatisch in einen Zeiger auf das erste Element im Array konvertiert wird.
Beachten Sie, dass Funktionsnamen auf ähnliche Weise funktionieren, Funktionszeiger jedoch weitaus weniger und viel spezieller verwendet werden, da dies nicht annähernd so verwirrend ist wie die automatische Konvertierung von Array-Namen in Zeiger.
Der C ++ - Standard (4.2 Array-zu-Zeiger-Konvertierung) lockert die Konvertierungsanforderung auf (Hervorhebung von mir):
Ein l-Wert oder r-Wert vom Typ "Array von NT" oder "Array von unbekannter Grenze von T" kann in einen r-Wert vom Typ "Zeiger auf T" konvertiert werden.
Die Konvertierung also nicht haben passieren , wie es so ziemlich immer in C tut ( auf diese Weise können Funktionen Überlastung oder Vorlagen auf dem Array - Typ übereinstimmen).
Dies ist auch der Grund, warum Sie in C die Verwendung von Array-Parametern in Funktionsprototypen / -definitionen vermeiden sollten (meiner Meinung nach - ich bin mir nicht sicher, ob es eine allgemeine Übereinstimmung gibt). Sie verursachen Verwirrung und sind sowieso eine Fiktion - verwenden Sie Zeigerparameter und die Verwirrung verschwindet möglicherweise nicht vollständig, aber zumindest lügt die Parameterdeklaration nicht.
Was ist eine Beispielcodezeile, in der ein "Ausdruck mit dem Typ 'Array vom Typ'" "ein Zeichenfolgenliteral ist, das zum Initialisieren eines Arrays verwendet wird"?
Garrett
4
@ Garrett char x[] = "Hello";. Das Array von 6 Elementen "Hello"zerfällt nicht; stattdessen xerhält Größe 6und seine Elemente werden aus den Elementen von initialisiert "Hello".
MM
30
"Zerfall" bezieht sich auf die implizite Konvertierung eines Ausdrucks von einem Array-Typ in einen Zeigertyp. In den meisten Kontexten konvertiert der Compiler, wenn er einen Array-Ausdruck sieht, den Typ des Ausdrucks von "N-Element-Array von T" in "Zeiger auf T" und setzt den Wert des Ausdrucks auf die Adresse des ersten Elements des Arrays . Ausnahmen von dieser Regel sind, wenn ein Array ein Operand des sizeofoder ist& Operatoren oder das Array ein Stringliteral als Initialisierer in einer Erklärung verwendet wird.
Nehmen Sie den folgenden Code an:
char a[80];
strcpy(a,"This is a test");
Der Ausdruck aist vom Typ "80-Element-Array von char" und der Ausdruck "Dies ist ein Test" vom Typ "16-Element-Array von char" (in C; in C ++ sind String-Literale Arrays von const char). Beim Aufruf von strcpy()ist jedoch keiner der Ausdrücke ein Operand von sizeofoder &, sodass ihre Typen implizit in "Zeiger auf Zeichen" konvertiert werden und ihre Werte jeweils auf die Adresse des ersten Elements gesetzt werden. Was strcpy()empfängt, sind keine Arrays, sondern Zeiger, wie im Prototyp zu sehen:
char*strcpy(char*dest,constchar*src);
Dies ist nicht dasselbe wie ein Array-Zeiger. Zum Beispiel:
Beide ptr_to_first_elementund ptr_to_arrayhaben den gleichen Wert ; die Basisadresse von a. Es handelt sich jedoch um unterschiedliche Typen, die wie unten gezeigt unterschiedlich behandelt werden:
Denken Sie daran, dass der Ausdruck wie a[i]folgt interpretiert wird *(a+i)(was nur funktioniert, wenn der Array-Typ in einen Zeigertyp konvertiert wird), also beide a[i]und ptr_to_first_element[i]dasselbe. Der Ausdruck (*ptr_to_array)[i]wird interpretiert als *(*a+i). Die Ausdrücke *ptr_to_array[i]und ptr_to_array[i]können je nach Kontext zu Compiler-Warnungen oder -Fehlern führen. Sie werden definitiv das Falsche tun, wenn Sie erwarten, dass sie es bewerten a[i].
sizeof a ==sizeof*ptr_to_array ==80
Wenn ein Array ein Operand von ist sizeof, wird es nicht in einen Zeigertyp konvertiert.
sizeof*ptr_to_first_element ==sizeof(char)==1sizeof ptr_to_first_element ==sizeof(char*)== whatever the pointer size
is on your platform
ptr_to_first_element ist ein einfacher Zeiger auf char.
Ist nicht "This is a test" is of type "16-element array of char"ein "15-element array of char"? (Länge 14 + 1 für \ 0)
chux - Monica
16
Arrays in C haben keinen Wert.
Überall dort, wo der Wert eines Objekts erwartet wird, das Objekt jedoch ein Array ist, wird stattdessen die Adresse seines ersten Elements mit dem Typ verwendet pointer to (type of array elements) .
In einer Funktion werden alle Parameter als Wert übergeben (Arrays sind keine Ausnahme). Wenn Sie ein Array in einer Funktion übergeben, "zerfällt es in einen Zeiger" (sic); Wenn Sie ein Array mit etwas anderem vergleichen, "zerfällt es wieder in einen Zeiger" (sic). ...
void foo(int arr[]);
Die Funktion foo erwartet den Wert eines Arrays. In C haben Arrays jedoch keinen Wert! So foowird stattdessen die Adresse des ersten Elements des Arrays.
int arr[5];int*ip =&(arr[1]);if(arr == ip){/* something; */}
Hat im obigen Vergleich arrkeinen Wert, so dass es ein Zeiger wird. Es wird ein Zeiger auf int. Dieser Zeiger kann mit der Variablen verglichen werden ip.
In der Array-Indizierungssyntax, die Sie gewohnt sind, wird der arr wieder in einen Zeiger zerfallen.
arr[42];/* same as *(arr + 42); *//* same as *(&(arr[0]) + 42); */
Ein Array zerfällt nur dann nicht in einen Zeiger, wenn es der Operand des Operators sizeof oder der Operator & (der Operator 'address of') oder ein Zeichenfolgenliteral ist, das zum Initialisieren eines Zeichenarrays verwendet wird.
"Arrays haben keinen Wert" - was soll das heißen? Natürlich haben Arrays einen Wert ... sie sind Objekte, Sie können Zeiger haben und in C ++ Verweise auf sie usw.
Pavel Minaev
2
Ich glaube streng genommen, dass "Wert" in C als die Interpretation der Bits eines Objekts gemäß einem Typ definiert ist. Es fällt mir schwer, mit einem Array-Typ eine nützliche Bedeutung herauszufinden. Stattdessen können Sie sagen, dass Sie in einen Zeiger konvertieren, dies interpretiert jedoch nicht den Inhalt des Arrays, sondern nur dessen Position. Was Sie erhalten, ist der Wert eines Zeigers (und es ist eine Adresse), nicht der Wert eines Arrays (dies wäre "die Folge von Werten der enthaltenen Elemente", wie sie in der Definition von "Zeichenfolge" verwendet werden). Das heißt, ich denke, es ist fair, "Wert des Arrays" zu sagen, wenn man den Zeiger meint, den man bekommt.
Johannes Schaub - Litb
Ich denke jedenfalls, dass es eine leichte Mehrdeutigkeit gibt: Wert eines Objekts und Wert eines Ausdrucks (wie in "rvalue"). Wenn ein Array-Ausdruck auf die letztere Weise interpretiert wird, hat er sicherlich einen Wert: Er ist derjenige, der sich aus dem Zerlegen in einen r-Wert ergibt, und ist der Zeigerausdruck. Aber wenn es auf die frühere Weise interpretiert wird, gibt es natürlich keine nützliche Bedeutung für ein Array-Objekt.
Johannes Schaub - Litb
1
+1 für die Phrase mit einem kleinen Fix; Für Arrays ist es nicht einmal ein Triplett, sondern nur ein Couplet [Ort, Typ]. Hatten Sie noch etwas anderes für den dritten Ort im Fall von Array im Sinn? Mir fällt nichts ein.
Legends2k
1
@ legends2k: Ich glaube, ich habe die dritte Position in Arrays verwendet, um zu vermeiden, dass sie zu einem Sonderfall werden, bei dem nur ein Couplet vorhanden ist. Vielleicht wäre [Ort, Typ, Leere ] besser gewesen.
pmg
8
Es ist, wenn Array verrottet und darauf hingewiesen wird ;-)
Eigentlich ist es nur so, dass, wenn Sie irgendwo ein Array übergeben möchten, aber stattdessen der Zeiger übergeben wird (denn wer zum Teufel würde das gesamte Array für Sie übergeben), die Leute sagen, dass ein schlechtes Array in einen Zeiger zerfallen ist.
Schön gesagt. Was wäre ein schönes Array, das nicht in einen Zeiger zerfällt oder dessen Zerfall verhindert wird? Können Sie ein Beispiel in C anführen? Vielen Dank.
Unheilig
@Unheilig, klar, man kann ein Array in struct vakuumieren und die struct übergeben.
Michael Krelin - Hacker
Ich bin mir nicht sicher, was du mit "Arbeit" meinst. Es ist nicht erlaubt, über das Array hinaus zuzugreifen, obwohl es wie erwartet funktioniert, wenn Sie erwarten, was wirklich passieren wird. Dieses Verhalten (obwohl wiederum offiziell undefiniert) bleibt erhalten.
Michael Krelin - Hacker
Zerfall tritt auch in vielen Situationen auf, in denen das Array nirgendwo passiert wird (wie in anderen Antworten beschrieben). Zum Beispiel a + 1.
MM
3
Array-Zerfall bedeutet, dass ein Array, wenn es als Parameter an eine Funktion übergeben wird, identisch mit einem Zeiger behandelt wird ("zerfällt zu").
void do_something(int*array){// We don't know how big array is here, because it's decayed to a pointer.
printf("%i\n",sizeof(array));// always prints 4 on a 32-bit machine}int main (int argc,char**argv){int a[10];int b[20];int*c;
printf("%zu\n",sizeof(a));//prints 40 on a 32-bit machine
printf("%zu\n",sizeof(b));//prints 80 on a 32-bit machine
printf("%zu\n",sizeof(c));//prints 4 on a 32-bit machine
do_something(a);
do_something(b);
do_something(c);}
Es gibt zwei Komplikationen oder Ausnahmen zu den oben genannten.
Erstens geht beim Umgang mit mehrdimensionalen Arrays in C und C ++ nur die erste Dimension verloren. Dies liegt daran, dass Arrays zusammenhängend im Speicher angeordnet sind, sodass der Compiler alle bis auf die erste Dimension kennen muss, um Offsets in diesen Speicherblock berechnen zu können.
void do_something(int array[][10]){// We don't know how big the first dimension is.}int main(int argc,char*argv[]){int a[5][10];int b[20][10];
do_something(a);
do_something(b);return0;}
Zweitens können Sie in C ++ Vorlagen verwenden, um die Größe von Arrays abzuleiten. Microsoft verwendet dies für die C ++ - Versionen von Secure CRT-Funktionen wie strcpy_s , und Sie können einen ähnlichen Trick verwenden, um die Anzahl der Elemente in einem Array zuverlässig abzurufen .
Ich könnte so mutig sein zu glauben, dass es vier (4) Möglichkeiten gibt, ein Array als Funktionsargument zu übergeben. Auch hier ist der kurze, aber funktionierende Code für Ihre Durchsicht.
#include<iostream>#include<string>#include<vector>#include<cassert>usingnamespace std;// test data// notice native array init with no copy aka "="// not possible in Cconstchar* specimen[]{ __TIME__, __DATE__, __TIMESTAMP__ };// ONE// simple, dangerous and uselesstemplate<typename T>void as_pointer(const T*array){// a pointer
assert(array!=nullptr);};// TWO// for above const T array[] means the same// but and also , minimum array size indication might be given too// this also does not stop the array decay into T *// thus size information is losttemplate<typename T>void by_value_no_size(const T array[0xFF]){// decayed to a pointer
assert(array!=nullptr);}// THREE// size information is preserved// but pointer is asked fortemplate<typename T,size_t N>void pointer_to_array(const T (*array)[N]){// dealing with native pointer
assert(array!=nullptr);}// FOUR// no C equivalent// array by reference// size is preservedtemplate<typename T,size_t N>void reference_to_array(const T (&array)[N]){// array is not a pointer here// it is (almost) a container// most of the std:: lib algorithms // do work on array reference, for example// range for requires std::begin() and std::end()// on the type passed as range to iterate overfor(auto&& elem :array){
cout << endl << elem ;}}int main(){// ONE
as_pointer(specimen);// TWO
by_value_no_size(specimen);// THREE
pointer_to_array(&specimen);// FOUR
reference_to_array( specimen );}
Ich könnte auch denken, dass dies die Überlegenheit von C ++ gegenüber C zeigt. Zumindest in Bezug auf (Wortspiel beabsichtigt), ein Array als Referenz zu übergeben.
Natürlich gibt es extrem strenge Projekte ohne Heap-Zuordnung, ohne Ausnahmen und ohne std :: lib. Man könnte sagen, dass die native C ++ - Array-Behandlung eine geschäftskritische Sprachfunktion ist.
int a[10]; int b(void);
, dann+a
ist ein int-Zeiger und+b
ein Funktionszeiger. Nützlich, wenn Sie es an eine Vorlage übergeben möchten, die eine Referenz akzeptiert.std::decay
ab C ++ 14 wäre eine weniger obskure Möglichkeit, ein Array über unary + zu zerlegen.+a
und+b
in C ++ legal ist, ist es in C (C11 6.5.3.3/1 „Der Operand des unären illegal ist+
oder-
Betreiber hat hat arithmetischer Typ ")Antworten:
Es wird gesagt, dass Arrays in Zeiger "zerfallen". Ein als deklariertes C ++ - Array
int numbers [5]
kann nicht erneut angezeigt werden, dh Sie können es nicht sagennumbers = 0x5a5aff23
. Noch wichtiger ist, dass der Begriff Zerfall den Verlust von Typ und Dimension bedeutet.numbers
Zerfallint*
durch Verlust der Dimensionsinformationen (Anzahl 5) und der Typ ist nichtint [5]
mehr. Suchen Sie hier nach Fällen, in denen der Zerfall nicht auftritt .Wenn Sie ein Array als Wert übergeben, kopieren Sie tatsächlich einen Zeiger - ein Zeiger auf das erste Element des Arrays wird in den Parameter kopiert (dessen Typ auch ein Zeiger auf den Typ des Array-Elements sein sollte). Dies funktioniert aufgrund der verfallenden Natur des Arrays. Einmal verfallen,
sizeof
gibt es nicht mehr die Größe des gesamten Arrays an, da es im Wesentlichen zu einem Zeiger wird. Aus diesem Grund wird es (unter anderem) bevorzugt, als Referenz oder Zeiger zu übergeben.Drei Möglichkeiten, ein Array 1 zu übergeben :
Die letzten beiden geben die richtigen
sizeof
Informationen, die erste nicht, da das Array-Argument verfallen ist, um dem Parameter zugewiesen zu werden.1 Die Konstante U sollte zur Kompilierungszeit bekannt sein.
quelle
T a[]
ist identisch mitT *a
. by_pointer übergibt dasselbe, außer dass der Zeigerwert jetzt qualifiziert istconst
. Wenn Sie einen Zeiger auf das Array übergeben möchten (im Gegensatz zu einem Zeiger auf das erste Element des Arrays), lautet die SyntaxT (*array)[U]
.a
es sich um ein Array von handeltchar
,a
ist es vom Typchar[N]
und zerfällt inchar*
; aber&a
ist vom Typchar(*)[N]
und wird nicht verfallen.U
Sie also Änderungen vornehmen müssen, müssen Sie nicht daran denken, sie an zwei Stellen zu ändern, oder riskieren Sie stille Fehler ... Autonomie!Arrays sind im Grunde die gleichen wie Zeiger in C / C ++, aber nicht ganz. Sobald Sie ein Array konvertiert haben:
in einen Zeiger (der ohne Casting funktioniert und daher in einigen Fällen unerwartet auftreten kann):
Sie verlieren die Fähigkeit des
sizeof
Operators, Elemente im Array zu zählen:Diese verlorene Fähigkeit wird als "Zerfall" bezeichnet.
Weitere Informationen finden Sie in diesem Artikel zum Array-Zerfall .
quelle
Der Standard sagt Folgendes (C99 6.3.2.1/3 - Andere Operanden - L-Werte, Arrays und Funktionsbezeichner):
Dies bedeutet, dass der Array-Name so gut wie immer, wenn er in einem Ausdruck verwendet wird, automatisch in einen Zeiger auf das erste Element im Array konvertiert wird.
Beachten Sie, dass Funktionsnamen auf ähnliche Weise funktionieren, Funktionszeiger jedoch weitaus weniger und viel spezieller verwendet werden, da dies nicht annähernd so verwirrend ist wie die automatische Konvertierung von Array-Namen in Zeiger.
Der C ++ - Standard (4.2 Array-zu-Zeiger-Konvertierung) lockert die Konvertierungsanforderung auf (Hervorhebung von mir):
Die Konvertierung also nicht haben passieren , wie es so ziemlich immer in C tut ( auf diese Weise können Funktionen Überlastung oder Vorlagen auf dem Array - Typ übereinstimmen).
Dies ist auch der Grund, warum Sie in C die Verwendung von Array-Parametern in Funktionsprototypen / -definitionen vermeiden sollten (meiner Meinung nach - ich bin mir nicht sicher, ob es eine allgemeine Übereinstimmung gibt). Sie verursachen Verwirrung und sind sowieso eine Fiktion - verwenden Sie Zeigerparameter und die Verwirrung verschwindet möglicherweise nicht vollständig, aber zumindest lügt die Parameterdeklaration nicht.
quelle
char x[] = "Hello";
. Das Array von 6 Elementen"Hello"
zerfällt nicht; stattdessenx
erhält Größe6
und seine Elemente werden aus den Elementen von initialisiert"Hello"
."Zerfall" bezieht sich auf die implizite Konvertierung eines Ausdrucks von einem Array-Typ in einen Zeigertyp. In den meisten Kontexten konvertiert der Compiler, wenn er einen Array-Ausdruck sieht, den Typ des Ausdrucks von "N-Element-Array von T" in "Zeiger auf T" und setzt den Wert des Ausdrucks auf die Adresse des ersten Elements des Arrays . Ausnahmen von dieser Regel sind, wenn ein Array ein Operand des
sizeof
oder ist&
Operatoren oder das Array ein Stringliteral als Initialisierer in einer Erklärung verwendet wird.Nehmen Sie den folgenden Code an:
Der Ausdruck
a
ist vom Typ "80-Element-Array von char" und der Ausdruck "Dies ist ein Test" vom Typ "16-Element-Array von char" (in C; in C ++ sind String-Literale Arrays von const char). Beim Aufruf vonstrcpy()
ist jedoch keiner der Ausdrücke ein Operand vonsizeof
oder&
, sodass ihre Typen implizit in "Zeiger auf Zeichen" konvertiert werden und ihre Werte jeweils auf die Adresse des ersten Elements gesetzt werden. Wasstrcpy()
empfängt, sind keine Arrays, sondern Zeiger, wie im Prototyp zu sehen:Dies ist nicht dasselbe wie ein Array-Zeiger. Zum Beispiel:
Beide
ptr_to_first_element
undptr_to_array
haben den gleichen Wert ; die Basisadresse von a. Es handelt sich jedoch um unterschiedliche Typen, die wie unten gezeigt unterschiedlich behandelt werden:Denken Sie daran, dass der Ausdruck wie
a[i]
folgt interpretiert wird*(a+i)
(was nur funktioniert, wenn der Array-Typ in einen Zeigertyp konvertiert wird), also beidea[i]
undptr_to_first_element[i]
dasselbe. Der Ausdruck(*ptr_to_array)[i]
wird interpretiert als*(*a+i)
. Die Ausdrücke*ptr_to_array[i]
undptr_to_array[i]
können je nach Kontext zu Compiler-Warnungen oder -Fehlern führen. Sie werden definitiv das Falsche tun, wenn Sie erwarten, dass sie es bewertena[i]
.Wenn ein Array ein Operand von ist
sizeof
, wird es nicht in einen Zeigertyp konvertiert.ptr_to_first_element
ist ein einfacher Zeiger auf char.quelle
"This is a test" is of type "16-element array of char"
ein"15-element array of char"
? (Länge 14 + 1 für \ 0)Arrays in C haben keinen Wert.
Überall dort, wo der Wert eines Objekts erwartet wird, das Objekt jedoch ein Array ist, wird stattdessen die Adresse seines ersten Elements mit dem Typ verwendet
pointer to (type of array elements)
.In einer Funktion werden alle Parameter als Wert übergeben (Arrays sind keine Ausnahme). Wenn Sie ein Array in einer Funktion übergeben, "zerfällt es in einen Zeiger" (sic); Wenn Sie ein Array mit etwas anderem vergleichen, "zerfällt es wieder in einen Zeiger" (sic). ...
Die Funktion foo erwartet den Wert eines Arrays. In C haben Arrays jedoch keinen Wert! So
foo
wird stattdessen die Adresse des ersten Elements des Arrays.Hat im obigen Vergleich
arr
keinen Wert, so dass es ein Zeiger wird. Es wird ein Zeiger auf int. Dieser Zeiger kann mit der Variablen verglichen werdenip
.In der Array-Indizierungssyntax, die Sie gewohnt sind, wird der arr wieder in einen Zeiger zerfallen.
Ein Array zerfällt nur dann nicht in einen Zeiger, wenn es der Operand des Operators sizeof oder der Operator & (der Operator 'address of') oder ein Zeichenfolgenliteral ist, das zum Initialisieren eines Zeichenarrays verwendet wird.
quelle
Es ist, wenn Array verrottet und darauf hingewiesen wird ;-)
Eigentlich ist es nur so, dass, wenn Sie irgendwo ein Array übergeben möchten, aber stattdessen der Zeiger übergeben wird (denn wer zum Teufel würde das gesamte Array für Sie übergeben), die Leute sagen, dass ein schlechtes Array in einen Zeiger zerfallen ist.
quelle
a + 1
.Array-Zerfall bedeutet, dass ein Array, wenn es als Parameter an eine Funktion übergeben wird, identisch mit einem Zeiger behandelt wird ("zerfällt zu").
Es gibt zwei Komplikationen oder Ausnahmen zu den oben genannten.
Erstens geht beim Umgang mit mehrdimensionalen Arrays in C und C ++ nur die erste Dimension verloren. Dies liegt daran, dass Arrays zusammenhängend im Speicher angeordnet sind, sodass der Compiler alle bis auf die erste Dimension kennen muss, um Offsets in diesen Speicherblock berechnen zu können.
Zweitens können Sie in C ++ Vorlagen verwenden, um die Größe von Arrays abzuleiten. Microsoft verwendet dies für die C ++ - Versionen von Secure CRT-Funktionen wie strcpy_s , und Sie können einen ähnlichen Trick verwenden, um die Anzahl der Elemente in einem Array zuverlässig abzurufen .
quelle
tl; dr: Wenn Sie ein von Ihnen definiertes Array verwenden, verwenden Sie tatsächlich einen Zeiger auf das erste Element.
Somit:
arr[idx]
, sagst du wirklich nur*(arr + idx)
.Art von Ausnahmen von dieser Regel:
struct
.sizeof()
gibt die Größe an, die das Array einnimmt, nicht die Größe eines Zeigers.quelle
Ich könnte so mutig sein zu glauben, dass es vier (4) Möglichkeiten gibt, ein Array als Funktionsargument zu übergeben. Auch hier ist der kurze, aber funktionierende Code für Ihre Durchsicht.
Ich könnte auch denken, dass dies die Überlegenheit von C ++ gegenüber C zeigt. Zumindest in Bezug auf (Wortspiel beabsichtigt), ein Array als Referenz zu übergeben.
Natürlich gibt es extrem strenge Projekte ohne Heap-Zuordnung, ohne Ausnahmen und ohne std :: lib. Man könnte sagen, dass die native C ++ - Array-Behandlung eine geschäftskritische Sprachfunktion ist.
quelle