Warum können wir Arrays nicht per Wert an Funktion übergeben?

Lesezeit: 10 Minuten

Warum konnen wir Arrays nicht per Wert an Funktion ubergeben
Alcott

Anscheinend können wir komplexe Klasseninstanzen an Funktionen übergeben, aber warum können wir keine Arrays an Funktionen übergeben?

  • FWIW, das hat seinen Ursprung in C und als Arrays (die primitiven, nicht std::vector oder std::array) ziemlich unverändert in C++ übernommen, vermute ich, dass der Grund derselbe ist.

    Benutzer395760

    17. September 2011 um 13:11 Uhr

  • @delnan, der Grund ist derselbe? was ist der “gleiche” grund? Bitte sei spezifischer.

    – Alcott

    17. September 2011 um 13:31 Uhr

  • Ich glaube, dass Sie es mit boost::array (oder tr1::array) tun können.

    – Brent Bradburn

    17. September 2011 um 19:43 Uhr

  • …(oder std::vector) usw. Ihre Frage bezieht sich auf eine Designentscheidung, die jemand vor etwa 40 Jahren für C getroffen hat. Die Antwort auf Ihre Frage (für C++) ist “who cares”. Dies ist kein Problem für modernes C++, da es im Allgemeinen eine gute Idee ist, die Deklaration von rohen Arrays (und rohen Zeigern) nach Möglichkeit zu vermeiden. Es ist vorzuziehen, eine Array-Klasse höherer Ebene zu verwenden, wie z. B. eine der von mir aufgeführten.

    – Brent Bradburn

    17. September 2011 um 20:43 Uhr

  • Weil (1) Dennis Ritchie vor über 40 Jahren eine Pointer/Array-Äquivalenz tief in die C-Sprache eingebaut hat und (2) es eine wirklich schlechte Idee wäre.

    – Benutzer207421

    26. März 2015 um 7:27 Uhr

Der Ursprung ist historisch. Das Problem ist, dass die Regel „Arrays zerfallen in Zeiger, wenn sie an eine Funktion übergeben werden“ einfach ist.

Das Kopieren von Arrays wäre etwas kompliziert und nicht sehr übersichtlich, da sich das Verhalten für verschiedene Parameter und verschiedene Funktionsdeklarationen ändern würde.

Beachten Sie, dass Sie immer noch eine indirekte Wertübergabe durchführen können:

struct A { int arr[2]; };
void func(struct A);

  • In C++ können Sie Arrays per Referenz an Funktionen übergeben. Mit Funktions-Templates und Nicht-Typ-Template-Argumenten können Sie sogar beliebig lange Arrays übergeben.

    – sbi

    17. September 2011 um 13:30 Uhr

  • Die Regel, dass Array-Ausdrücke in Zeiger zerfallen, ist nicht spezifisch für Funktionsaufrufe. In C geschieht der Zerfall in jedem anderen Kontext als (a) dem Operanden des Unären & Adresse des Betreibers; (b) der Operand des Unären sizeof Operator; oder (c) ein Zeichenfolgenliteral in einem Initialisierer, der zum Initialisieren eines Zeichenarrays verwendet wird (char s[] = "hello";); Ich denke, es gibt ein oder zwei andere Ausnahmen in C++, wahrscheinlich mit Referenzen. Zum Beispiel im int arr[10]; int *p; p = arr; Der Zerfall tritt auf, aber es ist kein Funktionsaufruf in Sicht.

    – Keith Thompson

    17. September 2011 um 21:02 Uhr


  • Das erklärt irgendwie überhaupt nicht den aktuellen Stand der Dinge. Es ist kein Problem, Arrays kopierbar zu machen und das seltsame implizite Zerfallen in Zeiger loszuwerden. Dies würde jedoch wahrscheinlich die Einführung einer speziellen Syntax erfordern, um ein Array in einen Zeiger zu konvertieren (wie z @arr), die die Absicht ausdrückt, einen Zeiger auf alle Elemente zu erhalten, anstatt auf das erste Element (&(arr[0])), aber alle Operatorsymbole wurden bereits verwendet. Oder einfach ihre damalige Codebasis erforderte kein Kopieren von Arrays, also entschieden sie sich, Abstriche zu machen, was sich auf lange Sicht als schlechte Entscheidung herausstellte.

    – Benutzer7860670

    25. Oktober 2018 um 8:02 Uhr


1646181014 69 Warum konnen wir Arrays nicht per Wert an Funktion ubergeben
Kerrek SB

Hier ist eine andere Perspektive: Es gibt keinen einzigen Typ “Array” in C. Vielmehr T[N] ist AA unterschiedlich Typ für alle N. Damit T[1], T[2]usw., sind alle verschiedene Typen.

In C gibt es kein Überladen von Funktionen, und das einzig Vernünftige, was Sie hätten zulassen können, wäre eine Funktion, die a akzeptiert (oder zurückgibt). einzige Art von Array:

void foo(int a[3]);  // hypothetical

Vermutlich wurde dies nur als weit weniger nützlich angesehen als die eigentliche Entscheidung, alle Arrays in einen Zeiger auf das erste Element zerfallen zu lassen und den Benutzer aufzufordern, die Größe auf andere Weise mitzuteilen. Schließlich könnte das obige umgeschrieben werden als:

void foo(int * a)
{
  static const unsigned int N = 3;
  /* ... */
}

Es gibt also keinen Verlust an Ausdruckskraft, aber einen enormen Gewinn an Allgemeingültigkeit.

Beachten Sie, dass dies in C++ nicht anders ist, aber die vorlagengesteuerte Codegenerierung ermöglicht es Ihnen, eine vorlagenbasierte Funktion zu schreiben foo(T (&a)[N])wo N wird für Sie abgeleitet — aber das bedeutet nur, dass Sie eine ganze Familie von erstellen können deutlich, anders Funktionen, eine für jeden Wert von N.

Stellen Sie sich als Extremfall vor, Sie bräuchten zwei Funktionen print6(const char[6]) und print12(const char[12]) sagen print6("Hello") und print12("Hello World") Wenn Sie Arrays nicht in Zeiger zerlegen möchten oder andernfalls eine explizite Konvertierung hinzufügen müssten, print_p((const char*)"Hello World").

  • Es ist erwähnenswert, dass einige andere Sprachen tun erlauben die Übergabe von Arrays als Parameter. In Ada zum Beispiel das Äquivalent von int[5] und int[10] sind (oder zumindest sein kann) vom gleichen Typ; sie sind nur verschiedene Subtypen. Sie können eine Ada-Routine definieren, die ein Integer-Array mit beliebigen Grenzen als Parameter verwendet oder (für eine Funktion) ein solches Array zurückgibt. Der dafür zu zahlende Preis besteht darin, dass der Compiler Code generieren muss, um die gesamte erforderliche Buchhaltung und Speicherverwaltung durchzuführen. Solcher impliziter Code wird im Allgemeinen nicht als “im Geiste von C” (oder von C++) angesehen.

    – Keith Thompson

    17. September 2011 um 21:07 Uhr

1646181014 761 Warum konnen wir Arrays nicht per Wert an Funktion ubergeben
Ayub

Um eine sehr alte Frage zu beantworten, da Question mit C++ nur zu Vervollständigungszwecken hinzugefügt wird, können wir std::array verwenden und Arrays per Wert oder Referenz an Funktionen übergeben, was Schutz vor dem Zugriff auf ungebundene Indizes bietet:

Unten ist Beispiel:

#include <iostream>
#include <array>

//pass array by reference
template<size_t N>
void fill_array(std::array<int, N>& arr){
    for(int idx = 0; idx < arr.size(); ++idx)
        arr[idx] = idx*idx;
}

//pass array by value
template<size_t N>
void print_array(std::array<int, N> arr){
    for(int idx = 0; idx < arr.size(); ++idx)
        std::cout << arr[idx] << std::endl;
}

int main()
{
    std::array<int, 5> arr;
    fill_array(arr);
    print_array(arr);
    //use different size
    std::array<int, 10> arr2;
    fill_array(arr2);
    print_array(arr2);
}

Warum konnen wir Arrays nicht per Wert an Funktion ubergeben
Almog

Sommerlich:

  1. Vorbei an der Adresse des ersten Elements des Arrays &a = a = &(a[0])
  2. Neuer Zeiger (neuer Zeiger, neue Adresse4 Bytes, im Speicher)
  3. Hinweise auf die gleichen Speicherortin Anderer Typ.

Beispiel 1:

void by_value(bool* arr) // pointer_value passed by value
{
    arr[1] = true;
    arr = NULL; // temporary pointer that points to original array
}

int main()
{
    bool a[3] = {};
    cout << a[1] << endl; // 0
    by_value(a);
    cout << a[1] << endl; // 1 !!! 
}

Adressen:

[main] 
     a = 0046FB18 // **Original**
     &a = 0046FB18 // **Original**
[func]
     arr = 0046FB18 // **Original**
     &arr = 0046FA44 // TempPTR
[func]
     arr = NULL
     &arr = 0046FA44 // TempPTR

Beispiel 2:

void by_value(bool* arr) 
{
    cout << &arr << arr; // &arr != arr
}

int main()
{
    bool a[3] = {};
    cout << &a << a; // &a == a == &a[0]
    by_value(arr);
}

Adressen

Prints: 
[main] 0046FB18 = 0046FB18
[func] 0046FA44 != 0046FB18

Bitte beachten Sie:

  1. &(required-lvalue): lvalue -to-> rvalue
  2. Array-Verfall: neuer Zeiger (temporär) zeigt auf (nach Wert) Array-Adresse

Weiterlesen:

RWert

Array-Verfall

  • Wenn das Array als a deklariert ist[4]dann kennen Sie einfach die Größe zur Kompilierzeit.

    – quant_dev

    17. September 2011 um 14:36 ​​Uhr

  • Jawohl, wenn. Aber die Größe wird nicht zusammen mit dem Array weitergegeben, sie werden nicht so “zusammengeklebt”, wie es erforderlich wäre, um Arrays als Wert zu übergeben.

    – David Schwartz

    17. September 2011 um 14:42 Uhr

  • @quant: im ursprünglichen Scope ist das bekannt, aber wohin geht die 4 in der aufgerufenen Funktion?

    – Dennis Zickefoose

    17. September 2011 um 14:46 Uhr

  • Was ist, wenn wir ein Array speziell mit der Größe deklarieren? Beispiel: func(int array[20]). Kann die Größe immer noch nicht in Funktion abrufen? @DennisZickefoose

    – Sazzad Hissain Khan

    18. Oktober 2016 um 13:10 Uhr

  • @SazzadHissainKhan Vielleicht in einer anderen Sprache als C++, wo Arrays ganz anders funktionieren als in C++. Aber in C++ können Sie a übergeben char* von dem du gekommen bist malloc zu einer Funktion, die eine erwartet int[4]. Und sizeof macht nicht was du erwartest.

    – David Schwartz

    18. Oktober 2016 um 17:28 Uhr


Warum konnen wir Arrays nicht per Wert an Funktion ubergeben
Ameise

Dies wurde so gemacht, um die syntaktische und semantische Kompatibilität mit der B-Sprache zu wahren, in der Arrays als physische Zeiger implementiert wurden.

Eine direkte Antwort auf diese Frage gibt Dennis Ritchie’s “Die Entwicklung der C-Sprache”, siehe Abschnitt “Kritik”. Es sagt

Zum Beispiel die leeren eckigen Klammern in der Funktionsdeklaration

int f(a) int a[]; { ... }

sind ein lebendes Fossil, ein Überbleibsel von NBs Art, einen Pointer zu deklarieren; a wird in C nur in diesem speziellen Fall als Zeiger interpretiert. Die Notation überlebte teilweise aus Gründen der Kompatibilität, teilweise unter der Begründung, dass sie es Programmierern ermöglichen würde, ihren Lesern eine Absicht zum Bestehen mitzuteilen f ein Zeiger, der aus einem Array generiert wird, und kein Verweis auf eine einzelne Ganzzahl. Leider dient es sowohl der Verwirrung des Lernenden als auch der Warnung des Lesers.

Dies sollte im Zusammenhang mit dem vorherigen Teil des Artikels gesehen werden, insbesondere “Embryonic C”, der die Einführung von erklärt struct Typen in C führten zur Ablehnung des Ansatzes im B- und BCPL-Stil zur Implementierung von Arrays (dh als gewöhnliche Zeiger). C wechselte zu einer Nicht-Zeiger-Array-Implementierung und behielt diese Legacy-Semantik im B-Stil nur in Funktionsparameterlisten bei.

Die aktuelle Variante des Verhaltens von Array-Parametern ist also das Ergebnis eines Kompromisses: Einerseits mussten wir kopierbare Arrays einbauen structs hingegen wollten wir die semantische Kompatibilität mit Funktionen bewahren, die in B geschrieben sind, wo Arrays immer “per Zeiger” übergeben werden.

Das Äquivalent dazu wäre, zuerst eine Kopie des Arrays zu erstellen und es dann an die Funktion zu übergeben (was bei großen Arrays sehr ineffizient sein kann).

Abgesehen davon würde ich sagen, dass es historische Gründe hat, dh man konnte Arrays nicht nach Wert in C übergeben.

Meine Vermutung ist, dass der Grund hinter der NICHT-Einführung von Arrays nach Wert in C++ darin bestand, dass Objekte im Vergleich zu Arrays als mäßig groß angesehen wurden.

Wie von delnan betont, bei der Verwendung std::vector Sie können Array-ähnliche Objekte tatsächlich per Wert an Funktionen übergeben.

1646181015 205 Warum konnen wir Arrays nicht per Wert an Funktion ubergeben
KonradG

Sie sind Wertübergabe: Der Wert des Zeigers auf das Array. Denken Sie daran, dass die Verwendung der Notation mit eckigen Klammern in C einfach eine Abkürzung für die Dereferenzierung eines Zeigers ist. ptr[2] bedeutet *(ptr+2).

Wenn Sie die Klammern fallen lassen, erhalten Sie einen Zeiger auf das Array, das als Wert an eine Funktion übergeben werden kann:

int x[2] = {1, 2};
int result;
result = DoSomething(x);

Siehe die Liste der Typen in der ANSI C-Spezifikation. Arrays sind keine primitiven Typen, sondern aus einer Kombination von Zeigern und Operatoren aufgebaut. (Ich kann keinen weiteren Link setzen, aber die Konstruktion ist unter “Array-Typ-Ableitung” beschrieben.)

  • Sie übergeben nicht die Adresse des Arrays, sondern die Adresse des ersten Elements des Arrays (gleicher Speicherplatz, anderer Typ). Die Array-Indizierungsoperation ist per Definition eine Kombination aus Zeigerarithmetik und dem Unären * Dereferenzierungsoperator, aber ein Array selbst ist nur ein Array. Was Arrays weniger als erstklassige Typen in C macht, sind nicht die Array-Objekte selbst, sondern die begrenzte Anzahl von Operationen auf ihnen.

    – Keith Thompson

    17. September 2011 um 20:59 Uhr

  • Sie sprechen von Arrays als Programmierkonzept: Datenstrukturen aus Sicht des Programmierers. Ich dachte, die Frage wäre, warum sich die C-Syntax für Array-Daten von anderen Daten zu unterscheiden scheint. Wenn Sie es als eine Frage der Sprachstruktur betrachten, liegt das daran, dass Arrays keine Primitiven sind. Sie sind Zeigeroperationen, die sich fast wie Primitive verhalten.

    – KonradG

    18. September 2011 um 3:30 Uhr

  • Die meisten Operationen on Arrays werden als Zeigeroperationen implementiert. Arrays sind nicht Zeiger. Beispielsweise ist ein Array-Objekt ein Array-Objekt, und durch das Definieren eines Array-Objekts wird kein Zeigerobjekt explizit oder implizit erstellt. (Und ich bin mir überhaupt nicht sicher, ob ich verstehe, welchen Unterschied Sie zwischen “Datenstrukturen” und “Sprachstruktur” machen.)

    – Keith Thompson

    18. September 2011 um 3:32 Uhr

  • Ich sage nicht, dass Arrays Zeiger sind. Aber der “Array-Typ” in C ist nur der Array-Operator, der so gekleidet ist, dass er sich wie ein Datentyp verhält. Das Ergebnis ist eine ziemlich überzeugende Imitation, aber es ist nicht perfekt. 🙂 Eine Eigenart, wie Sie bereits erwähnt haben, ist, dass das Array selbst keine Adresse hat.

    – KonradG

    19. September 2011 um 22:46 Uhr

  • @KeithThompson Was die Unterscheidung betrifft, so ist es der Unterschied zwischen der Frage, was “unter der Haube” vor sich geht, und der Frage, “warum wurde es so gemacht”.

    – KonradG

    19. September 2011 um 22:58 Uhr

906460cookie-checkWarum können wir Arrays nicht per Wert an Funktion übergeben?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy