Länge des Arrays im Funktionsargument

Lesezeit: 8 Minuten

Benutzeravatar von Jan Turoň
Jan Turoň

Dies ist ein bekannter Code zum Berechnen der Array-Länge in C:

sizeof(array)/sizeof(type)

Aber ich kann anscheinend nicht die Länge des Arrays herausfinden, das als Argument an eine Funktion übergeben wird:

#include <stdio.h>

int length(const char* array[]) {
  return sizeof(array)/sizeof(char*);
}

int main() {
  const char* friends[] = { "John", "Jack", "Jim" };
  printf("%d %d", sizeof(friends)/sizeof(char*), length(friends)); // 3 1
}

Ich gehe davon aus, dass das Array als Wert in das Funktionsargument kopiert wird, da ein konstanter Zeiger und ein Verweis darauf dies lösen sollten, aber diese Deklaration ist nicht gültig:

int length(const char**& array);

Ich finde, dass das Übergeben der Array-Länge als zweites Argument redundante Informationen sind, aber warum ist die Standarddeklaration von main so was:

int main(int argc, char** argv);

Bitte erklären Sie, ob es möglich ist, die Array-Länge im Funktionsargument herauszufinden, und wenn ja, warum gibt es die Redundanz darin main.


  • Arrays sind C-Bürger zweiter Klasse. In den meisten Kontexten zerfallen sie in einen Zeiger auf ihr erstes Element. Das passiert in Funktionsaufrufen (und Prototypen). In Ihrem Beispiel ist die friends Array zerfällt zu einem Zeiger, der an die Funktion übergeben wird (innerhalb der Funktion ist es immer noch ein Zeiger)

    – pmg

    25. November 2011 um 12:23 Uhr


  • func(Typ* Array[]) == func(type** array), Information als Array, der Stack kann verloren gehen.

    – BLUEPIXY

    25. November 2011 um 13:18 Uhr


  • Das mögliche Duplikat ist verwandt, aber nicht so eng verwandt wie all das.

    – Jonathan Leffler

    3. März 2015 um 7:40 Uhr

  • Beachten Sie, dass die Nullzeigerlösung keine allgemeine Lösung für das Problem ist, die Größe eines Arrays zu bestimmen, das von innerhalb der Funktion an eine Funktion übergeben wird. Im Allgemeinen können Sie die Größe eines Arrays, das an die Funktion übergeben wird, nicht innerhalb einer Funktion bestimmen, und die Größe wird normalerweise als weiterer Parameter übergeben (beachten Sie unter anderem fgets(), qsort(), fread(), fwrite() in der Standard-C-Bibliothek).

    – Jonathan Leffler

    3. März 2015 um 7:42 Uhr


Benutzeravatar von hugomg
Umarmung

sizeof funktioniert nur, um die Länge des Arrays zu ermitteln, wenn Sie es auf das ursprüngliche Array anwenden.

int a[5]; //real array. NOT a pointer
sizeof(a); // :)

Wenn das Array jedoch in einen Zeiger zerfällt, gibt sizeof die Größe des Zeigers und nicht die des Arrays an.

int a[5];
int * p = a;
sizeof(p); // :(

Wie Sie bereits intelligent darauf hingewiesen haben, erhält main die Länge des Arrays als Argument (argc). Ja, das ist notwendig und nicht überflüssig. (Nun, es ist irgendwie überflüssig, da argv praktischerweise durch einen Nullzeiger beendet wird, aber ich schweife ab.)

Es gibt einige Gründe, warum dies geschehen würde. Wie könnten wir Dinge so machen, dass ein C-Array auch seine Länge kennt?

Eine erste Idee wäre, Arrays nicht in Zeiger zerfallen zu lassen, wenn sie an eine Funktion übergeben werden, und die Arraylänge weiterhin im Typsystem beizubehalten. Das Schlimme daran ist, dass Sie für jede mögliche Array-Länge eine separate Funktion benötigen würden, was keine gute Idee ist. (Pascal hat dies getan und einige Leute denken, dass dies einer der Gründe ist, warum es gegen C “verloren” hat)

Eine zweite Idee besteht darin, die Array-Länge neben dem Array zu speichern, genau wie es jede moderne Programmiersprache tut:

a -> [5];[0,0,0,0,0]

Aber dann erschaffst du nur ein Unsichtbares struct hinter den Kulissen und die C-Philosophie billigt diese Art von Overhead nicht. Das Erstellen einer solchen Struktur selbst ist jedoch oft eine gute Idee für einige Arten von Problemen:

struct {
    size_t length;
    int * elements;
}

Eine andere Sache, über die Sie nachdenken können, ist, wie Zeichenfolgen in C nullterminiert werden, anstatt eine Länge zu speichern (wie in Pascal). Um eine Länge zu speichern, ohne sich Gedanken über Grenzen machen zu müssen, benötigen Sie a satt vier Bytes, eine unvorstellbar teure Menge (zumindest damals). Man könnte sich fragen, ob Arrays auch so nullterminiert werden könnten, aber wie würden Sie dann zulassen, dass das Array eine Null speichert?

Wills Benutzeravatar
Werden

Das Array zerfällt zu einem Zeiger, wenn es übergeben wird.

Abschnitt 6.4 der C-FAQ deckt dies sehr gut ab und liefert die K&R-Referenzen usw.


Abgesehen davon, stellen Sie sich vor, es wäre möglich, dass die Funktion die Größe des in einem Zeiger zugewiesenen Speichers kennt. Sie könnten die Funktion zwei- oder mehrmals aufrufen, jedes Mal mit unterschiedlichen Eingabearrays, die möglicherweise unterschiedliche Längen haben. die Länge müsste also irgendwie als geheime versteckte Variable übergeben werden. Und dann überlegen Sie, ob Sie einen Offset in ein anderes Array oder ein Array übergeben haben, das auf dem Heap (malloc und alle sind Bibliotheksfunktionen – etwas, auf das der Compiler verlinkt, anstatt den Körper von zu sehen und zu begründen).

Es wird schwierig, sich vorzustellen, wie das ohne ein paar Slice-Objekte hinter den Kulissen und dergleichen funktionieren könnte, richtig?


Symbian hatte eine AllocSize() Funktion, die die Größe einer Zuweisung mit zurückgegeben hat malloc(); dies funktionierte nur für den vom malloc zurückgegebenen wörtlichen Zeiger, und Sie würden Gobbledygook oder einen Absturz erhalten, wenn Sie ihn nach der Größe eines ungültigen Zeigers oder einem Zeiger-Offset von einem fragen wollten.

Sie wollen nicht glauben, dass es nicht möglich ist, aber es ist wirklich nicht möglich. Die einzige Möglichkeit, die Länge von etwas zu erfahren, das an eine Funktion übergeben wird, besteht darin, die Länge selbst zu verfolgen und sie selbst als separaten expliziten Parameter zu übergeben.

  • Ich weiß, aber kann ich es mit Referenzen lösen? Oder gibt es eine andere Einstellung? Ich kann nicht glauben, dass es nicht möglich ist, die Länge des Blocks des zugewiesenen Speichers herauszufinden, der als Zeiger auf einer so niedrigen Programmierebene übergeben wird!

    – Jan Turoň

    25. November 2011 um 12:27 Uhr

Wie von @Will angegeben, erfolgt der Abfall während der Parameterübergabe. Eine Möglichkeit, dies zu umgehen, besteht darin, die Anzahl der Elemente zu übergeben. Um dies zu ergänzen, finden Sie möglicherweise die _countof() Makro nützlich – es macht das Äquivalent zu dem, was Sie getan haben;)

  • _countof() ist nicht C, kann aber eine herstellerspezifische Erweiterung sein. Die Verwendung unterbricht die Portabilität des Codes.

    – alk

    30. Dezember 2017 um 17:55 Uhr


Erstens ist eine bessere Verwendung zum Berechnen der Anzahl der Elemente, wenn sich die tatsächliche Array-Deklaration im Gültigkeitsbereich befindet, wie folgt:

sizeof array / sizeof array[0]

Auf diese Weise wiederholen Sie nicht den Typnamen, der sich natürlich in der Deklaration ändern und zu einer falschen Längenberechnung führen könnte. Dies ist ein typischer Fall von wiederhole dich nicht.

Zweitens, als kleinen Punkt, bitte beachten Sie das sizeof ist keine Funktion, daher benötigt der obige Ausdruck keine Klammern um das Argument to sizeof.

Drittens hat C keine Referenzen, also Ihre Verwendung von & in einer Deklaration funktioniert nicht.

Ich stimme zu, dass die richtige C-Lösung darin besteht, die Länge zu übergeben (unter Verwendung der size_t type) als separates Argument und use sizeof an der Stelle, an der der Aufruf erfolgt, wenn das Argument ein “echtes” Array ist.

Beachten Sie, dass Sie oft mit Speicher arbeiten, der von zB zurückgegeben wird malloc()und in diesen Fällen haben Sie nie ein “wahres” Array, aus dem Sie die Größe berechnen können. Daher ist es flexibler, die Funktion so zu entwerfen, dass sie eine Elementanzahl verwendet.

Bezüglich int main():

Nach der Norm, argv zeigt auf eine NULL-beendet Array (von Zeigern auf nullterminierte Strings). (5.1.2.2.1:1).

Das ist, argv = (char **){ argv[0], ..., argv[argc - 1], 0 };.

Daher wird die Größenberechnung durch eine Funktion durchgeführt, die eine triviale Modifikation von ist strlen().

argc ist nur zum machen da argv Längenberechnung O(1).

Die Zählen-bis-NULL-Methode wird NICHT Arbeit für generische Array-Eingabe. Sie müssen die Größe als zweites Argument manuell angeben.

Benutzeravatar von xryl669
xryl669

Dies ist eine alte Frage, und das OP scheint C++ und C in seinen Absichten/Beispielen zu mischen. Wenn Sie in C ein Array an eine Funktion übergeben, wird es in einen Zeiger zerlegt. Es gibt also keine Möglichkeit, die Arraygröße zu übergeben, außer indem Sie ein zweites Argument in Ihrer Funktion verwenden, das die Arraygröße speichert:

void func(int A[]) 
// should be instead: void func(int * A, const size_t elemCountInA)

Dies sind nur sehr wenige Fälle, in denen Sie dies nicht benötigen, z. B. wenn Sie mehrdimensionale Arrays verwenden:

void func(int A[3][whatever here]) // That's almost as if read "int* A[3]"

Die Verwendung der Array-Notation in einer Funktionssignatur ist für den Entwickler immer noch nützlich, da es hilfreich sein kann, festzustellen, wie viele Elemente Ihre Funktion erwartet. Zum Beispiel:

void vec_add(float out[3], float in0[3], float in1[3])

ist einfacher zu verstehen als diese (obwohl nichts den Zugriff auf das 4. Element in der Funktion in beiden Funktionen verhindert):

void vec_add(float * out, float * in0, float * in1)

Wenn Sie C++ verwenden, können Sie die Arraygröße tatsächlich erfassen und erhalten, was Sie erwarten:

template <size_t N>
void vec_add(float (&out)[N], float (&in0)[N], float (&in1)[N])
{
    for (size_t i = 0; i < N; i++) 
        out[i] = in0[i] + in1[i];
}

In diesem Fall stellt der Compiler sicher, dass Sie keinen 4D-Vektor mit einem 2D-Vektor hinzufügen (was in C nicht möglich ist, ohne die Dimension jeder Dimension als Argumente der Funktion zu übergeben). Es gibt so viele Instanzen der vec_add-Funktion wie die Anzahl der Dimensionen, die für Ihre Vektoren verwendet werden.

Benutzeravatar von Baris
Baris

int arsize(int st1[]) {
    int i = 0;
    for (i; !(st1[i] & (1 << 30)); i++);
    return i;
}

Das funktioniert bei mir 🙂

  • mehr beschreibung bedarf.

    – KARTHIKEYAN.A

    22. Oktober 2017 um 7:03 Uhr

  • Ohne zu wissen, wie die Erinnerung darauf hinwies st1vom Anrufer initialisiert/gesetzt wurde, macht diese Antwort keinen Sinn.

    – alk

    30. Dezember 2017 um 17:49 Uhr

1415690cookie-checkLänge des Arrays im Funktionsargument

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

Privacy policy