C++ Weitergabe von Array an andere Funktionen

Bender86

Lieutenant
Registriert
Jan. 2007
Beiträge
722
Moin zusammen,

da ich im Studium (technische Informatik) grade C++ benutzen soll/muss spiele ich grade ein bisschen damit herum. Ich möchte im Moment den Inhalt eines Arrays auf der Konsole ausgeben. Dazu habe ich mir 3 Methoden geschrieben, eine main(), eine print(), die das Array ausgeben soll und eine arrayLength() zur Bestimmung der Länge des Arrays. Das ganze sieht so aus:

Code:
#include <iostream>

using namespace std;


template <class T>
int arrayLength(T a[]) {
	cout << "Methode arrayLength(): sizeof(a): " << sizeof(a) << endl;
	cout << "Methode arrayLength(): sizeof(a) / sizeof(T): " << sizeof(a) / sizeof(T) << endl;
	return sizeof(a) / sizeof(T);
}


int print(int a[]) {
	cout << "Methode print(): sizeof(a): " << sizeof(a) << endl;
	for(int i = 0; i < arrayLength(a)-1; i++) {
		cout << a[i] << endl;
	}
	return 1;
}

int main() {
	int a[] = {0,1,2,3,1,2,7,4,5,6,7,5,4,3};
	cout << "Methode main(): sizeof(a): " << sizeof(a) << endl;
	print(a);
	return 1;
}

Da ich wie gesagt ein bisschen rumexperimentiere hab ich die Methode für die Länge für generische Datentypen gemacht. (zumindest kenn ich das so von Java) Wenn ich das ganze Ausführe erhalte ich auf der Konsole:

Code:
Methode main(): sizeof(a): 56
Methode print(): sizeof(a): 4
Methode arrayLength(): sizeof(a): 4
Methode arrayLength(): sizeof(a) / sizeof(T): 1

sizeof(a) gibt mir in Main also die Größe in Bytes des gesamten Arrays zurück. In den anderen Methoden, an die "a" übergeben wird, aber scheinbar nur die Größe von einem Integer, also 4 Bytes. Zumindest erkläre ich mir das so.

Stimmt meine Vermutung? Was muss ich ändern damit ich bei arrayLength() die richtige Größe ermitteln kann?

Viele Grüße,
Bender

P.s: Nein, das ist keine Hausaufgabe, ich versuche auch außerhalb der Aufgaben ein bisschen zu experimentieren. ;)
 
Mit int[]a=... erzeugst du einen Speicherbereich und speicherst dir in ´a´einen Pointer auf diesen Bereich.
sizeof(a) gibt dir eine 4, weil du die Größe des Pointers unter einem 32Bit Betriebssystem misst. Ein Pointer benötigt hier 4 Byte (=32Bit). Die Arraylänge kannst du nicht überprüfen. Daher empfiehlt sich ja meist auch die Benutzung von stl-Container-Klassen wie beispielsweise dem vector.
Die Größe eines alloziierten Speichers weiß nur der, der ihn auch alloziiert hat. Was du nach der alloziierung behälst ist ja nur der Ort (Pointer) auf diesen Bereich. Der Pointer an sich hat keine weiteren Informationen abgesehen vom Typ.
 
Zuletzt bearbeitet:
sizeof(a) gibt 4 zurück weil das die Größe eines Zeigers ist (zumindest auf deinem 32bit system), und nichts anderes ist ein C array ja, ein Zeiger auf das erste Element. Daran erkennst du auch schon, dass du nur an Hand des Zeigers keinerlei Aussage über die Länge des Arrays machen kannst.
Du musst also entweder die Länge des Arrays als zusätzlichen Parameter übergeben (das ist der übliche Weg) oder markierst das letzte Element damit du merkst wann du das Ende erreicht hast (das ist das Prinzip von strings in C und schrecklich unschön).

Noch besser ist es natürlich in C++ anstelle von Arrays vector zu verwenden.

Dass das sizeof() in main den von dir erwarteten Wert zurückliefert liegt übrigens daran, dass der compiler hier mehr Informationen über den Typ von a hat als in den anderen Funktionen.
 
Danke euch beiden für die Antworten. :) Das ist irgendwie doof.. bei Java war das so schön einfach mit Arrays.

Green Mamba schrieb:
Daher empfiehlt sich ja meist auch die Benutzung von stl-Container-Klassen wie beispielsweise dem vector.

Was versteht man unter einer stl-Container-Klasse?

/edit

Bzgl. sizeof(a), das hatte ich an einer anderen Stelle gelesen, bei der mittels sizeof(a) / sizeof(int) die Länge bestimmt wurde. Daher bin ich davon ausgegangen, dass C++ das irgendwie als Array erkennt und sizeof(a) dann die gesamte Größe vom Array ist.
 
Zuletzt bearbeitet:
STL ist die Standard Template Library, sie enthält die Standardbibliotheken von C++. Und Container sind (wie der Name schon andeutet) Klassen die eine Sammlung von Objekten enthalten.

Eine Übersicht findest du zum Beispiel auf http://www.cplusplus.com/reference/stl/.

Wenn du ernsthaft mit C++ arbeiten willst/musst sollest du dir die STL (und möglichst auch Boost) genau anschauen, die beiden erleichtert einem das Leben ungemein. Damit kannst du dann Code schreiben der so schön einfach ist wie in Java :p

PHP:
#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
#include <boost/lambda/lambda.hpp>

using namespace std;
using namespace boost::lambda;

int main()
{
	// Vector a mit 10 Zufallszahlen füllen
	vector<int> a;
	generate_n(back_inserter(a), 10, rand);

	// Alle Elemente von a ausgeben
	for_each(a.begin(), a.end(), cout << _1 << ' ');
	cout << endl;
}
 
Raechaer schrieb:
und nichts anderes ist ein C array ja, ein Zeiger auf das erste Element.
Nein, das ist fundamental falsch. Ein Array ist ein zusammenhängender Speicherbereich der Größe sizeof(T)*n. Es konvertiert allerdings in den meisten Fällen in einen Zeiger auf sein erstes Element. Eine Ausnahme ist sizeof - da passiert das nicht. Es gibt immer die tatsächliche Größe des Arrays zurück.

Was hier allerdings auftritt, ist im C++ Standard unter 8.3.5/3, 4. Satz beschrieben (bezieht sich auf Funktionsparameter):
After determining the type of each parameter, any parameter of type “array of T” [...] is adjusted to be “pointer to T” [...]
Deshalb bestimmt sizeof in der Funktion nur noch die Größe eines T*. Die ursprüngliche Definition des Arrays inklusive seiner Größe geht durch diese Umwandlung verloren.

Nachtrag:
Eine Funktionsdeklaration
ist also äquivalent zu
und zu
Das schließt auch eine Überladung aus.

Folgendes Programm ist völlig standardkonform und hat auf einem 32Bit-System in der Regel 4 zur Ausgabe:
Code:
#include <iostream>

using namespace std;

void f( int a[5])
{
  cout << sizeof(a) << endl;
}

int main()
{
  int a[20];
  f( a);
  return 0;
}
 
Zuletzt bearbeitet:
7H3 N4C3R schrieb:
Es konvertiert allerdings in den meisten Fällen in einen Zeiger auf sein erstes Element. Eine Ausnahme ist sizeof - da passiert das nicht. Es gibt immer die tatsächliche Größe des Arrays zurück.
Was daran liegt, dass sizeof keine normale Funktion, sondern ein Operator ist.
 
Äh nein. Dass sizeof ein Operator ist hat damit nichts zu tun, sondern 5.3.3/4:
4 The lvalue-to-rvalue (4.1), array-to-pointer (4.2), and function-to-pointer (4.3) standard conversions are not applied to the operand of sizeof.
 
Du hast mich gerade wiederholt.

standard conversions are not applied to the operand of sizeof
Sizeof ist ein Operator und dort ist definiert, wie er sich verhält. Es ist jedenfalls so, dass du das Verhalten von sizeof mit normalen Sprachmitteln nicht nachbilden kannst(zumindest in C).
 
Nein, habe ich nicht:
Was daran liegt, dass sizeof keine normale Funktion, sondern ein Operator ist.
Du hast die Unterdrückung der array-to-pointer-conversion (zumindest so wie du es geschrieben hast) damit begründet, dass sizeof ein Operator ist. Das ist falsch. Es liegt einzig und allein an den Regeln, die auf sizeof zutreffen. Dass sizeof ein Operator ist, spielt dabei keine Rolle bzw. höchstens in sofern, dass builtin Operatoren bestimmte Regeln aufstellen können (4/2 Punkt 1 und 4/5). Bei anderen Operatoren gibt es andere Regeln. Für den builtin []-Operator muss z.B. eine array-to-pointer-conversion durchgeführt werden damit er mit Arrays arbeiten kann, da er selbst nur Pointer verarbeitet.
 
Zuletzt bearbeitet:
Man kann sich auch an Details hochziehen, is sag dazu lieber nichts mehr da du eh jeden Buchstaben auf die Goldwaage legen wirst.
 
Hey, das soll kein persönlicher Angriff gegen dich sein. :schluck: Also bitte nicht als solchen verstehen.

Die Sprache lebt nun mal von unheimlich vielen, subtilen Details. Und auch wenn wir beide wissen was gemeint ist, kann ein Dritter es falsch verstehen. Nicht mehr und nicht weniger. :)
 
Wollte an dieser Stelle mal meine Anerkennung für 7H3 N4C3R aussprechen. Ich bin immer wieder angetan von deiner Eigenart, selbst solchen durch die Praxis eingeschliffenen (als selbstverständlich erachteten) Tatsachen durch fundierte Recherche klar zu stellen und zu bestätigen. :)
So kann man selbst als erfahrender Entwickler immer wieder mal was dazu lernen. :daumen:
 
Danke für die Blumen. :)

Green Mamba schrieb:
Ich bin immer wieder angetan von deiner Eigenart, selbst solchen durch die Praxis eingeschliffenen (als selbstverständlich erachteten) Tatsachen durch fundierte Recherche klar zu stellen und zu bestätigen. :)
So kann man selbst als erfahrender Entwickler immer wieder mal was dazu lernen. :daumen:

Genau das ist auch mein Ziel. :) Also nicht nur erklären, dass etwas so ist oder nicht ist sondern auch erklären warum es so ist oder eben nicht ist - und wo man es selbst nochmal nachlesen kann. C++ ist leider eine höllisch detaillierte Sprache mit zig Ausnahmen, da kann man es sich nicht erlauben unpräzise zu sein (zumindest für meine Ansprüche :)). Ich will hier wirklich niemanden persönlich angreifen, auch wenn mein Schreibstil vielleicht so aussieht.
 
Bender86 schrieb:
sizeof(a) gibt mir in Main also die Größe in Bytes des gesamten Arrays zurück. In den anderen Methoden, an die "a" übergeben wird, aber scheinbar nur die Größe von einem Integer, also 4 Bytes. Zumindest erkläre ich mir das so.

Stimmt meine Vermutung? Was muss ich ändern damit ich bei arrayLength() die richtige Größe ermitteln kann?
Wie 7H3 N4C3R schon sagte, Arrays sind zwar keine Zeiger, können aber implizit in Zeiger auf das erste Element umgewandelt werden. Genau das wird bei entsprechenden Funktionsparametern auch gemacht. Du hast letztendlich zwei Möglichkeiten, die Array Länge an die Funktion zu übergeben. Erstens, was bereits erwähnt wurde, du übergibst die Länge als weiteren Parameter. Oder zweitens, du nutzt Referenzen:
Code:
template <class T, size_t N>
void print(T(& a)[N])
{
	cout << "length of a = " << N << endl;
}
 
Hier etwas übersichtlicher:

Code:
#include <iostream>

using namespace std;

template<class T>
void ripple(T array[], int elements); 

int main()
{
    double array[]={10,9,7,5,4,6,2,1,4,65,45,234,23,12,32,32,0,-0.4,-0.000023};
    ripple(array, sizeof(array)/sizeof(array[0]));
    for(int i=0; i<sizeof(array)/sizeof(array[0]); i++)
    {
            cout<<array[i]<<endl;
            }

	return 0;	
}

template<class T>
void ripple(T array[], int elements)
{
     for(int i=0; i<elements-1 ; i++)
     {
             for(int j=i+1; j<elements ; j++)
             {
                     if(array[i]> array[j])
                     {
                                  T temp= array[i];
                                  array[i]= array[j];
                                  array[j]= temp;
                                  }
                     }
             }
     }

Und wenn mit int eine varible deklariert wird, werden dafür 4byte reserviert.
Von Zeigern/Pointer hängt das nicht ab...

char benötigt 1 Byte
short 2Byte
int 4
long 4
float 4
double 8
long double 10

:)
 
GustlPC schrieb:
Und wenn mit int eine varible deklariert wird, werden dafür 4byte reserviert.
Von Zeigern/Pointer hängt das nicht ab...

char benötigt 1 Byte
short 2Byte
int 4
long 4
float 4
double 8
long double 10

:)

Es ist richtig, dass char immer ein Byte benötigt. Die Größe der anderen von dir aufgezählten Typen ist vielleicht auf deiner Plattform mit deinem Compiler in der derzeitigen Version und den Optionen mit denen du compilierst vielleicht so. Festgeschrieben ist das nirgendwo und ich kann dir aus dem Stehgreif mindestens 5 Plattformen nennen, auf denen das nicht so zutrifft.

Was zutrifft ist:
sizeof(char) < sizeof(short) <= sizeof(int) <= sizeof(long) // respekive ihrer unsigned Entsprechungen
sizeof(float) <= sizeof(double) <= sizeof(long double)

Mit Datentypen bestimmte Bytegrößen fest zu verbinden (ausgeschlossen char) macht Anwendungen äußerst unportabel - selbst in Bezug auf die selbe Plattform.
 
Zuletzt bearbeitet:
Der Standard legt aber nun mal nicht fest, dass int 32 Bits hat. Unter DOS (Ur-x86) war int zB 16 Bit breit. Das dürfte für den einen oder anderen Mikrocontroller immer noch so sein. 7H3 N4C3R hat dazu eigentlich schon alles relevante gesagt. Wenn du Typen mit bestimmten Bitgrössen brauchst, dann verwende entsprechende typedefs.
 
Zurück
Oben