Wenn eine Funktion einen Array-Parameter bestimmter Größe hat, warum wird sie durch einen Zeiger ersetzt?

71

Angesichts des folgenden Programms,

#include <iostream>

using namespace std;

void foo( char a[100] )
{
    cout << "foo() " << sizeof( a ) << endl;
}

int main()
{
    char bar[100] = { 0 };
    cout << "main() " << sizeof( bar ) << endl;
    foo( bar );
    return 0;
}

Ausgänge

main() 100
foo() 4
  1. Warum wird das Array als Zeiger auf das erste Element übergeben?
  2. Ist es ein Erbe von C?
  3. Was sagt der Standard?
  4. Warum wird die strikte Typensicherheit von C ++ aufgehoben?
CsTamas
quelle
Ich benutze in diesen Fällen immer std :: array, vermeide es,
mich
2
Welche strenge Typensicherheit? Wer hat strenge Typensicherheit versprochen? In C ++ gibt es so etwas nicht.
n. 'Pronomen' m.
TL; DR für die folgenden Antworten: Arrays werden zu Zeigern, wenn sie an die Funktion übergeben werden. Wenn Sie also ihre Größe überprüfen, erhalten Sie nur die Größe eines Zeigers. Wenn Sie nur mit C arbeiten, kann ich nur vorschlagen, dass Sie die Größe, die Sie aus dem Array entfernen möchten, als weiteren Parameter vorberechnen.
Super Cat
Relevante Linus Rant
Millie Smith

Antworten:

85

Ja, es ist von C geerbt. Die Funktion:

void foo ( char a[100] );

Wird der Parameter als Zeiger angepasst und wird so:

void foo ( char * a );

Wenn Sie möchten, dass der Array-Typ erhalten bleibt, sollten Sie einen Verweis auf das Array übergeben:

void foo ( char (&a)[100] );

C ++ '03 8.3.5 / 3:

... Der Typ einer Funktion wird nach folgenden Regeln bestimmt. Der Typ jedes Parameters wird aus seiner eigenen Deklarationsspezifizierer-Sequenz und seinem eigenen Deklarator bestimmt. Nach dem Bestimmen des Typs jedes Parameters wird jeder Parameter vom Typ "Array von T" oder "Funktion, die T zurückgibt" so eingestellt, dass er "Zeiger auf T" bzw. "Zeiger auf Funktion, die T zurückgibt" ist ....

So erklären Sie die Syntax:

Suchen Sie in Google nach der Regel "rechts-links". Ich habe hier eine Beschreibung gefunden .

Es würde ungefähr wie folgt auf dieses Beispiel angewendet:

void foo (char (&a)[100]);

Beginnen Sie mit der Kennung 'a'

'a' ist a

Bewegen Sie sich nach rechts - wir finden eine, )also kehren wir die Richtung um und suchen nach dem (. Wenn wir uns nach links bewegen, gehen wir vorbei&

'a' ist eine Referenz

Nach dem &erreichen wir die Öffnung, (also kehren wir wieder um und schauen nach rechts. Wir sehen jetzt[100]

'a' ist eine Referenz auf ein Array von 100

Und wir kehren die Richtung wieder um, bis wir erreichen char:

'a' ist eine Referenz auf ein Array von 100 Zeichen

Richard Corden
quelle
1
Letzteres hat den Nachteil, dass die Arraygröße fest in die Funktionssignatur eingebunden wird. Eine Funktionsvorlage kann dies vermeiden.
sbi
4
Es ist wahrscheinlich erwähnenswert, dass die Verwendung eines std :: -Vektors alle Probleme im Zusammenhang mit der Übergabe von Arrays ordentlich umgeht.
Markh44
5
Dies läuft darauf hinaus, dass einfache Array-Parameter in C / C ++ eine Fiktion sind - sie sind wirklich Zeiger. Array-Parameter sollten so weit wie möglich vermieden werden - sie verwirren wirklich nur die Dinge.
Michael Burr
2
Nur ein Trottel: Der Funktionsparameter zerfällt nicht in einen Zeiger. Es ist auf Zeiger eingestellt . Ein Array Namen als Funktion Argument kann abklingen auf einen Zeiger , wenn der Funktionsparameter ein Zeiger ist.
Juanchopanza
1
Bravo für die Erklärung der Regel, da der Link jetzt ein 404 ist.
gsamaras
16

Ja. In C und C ++ können Sie keine Arrays an Funktionen übergeben. So ist es halt.

Warum machst du überhaupt einfache Arrays? Hast du dir boost/ std::tr1::array/ std::arrayoder angesehen std::vector?

Beachten Sie, dass Sie kann jedoch eine Referenz auf ein Array von beliebiger Länge an eine Funktion Vorlage . Aus dem Kopf:

template< std::size_t N >
void f(char (&arr)[N])
{
  std::cout << sizeof(arr) << '\n';
}
sbi
quelle
6
Sie können jedoch "Verweis auf Array" übergeben.
Richard Corden
@ Richard: Ich habe das gerade hinzugefügt, als du deinen Kommentar geschrieben hast. :)
sbi
6
Die Übergabe unter Bezugnahme auf ein Array ist nicht auf "Funktionsvorlagen" beschränkt. Sie können ein Array unter Bezugnahme auf Nicht-Vorlagenfunktionen übergeben. Der Vorteil der Verwendung einer Funktionsvorlage besteht darin, dass Sie den Array-Index ableiten können, sodass Sie die Funktion für Array-Typen unterschiedlicher Größe aufrufen können.
Richard Corden
4
@CsTamas, ja, die Regeln für die Übergabe von Arrays und Objekten unterscheiden sich in C. Strukturen werden beim Übergeben als Parameter tatsächlich nach Wert kopiert. Arrays werden als Zeiger auf ihr erstes Element behandelt. (Arrays und Zeiger in C sind sehr eng miteinander verbunden. Sie sind nicht dasselbe, aber zum Zwecke der Parameterübergabe sind sie identisch.)
Tyler McHenry
1
Es gibt jetzt std :: array.
Trevor Hickey
0

In der C / C ++ - Terminologie gibt es ein großartiges Wort, das für statische Arrays und Funktionszeiger verwendet wird - Zerfall . Betrachten Sie den folgenden Code:

int intArray[] = {1, 3, 5, 7, 11}; // static array of 5 ints
//...
void f(int a[]) {
  // ...
}
// ...
f(intArray); // only pointer to the first array element is passed
int length = sizeof intArray/sizeof(int); // calculate intArray elements quantity (equals 5)
int ptrToIntSize = sizeof(*intArray); // calculate int * size on your system
Nickolay
quelle
4
Und? Dies zeigt bestenfalls indirekt an, was passiert. Das OP fragte, warum die Sprache so eingerichtet ist. Außerdem ist "statisches Array" ein verwirrender Begriff, wenn das, was Sie wirklich meinen, dynamisch zugewiesen wird. Technisch gesehen ist das angezeigte Array externnicht verknüpft static. Und ich bin mir nicht sicher, was Funktionszeiger hier mit irgendetwas zu tun haben?
underscore_d