Das verwirrende char in C/C++
-
trotzdem ist ein Array kein Zeiger. Wenn der Zeiger zerstört wird, macht das dem Speicher nichts. Wenn das Array zerstört wird, ist der Speicherbereich freiwild.
-
Das ist nicht strittig. Der Arrayzugriff a[b] ist im Standard über Zeigerarithmetik definiert. Das ist tatsächlich nur syntaktischer Zucker.
Es geht einzig um deine Behauptung, ein Array wäre ein Zeiger. Dass das nicht so ist, sieht man übrigens auch in deinem Assembler-Code, oder wo siehst du bei; a[0][4] = 'A'; movb $65, -124(%rbp)
eine Zeigerdereferenzierung? Da steht nur eine Adressberechnung relativ zu rbp, also relativ zu einer lokalen Variablen. Wäre a ein Zeiger, müsste der erst geladen und dereferenziert werden, bevor darauf 4 addiert werden.
-
!rr!rr_. schrieb:
Nexus schrieb:
Arrays sind keine Zeiger.
ja, wer behauptet denn das Gegenteil?
Ich sagte, Arrays seien "syntaktischer Zucker" für Zeiger und Adreßarithmetik. [...]
Und die Bedeutung dieser Aussage ist wahrscheinlich vielen nicht klar. Ich kann damit auch echt nichts anfangen. Höchstens, dass Du glaubst, dass man Arrays auch ohne Arrays mit Zeigern hinbekommt. Das wäre aber falsch. Irgendo muss doch der Speicher herkommen. Wenn ich Speicher für 5 ints reservieren will, schreibe ich
int arr[5]
. Wie soll das bitte mit Zeigern funktionieren? Vielleicht hast Du auch eine andere Definition von "Syntaxzucker". Den Begriff "Syntaxzucker" würde ich zB bei C++0x Lambdas benutzen. Man kann all das, was man mit C++0x Lambdas machen kann, auch ohne hinbekommen. Ein Lambda-Ausdrück ist eine syntaktische Abkürzung für die Erzeugung einer Klasse und gleichzeitig einer Instanz dieser Klasse.
-
Man kann das auch ganz philosophisch sehen: C++ ist doch auch nur Syntaxzucker für Assembler
.
Aber im Ernst, das sind doch alles nur Vorstellungsweisen. Ein Array verhält sich nicht genauso wie ein Zeiger und auch nicht genauso wie eine Variable. Ein Array verhält sich wie ein Array, und wer genau wissen will, wie das ist, soll sich halt die Regeln von krümelkracker einprägen.
-
Habt ihr sonst noch Probleme? Ein char ist ein einzelnes Zeichen vom Typ Byte oder Integer. Ein array wie char string[10] macht damit einen String für mehrere Zeichen. Ein Pointer kann benutzt werden, um indizierte Zugriffe wie sting[i] zu vermeiden. Wenn man sich mit Pointern (noch) nicht sicher auskennt, benutzt man einfach Indizes.
Zu beachten ist nur, dass ein String ein abschliessenden Nullzeichen zwingend braucht. Das machen aber die String-Funktionen von C strcpy, strcat, ... von allein!
Was bitte ist beim Programmieren Zucker, kann es nicht auch Pfeffer oder Paprika sein?
-
berniebutt schrieb:
[blabla]
Was bitte ist beim Programmieren Zucker, kann es nicht auch Pfeffer oder Paprika sein?
-
Bashar schrieb:
berniebutt schrieb:
[blabla]
Was bitte ist beim Programmieren Zucker, kann es nicht auch Pfeffer oder Paprika sein?Wobei beim mir der erste Treffer die deutsche Wikiseite ist und auch diesen Quatsch enthält:
...Ein Beispiel für syntaktischen Zucker ist die Behandlung von Feldern in der Programmiersprache C. C unterscheidet streng genommen nicht zwischen Zeigern auf Objekte und Felder von Objekten...
Das kann man da so eigentlich nicht stehen lassen. Die englische Version macht da schon einen viel besseren Eindruck:
In the C programming language arrays are constructed as blocks of memory, accessed via an offset from the array's starting point in memory. Since some pointer operations are hard to write and understand when expressed in terms of pointer arithmetic, C also provides the A *syntax for what would otherwise be written as (A + i). Similarly A[i][j] is more readable than (A + Li + j), L being A's first dimension.
Mit diesem Text verstehe ich auch, worauf !r!r wahrscheinlich hinaus wollte. Aber mit Arrays hat das weniger zu tun. Passender kann man sagen, dass der [i]Index-Operator* syntaktischer Zucker für Zeigerarithmetik ist. Seit Operatorüberladung in C++ müsste man diesen Satz aber noch etwas einschränken.
-
Bashar und Sugar Baby kennen sich da pefekt aus. Ist kein [blabla] mehr zu ergänzen. daddeldu! :p
-
krümelkacker schrieb:
Und die Bedeutung dieser Aussage ist wahrscheinlich vielen nicht klar. Ich kann damit auch echt nichts anfangen. Höchstens, dass Du glaubst, dass man Arrays auch ohne Arrays mit Zeigern hinbekommt.
Man braucht keine Arrays um Speicher statisch oder dynamisch auf dem Stack zu reservieren. Das Verhalten von Arrays auf dem Stack bekommt man auf einem UNIX auch mit alloca() und Zeigern hin. Der Vorteil von arrays besteht darin, daß man mehr dimensionale Arrays anlegen kann, sich also das manuelle Berechnen der korrekten Speicheradresse erspart, und man sich nicht explizit um die Speicherbereitstellung kümmern muß.
Folgendes Programm zeigt wie man mit einem struct Speicher entsprechender Größe anfordert. Es könnte in grauer Theorie Probleme mit dem Padding geben, aber das soll kein richtiges Programm sein, sondern nur verdeutlichen was beim Array eigentlich passiert. Daher erspare ich mir jetzt nachzusehen, ob bei Typen gleicher Größe in einem struct Padding auftreten könnte. Wichtig ist nur, daß der struct mindestens 5*int an Speicher bereitstellt, und ich das so nie in einem richtigem Programm verwenden würde.
#include <stdlib.h> #include <stdio.h> int main () { struct array { int a0,a1,a2,a3,a4,a5; }; struct array a = {0,1,2,3,4}; int* p = &a.a0; for (size_t i = 0; i != 5; ++i) { printf("%i\n",p[i]); } }
krümelkacker schrieb:
Irgendo muss doch der Speicher herkommen. Wenn ich Speicher für 5 ints reservieren will, schreibe ich
int arr[5]
. Wie soll das bitte mit Zeigern funktionieren?
Nachfolgendes Programm ist UNIX only, es soll aber nur das Prinzip erläutern.
Variante 1int my_function (size_t s) { int* p = alloca(sizeof(int)*s); for (size_t i = 0; i != s; i++) { p[i] = i; } } // hier wird der Speicher auf den p verweist freigegeben
Variante 2
int my_function (size_t s) { int p[s]; for (size_t i = 0; i != s; i++) { p[i] = i; } } // hier wird der Speicher von p freigegeben
Das sieht nun wirklich nicht mehr sehr unterschiedlich aus.
-
dem te ist mit der diskussion sicher nicht geholfen
-
Eines interessiert mich jetzt aber doch noch. Wie soll alloca() funktionieren?
Folgendes:
1. Die Rücksprungadresse von alloca() sowie der Integer wird auf den Stack gelegt und der Stack-Pointer inkrementiert.
2. alloca() legt den Wert des Stack-Pointers oder den Wert des Stack-Pointers minus 8 oder ähnliches irgendwo bei der Rückgabe ab.
3. alloca() kehrt zurück und der Stackpointer wird wieder dekrementiert.
4. Der Rückgabewert wird in die Variable kopiert.
5. Der Stack-Pointer muss inkrementiert werden, damit er hinter den allozierten Stack-Speicher zeigt, aber wie? Das könnte höchstens durch eine Calling-Convention gehen, bei der die Rücksprungadresse in einem Register liegt und alloca() daher selbst den Stack-Speicher inkrementieren kann, so dass dieser nur bis zum endgültigen Wert wieder dekrementiert wird.
6. Wer sagt dem Programm, dass hier mehr Stack-Speicher alloziert wurde und der Stack-Pointer weiter dekrementiert werden muss als gedacht??
-
It's magic! delete[] kennt auch die größe vom array...
-
da magican! schrieb:
It's magic! delete[] kennt auch die größe vom array...
Da kann es aber Konvention sein, dass new[] die Größe des Arrays in die vier Bytes vor dem Speicher schreibt.
-
wxSkip schrieb:
Eines interessiert mich jetzt aber doch noch. Wie soll alloca() funktionieren?
intrinsic.
alloca ist afaik nur ein anderer name für __builtin_alloca und das sagt ja schon alles
-
Aha... Compiler-Magik hatte ich schon erwartet
.
-
Also für mich sind Lambdas ja heiß aufgekochter Syntaxzucker und damit Syntaxkaramel!
Ps: Also ich stelle mir die NAMEN der Arrays immer als Zeiger vor, bei denen der Compiler noch ein gewisses Zugriffsschema mitschleift, eben wie bei ganz normalen typisierten Zeigern auch.
-
#include <iostream> bool isAnArrayAPointer() { return sizeof(int[10]) == sizeof(void*); } bool isTheNameOfAnArrayAPointer() { int temp[10]; return sizeof(temp) == sizeof(void*); } int main() { std::cout << std::boolalpha << isAnArrayAPointer() << std::endl; std::cout << std::boolalpha << isTheNameOfAnArrayAPointer() << std::endl; }
Frage beantwortet?
Grüssli
-
Das weiß ich nicht, kommt auf die Frage an! Welche Frage meinst du?
-
Decimad schrieb:
Das weiß ich nicht, kommt auf die Frage an! Welche Frage meinst du?
Lies die Funktionsnamen.
Grüssli
-
Boah, ich hab jetzt echt keine Lust hier groß auszuholen, warum ich mir das so vorstelle. Mir reicht jetzt hier zu betonen, dass eine Vorstellung nicht der Wirklichkeit entsprechen muss.