Seite 1 von 1

Fragen zu sizeof()

Verfasst: 30.07.2017, 23:05
von starcow
Guten Abend Miteinander! :-)

Ich bin gerade dabei wieder etwas intensiver in die C / C++ Programmierung einzusteigen und mache deswegen ein paar Auffrischungs-Uebungen. :)

Jetzt bin ich auf etwas gestossen, was mir nicht so recht einleuchtet:

Code: Alles auswählen

const char buffer1[] = "Hallo";
cout << sizeof(buffer1) << endl;

const char* buffer2 = "Hallo";
cout << sizeof(buffer2) << endl;
Wieso ergibt das nicht zweimal das selbe Resultat?
Also ich meine, bei buffer2 gibt er allem Anschein nach die Grösse des char-pointers zurück.
Aber ich frage mich: wieso?

buffer1, ohne die Index-Klammern, steht doch einfach für die Adresse des ersten Elementes.
Und buffer2 ist ebenfalls die Speicher-Adresse des erstes Elementes des Char-Arrays.

Also: buffer1 == &buffer1[0]
und: buffer2 == &buffer2[0]

sizeof() kriegt also in beiden Fällen einfach die Adresse des ersten Elementes übermittelt.
Demnach müsste sizeof() doch auch zweimal den selben Wert ausgeben.

Wie kann ich denn den genutzten Speicher ermitteln, wenn ich einen Pointer auf ein Array habe, wie im zweiten Fall?
Streng genommen braucht Methode eins doch mehr Speicher (ab vier Buchstaben), da der String sich nun zweimal im Stack befindet. Einmal als const char-Array "Hallo" und einmal in Form des arrays buffer[].
Folglich müsste doch die Methode

Code: Alles auswählen

const char* buffer = "Hallo";
Eine Spur eleganter sein.

Gruss starcow

Re: Fragen zu sizeof()

Verfasst: 31.07.2017, 00:09
von joggel
Wieso ergibt das nicht zweimal das selbe Resultat?
Also ich meine, bei buffer2 gibt er allem Anschein nach die Grösse des char-pointers zurück.
Aber ich frage mich: wieso?
Ich glaube, bei sizeof(buffer2) wird die Größe des Zeigers vom Typ char, und das ist eben nur 1Byte (ist doch so, oder?), zurück gegeben.
Bei dem ersten sizeof(buffer1), wird da die Größe des Arrays angegeben (mutmaße ich jetzt mal, ohne das ausprobiert zu haben), und die Größe ist zum Programmstart bekannt, oder vlt macht das gleich der Comnpiler beim übersetzen, das er ein array mit fixer größe von "Hallo" (reserviert).

Ich habe es selber nicht getestet, aber wenn ich mir das so anschaue, und das da anscheinend unterschiedliche Ergebnisse raus kommen, würde ich das so erklären.
Ja...ich denke es ist so.
:)

Re: Fragen zu sizeof()

Verfasst: 31.07.2017, 00:27
von Krishty
In C++ sind char * (Zeiger auf Buchstabe) und char [6] (Array aus sechs Buchstaben) eben unterschiedliche Typen. char [] (Array aus unbekannter Menge Buchstaben) gibt es auch noch.

char-Arrays konvertieren in sehr vielen Fällen implizit zu Zeigern, und deshalb kann man sie oft gleich verwenden – z.B. bei Funktionsaufrufen. sizeof ist aber kein solcher Fall, und es bekommt nicht einfach die Adresse des ersten Buchstabens. Es ist auch kein Funktionsaufruf (du kannst es ohne Klammern schreiben, also sizeof buffer2, um den Unterschied hervorzuheben) sondern ein Ausdruck, der vom Compiler während des Übersetzens ausgewertet wird.

Die Verwechslungsgefahr ist durch diese einfache Konvertierung umso größer (und das nötige Wissen, solche Situationen zu erkennen, umfangreicher). Ist einfach eine scheiß Extraregel.

Was die Leistung angeht, ist die Sache noch komplexer:

char x[] = "Hello";
  1. reserviert Platz für sechs Buchstaben
    • entweder reserviert es zusätzlich sechs Buchstaben in der EXE und füllt sie beim Kompilieren mit Hello\0, und kopiert von da in den neu reservierten Platz
    • oder es schreibt die Befehle, Hello\0 in den reservierten Platz zu schreiben, direkt in die CPU-Befehlskette
  2. wird möglicherweise (unter Visual C++ immer) bei jedem Aufruf wiederholt
  3. hat den String garantiert in irgendeiner Form doppelt im Speicher
char * x = "Hello";
  1. reserviert Platz für sechs Buchstaben in der EXE und füllt sie beim Kompilieren
  2. legt eine lokale Variable vom Typ char * (Zeiger auf Buchstabe) an (die aber üblicherweise wegoptimiert, also direkt durch die Adresse des Strings ersetzt wird)
  3. füllt sie mit der Adresse aus 1.
  4. nur 1. & 2. werden pro Aufruf wiederholt (ab einer gewissen Länge ist das schneller, als den String immer neu zu schreiben)
  5. hat den String nur einmal im Speicher
static char const x[] = "Hello";
  • verhält weitgehend wie char * x = "Hello";
Die Unterschiede sind nur wichtig, wenn man auf Größe/Cache-Lokalität optimiert und können Anfängern total egal sein.

Re: Fragen zu sizeof()

Verfasst: 31.07.2017, 16:49
von Helmut
Krishty hat geschrieben:char [] (Array aus unbekannter Menge Buchstaben) gibt es auch noch.
Wobei noch anzumerken ist, dass char[] immer seine Bedeutung wechselt, je nachdem wo man es benutzt.

char var[] = {'a', 'b'};
in einem Namespace oder einer Funktion ist das selbe wie
char var[2] = {'a', 'b'};


void bla(char var[])
in einer Parameterliste ist das selbe wie
void bla(char* var)

und
struct foo { char var[]; };
in einer Struktur ist (auf vielen Compilern) das selbe wie
struct foo { char var[0]; };.

(Macht allerdings nur als letztes Element der Struktur Sinn.)

Re: Fragen zu sizeof()

Verfasst: 31.07.2017, 17:05
von Krishty
Helmut hat geschrieben:char var[] = {'a', 'b'};
in einem Namespace oder einer Funktion ist das selbe wie
char var[2] = {'a', 'b'};
An der Stelle ist char [] aber kein Typ, ebenso wenig wie auto es wäre :)

Re: Fragen zu sizeof()

Verfasst: 01.08.2017, 10:27
von Helmut
Ach ja, was ist es denn dann? :)

Re: Fragen zu sizeof()

Verfasst: 01.08.2017, 11:03
von dot
Helmut hat geschrieben:Ach ja, was ist es denn dann? :)
char ist ein typ, [] ist ein operator.

Re: Fragen zu sizeof()

Verfasst: 01.08.2017, 11:19
von Helmut
[] ist ein operator, aber nicht in diesem Zusammenhang. Operatoren kommen nur im Zusammenhang mit Ausdrücken vor, hier ist var aber ein Name in einer Variablendefinition und kein Ausdruck.
char var+1; wäre beispielsweise ungültig.

Re: Fragen zu sizeof()

Verfasst: 01.08.2017, 11:48
von Krishty
Entschuldige – es ist ein Typ, aber nicht der Typ. Das hätte ich anders schreiben müssen.
C++0x Draft (weil ich nichts Neueres habe) hat geschrieben:The declared type of an array object might be an array of unknown size and therefore be incomplete at one point in a translation unit and complete later on; the array types at those two points (“array of unknown bound of T” and “array of N T”) are different types.

Re: Fragen zu sizeof()

Verfasst: 01.08.2017, 11:51
von dot
Helmut hat geschrieben:[] ist ein operator, aber nicht in diesem Zusammenhang. Operatoren kommen nur im Zusammenhang mit Ausdrücken vor, hier ist var aber ein Name in einer Variablendefinition und kein Ausdruck.
char var+1; wäre beispielsweise ungültig.
N4659: Working Draft, Standard for Programming Language C++. §11/2 hat geschrieben:[...] The declarators specify the names of these entities and (optionally) modify the type of the specifiers with operators such as * (pointer to) and () (function returning). [...]
Es ist in der Tat keine Expression sondern eine simple-declaration und es handelt sich auch nicht um den Subscript Operator. Aber es ist ein Operator. Wenn es deiner Meinung nach kein Operator ist, was ist es dann? Was sind denn * und () und [] und & und && und -> in Deklarationen wenn es keine Operatoren sind? Insbesondere würde mich interessieren, wie genau du erklärst, dass diese Nicht-Operator-Entitäten linksbindent oder rechtbindent sein können und eine Rangfolge haben... ;)

Re: Fragen zu sizeof()

Verfasst: 01.08.2017, 13:03
von Helmut
Gut, hast gewonnen :)
Ich wusste nicht, dass die Token auch im Zusammenhang mit Deklarationen Operatoren genannt werden. Sie haben natürlich auch Assoziativität, eine Reihenfolge und können geklammert werden, aber der semantische Unterschied ist ja doch erheblich, vor allem bei & und &&, sodass ich die Deklarationsoperatoren nicht Operatoren genannt hätte. In dieser Liste werden sie übrigens auch nicht als solche erwähnt.

Aber an meiner ursprünglichen Aussage, dass char[] ein Typ ist, halte ich fest. Ob nun incomplete oder nicht :)

Re: Fragen zu sizeof()

Verfasst: 03.08.2017, 12:53
von starcow
Erstmal vielen Dank für eure Antworten! :-)
Die Sache scheint ja nicht gerade trivial zu sein.
joggel hat geschrieben:Ich glaube, bei sizeof(buffer2) wird die Größe des Zeigers vom Typ char, und das ist eben nur 1Byte (ist doch so, oder?), zurück gegeben.
Soviel ich weiss, ist bei jeder 32-Bit Anwendung die Grösse eines Pointers stets 4Byte. Weil nur mit 4 Byte der entsprechende Adressraum abgebildet werden kann.
Es deckt sich jedenfalls mit der Ausgabe aus meinem Programm.
Krishty hat geschrieben:In C++ sind char * (Zeiger auf Buchstabe) und char [6] (Array aus sechs Buchstaben) eben unterschiedliche Typen. char [] (Array aus unbekannter Menge Buchstaben) gibt es auch noch.
Wie sieht es den bei C aus? Sind da die Typen char*, char[], char[6] vom gleichen Typ?

Ist es also unter C++ gar nicht möglich die Grösse eines Array zu ermitteln, wenn man "nur" einen Zeiger auf das erste Element hat?

Code: Alles auswählen

char* buffer = "Hallo";
Gruss starcow

Re: Fragen zu sizeof()

Verfasst: 03.08.2017, 13:30
von Helmut
starcow hat geschrieben:Wie sieht es den bei C aus? Sind da die Typen char*, char[], char[6] vom gleichen Typ?
Alles was in diesem Thread erwähnt wurde gilt für C und C++.
Es ist nur so, dass in C++ die Probleme, die hier diskutiert wurden, eher seltener auftreten, da man üblicherweise std::string für Strings und std::vector für Arrays nutzt. Aber ich denke es ist trotzdem sinnvoll die genauen Unterschiede zu kennen.

Re: Fragen zu sizeof()

Verfasst: 03.08.2017, 13:32
von joggel
Wenn Du die länge eines char-Array ermitteln willst, dann hilft Dir strlen
Aber es gibt die auch nicht die Größe des kompletten Arrays wieder, sondern eben nur soviel zeichen es enthalten. Also bis das Zeichen '\0' auftaucht...

Wie es bei einem Array von anderen Typen aussieht, weiß ich nicht...

[Nachtrag/Edit]
Ich meinte nicht char-Array, sondern einen String.

Re: Fragen zu sizeof()

Verfasst: 03.08.2017, 13:41
von Krishty
starcow hat geschrieben:
Krishty hat geschrieben:In C++ sind char * (Zeiger auf Buchstabe) und char [6] (Array aus sechs Buchstaben) eben unterschiedliche Typen. char [] (Array aus unbekannter Menge Buchstaben) gibt es auch noch.
Wie sieht es den bei C aus? Sind da die Typen char*, char[], char[6] vom gleichen Typ?

Ist es also unter C++ gar nicht möglich die Grösse eines Array zu ermitteln, wenn man "nur" einen Zeiger auf das erste Element hat?
Unter C ist es genauso; ebenfalls unterschiedliche Typen.

Die Länge eines Arrays kannst du via sizeof array / sizeof array[0] ermitteln. Beachte, dass bei char-Arrays die abschließende Null mitgezählt wird!
joggel hat geschrieben:Ich glaube, bei sizeof(buffer2) wird die Größe des Zeigers vom Typ char, und das ist eben nur 1Byte (ist doch so, oder?), zurück gegeben.
Bild

Wie Starcow schreibt, ist die Zeigergröße auf 32-Bit-Systemen 4 Bytes und auf 64-Bit-Systemen 8 B …

Re: Fragen zu sizeof()

Verfasst: 03.08.2017, 13:49
von joggel
Was möchtest Du mir jetzt damit und dem Bild sagen, Krishty?

Ich meine, das hat doch Starcow schon gesagt. Und wie viele Byte ein Zeiger auf einem 64Bit-System hat...ist ja klar.
Oder soll ich Dir das jetzt hier wie so ein dämliches Kleinkind beantworten???

Re: Fragen zu sizeof()

Verfasst: 03.08.2017, 13:54
von xq
Naja, dein Tipp mit strlen war eben auch nicht grade hilfreich, den das bestimmte die Länge des Strings und nicht die größe des Arrays bzw. des verfügbaren Speichers:

Code: Alles auswählen

char buffer[64];
strcpy(buffer, "kek");
char * pointer = buffer;

sizeof(buffer) // = 64
sizeof(pointer) // = 4
strlen(buffer) // = 3
strlen(pointer) // = 3


Re: Fragen zu sizeof()

Verfasst: 03.08.2017, 13:56
von joggel
MasterQ32 hat geschrieben:Naja, dein Tipp mit strlen war eben auch nicht grade hilfreich, den das bestimmte die Länge des Strings und nicht die größe des Arrays bzw. des verfügbaren Speichers:
Habe ich doch geschrieben....

Re: Fragen zu sizeof()

Verfasst: 03.08.2017, 13:59
von joggel
Nur was möchte starcow?
Die länge des Strings?

Code: Alles auswählen

char* buffer = "Hallo";
oder die länge des Arrays?

Wie man die Länge des Strings ermittelt, habe ich einen Tipp gegeben...
Okay, ich habe es fälschlicher Weise nicht "String" genannt, sonder "Array".... SORRY!!! :shock:

Re: Fragen zu sizeof()

Verfasst: 03.08.2017, 14:01
von Krishty
Du warst dir nicht sicher („ich glaube“) und Starcow auch nicht, und ich habe bestätigt, dass Starcow recht hat und du haarsträubend daneben lagst ;)

Re: Fragen zu sizeof()

Verfasst: 03.08.2017, 14:01
von joggel
Krishty hat geschrieben:Du warst dir nicht sicher („ich glaube“) und Starcow auch nicht, und ich habe bestätigt, dass Starcow recht hat und du haarsträubend daneben lagst ;)
Achso...

Re: Fragen zu sizeof()

Verfasst: 03.08.2017, 14:20
von dot
Krishty hat geschrieben:Die Länge eines Arrays kannst du via sizeof array / sizeof array[0] ermitteln. Beachte, dass bei char-Arrays die abschließende Null mitgezählt wird!
Oder in C++ mit std::extent<decltype(array)>::value, bzw. ab C++ 17 mit std::extent_v<decltype(array)>

Re: Fragen zu sizeof()

Verfasst: 10.08.2017, 12:48
von starcow
Entschuldigt, dass ich nochmals nachfragen muss:
Krishty hat geschrieben: Die Länge eines Arrays kannst du via sizeof array / sizeof array[0] ermitteln. Beachte, dass bei char-Arrays die abschließende Null mitgezählt wird!
Also irgendwie verstehe ich nicht ganz, worin der Unterschied liegt, zu meinem ersten Versuch (welcher ja so nicht zu funktionieren scheint).

Was ja bekanntlich klappt ist folgendes:

Code: Alles auswählen

char buffer[] = "Hallo";
cout << sizeof buffer;
Was aber nicht zu funktionieren scheint ist:

Code: Alles auswählen

char* buffer = "Hallo";
cout sizeof buffer;
Ich hänge aber an der ersten Variante - denn:

Code: Alles auswählen

char buffer[] = "Hallo";
char* buffer2 = buffer;
cout sizeof buffer2;
Wenn das funktionieren würde, könnte man die Grösse des Buffers auch in einer anderen Funktion bestimmen und nutzen

Code: Alles auswählen

int Groesse(const char* const buffer)
{
return sizeof buffer;
}

char buffer[] = "Hallo";
cout << Groesse(buffer);
Aber vielleicht, ist eine solche Methode schlicht gar nicht möglich?
Alternativ könnte man die Grösse eines Strings ja sehr einfach ermitteln, indem man das Array durchgeht, bis man auf die 0-Terminierung trifft.
Nur kann ich mich wirklich in _jedem_ Fall darauf verlassen, das ein C-String (char Array) ausnahmslos mit einer 0 beendet wird?
Oder begebe ich mich da in einen Bereich der riskanten Programmierung? :)

Gruss starcow

Re: Fragen zu sizeof()

Verfasst: 10.08.2017, 13:32
von xq
Hey!

Annahme: Wir befinden uns auf einem 32-Bit-System, also sizeof(void*) = 4

Code: Alles auswählen

char buffer_a[] = "Hallo";
char* buffer_b = buffer_a;
cout << sizeof(buffer_a); // (a) → 6
cout << sizeof(buffer_b); // (b) → 4
Der Unterschied hier zwischen (a) und (b) ist der folgende:
(a) besitzt den Typen char[6], wohingegen (b) den Typen char* besitzt. Da sizeof zur Compilezeit ausgeführt wird und nur die Größe des angegeben Ausdrucks oder Typen bestimmt (und den Inhalt komplett ignoriert, es wird kein Code emittiert), hast du hier den zu erwartenden Unterschied.

Bei deinem Beispiel

Code: Alles auswählen

int Groesse(const char* const buffer)
{
return sizeof buffer;
}

char buffer[] = "Hallo";
cout << Groesse(buffer);
liefert Groesse immer 4 zurück, da der Parameter buffer ignoriert wird. Der Compiler gibt also folgende Funktion aus:

Code: Alles auswählen

int Groesse(const char* const buffer)
{
return 4;
}
Damit ist die Antwort auf deine Frage "Aber vielleicht, ist eine solche Methode schlicht gar nicht möglich?" ein Nein, diese Methode ist nicht möglich
Nur kann ich mich wirklich in _jedem_ Fall darauf verlassen, das ein C-String (char Array) ausnahmslos mit einer 0 beendet wird?
Nein, du kannst dich nicht darauf verlassen, dass deine Strings immer mit einer 0 beendet werden. Wenn dies nicht so ist, wird ein Programm so lange den String absuchen, bis es eine 0 im Speicher findet. Aber auch Funktionen wie strlen prüfen das nicht, das liegt in der Verantwortung des Aufrufers.

Falls du dich aber absichern willst, kannst du strlen_s verwenden, welches eine maximale Länge des Speichers angibt.

Grüße
Felix

Re: Fragen zu sizeof()

Verfasst: 10.08.2017, 13:43
von Krishty
MasterQ32 hat geschrieben:
Nur kann ich mich wirklich in _jedem_ Fall darauf verlassen, das ein C-String (char Array) ausnahmslos mit einer 0 beendet wird?
Nein, du kannst dich nicht darauf verlassen, dass deine Strings immer mit einer 0 beendet werden. Wenn dies nicht so ist, wird ein Programm so lange den String absuchen, bis es eine 0 im Speicher findet. Aber auch Funktionen wie strlen prüfen das nicht, das liegt in der Verantwortung des Aufrufers.
Ich schätze, er meinte die Frage anders: Wenn er sowas wie char * s = "foobar" hinschreibt – also eine String-Konstante – ist die immer mit Null abgeschlossen? Und ja, das garantiert der Compiler dir. Sobald du irgendwie am String rumfummelst, ist die Garantie aber futsch.

(Im Gegensatz zu std::string, bei dem alle Operationen weiterhin garantieren, dass size() die tatsächliche Länge zurückgibt.)

Re: Fragen zu sizeof()

Verfasst: 10.08.2017, 15:27
von starcow
Ok, Danke für die Erläuterungen!
Ich leite jetzt daran folgende Schlussfolgerung ab:

Wenn ich ein Array (per Zeiger auf das erste Element) an eine Funktion weitergebe, habe ich _keine_ Möglichkeit, die Grösse dieses Arrays in der Funktion selbst zu ermitteln.
Ich müsste demnach die Grösse des Array zuvor per sizeof ermitteln und den ermittelten Wert per Parameter an die Funktion mitgeben.
Oder - bei einem String - mich darauf verlassen, dass das Array mit einem 0-Wert als abgeschlossen markiert wird.

Ist das so richtig?

Gruss starcow

Re: Fragen zu sizeof()

Verfasst: 10.08.2017, 15:32
von Krishty
Ja. Sobald du das Array an eine Funktion übergibst, erhält die nur einen Zeiger. Aus ihrer Sicht ist es kein Array mehr und du kannst auch nicht mehr die Größe feststellen.

Es gibt noch eine Lösung über Templates, aber dann wird die Syntax richtig hässlich ;)

Re: Fragen zu sizeof()

Verfasst: 10.08.2017, 15:35
von dot
starcow hat geschrieben:Ist das so richtig?
ja