Zeiger und Vektoren
-
@C-Sepp Dann schreib statt Zeile 9
*arr3++ = min(*arr1++, *arr2++);
-
Ich bin noch ein weiteres Codesbeispiel durchgegangen. Bei diesen wird ein string s mittels der Funktion strtok() in
Teilzeichenfolgen zerlegt, welche durch Zeichen aus dem String delim begrenzt werden. Die Funktion lautet:
char* strtok(char* s, const char* delim)Zum Lernen habe ich mir diese Funktion so in etwa mal nachprogrammiert, wobei wieder ein paar
Fragen entstanden sind, welche wahrscheinlich trivial sind:char** test(char* s1, const char* s2) { s1 = "ein anderer Test"; //Fehlerconst char [17]" kann nicht in "char *" konvertiert werden s2 = "Test1"; s2 = "Test2"; return &delim; } int main() { char s1[] = "Das ist ein Test"; char s2[] = "Das ist ein anderer Test"; test(s1,"Das ist ein anderer Test"); }
Warum kann ich den Read-Only-Zeiger s2 in der Funktion eigentlich beliebige Werte zuweisen? (Wäre dies
ein Zeiger auf Integer wäre dies nicht möglich.)
Warum kann ich s1 hingegen keinen Wert zuweisen? Nochmals vielen Dank!
-
Die Fehlermeldung kommt, weil ein String-Literal in C++ (also sowas in "doppelten Quotes") ein konstanter String ist. Diesen kannst du nicht verändern. (das ist auch ein Unterschied von C++ zu C, wo es auch ohne const ginge)
Die Zuweisung an s2 funktioniert, weil bei s2 eben
const
dransteht. Du musst unterscheiden: ist der Zeiger konstant oder ist das, auf das der Zeiger zeigt, konstant? Mitconst char *
hast du einen (veränderbaren) Zeiger auf einen konstanten char. Verwechsle das nicht mitchar * const
(const
hinter dem*
) - dann wäre der Zeiger konstant, aber nicht das, auf das der Zeiger zeigt. Und mitconst char * const
wäre sowohl der Zeiger als auch das, auf das der Zeiger zeigt, konstant. Du kannst auchchar const *
bzw.char const * const
schreiben, also dasconst
undchar
vertauschen, was in letzter Zeit populärer geworden ist (nennt sich east const / west const).Du kannst hier "C Gibberish" nach Englisch übersetzen: https://cdecl.org/?q=const+char+*c
Abgesehen davon folgendes:
delim
ist nicht bekannt (Zeile 6)- wozu übergibst du überhaupt s1 und s2 als Parameter, wenn du mit
s1 = ...
unds2 = ...
deren Werte als allererstes versuchst zu überschreiben?
-
might help: http://unixwiz.net/techtips/reading-cdecl.html
-
Super...wieder etwas dazugelernt. Gut zu wissen auch, dass der Schreibschutz eines konstanten Zeigers im Gegensatz zu strings nicht unterlaufen werden kann. Sprich ich kann einen konstanten Zeiger keinen nicht konstanten Zeiger zuweisen:
unsigned int strlen(const char *s) { ... char* temp = s; // Fehler } const string s1 ="Ein Stringstest"; string s2 = s1;
Dass der Aufruf der oben definierten Funktion test auch mit test(s1, s2) in der Main funktioniert, hängt dann wahrscheinlich konkret damit zusammen, dass s2 nach Zuweisung des Strings "Das ist ein anderer..." ein konstanter String ist?
-
Zu Testzwecken habe ich die Funktion test auch nochmal verändert:
const char** test(const char* const s1, const char* s2) { s1 = "ein anderer Test"; //Fehler s2 = "Test1"; s1 = s2; //Fehler return &s1; //Fehler }
Beide Zeiger sind jetzt konstant und zeigen auf ein Objekt, welches selbst konstant ist. Wie erwartet erscheinen in den Zeilen 3 und 5 jetzt Fehler. Warum Zeile 7 auch nicht passt, ist mir nicht klar. Ich hole mit & die Adresse des doppelt konstanten Zeigers s1. Also lese/hole nur die Adresse. Warum stimmt der Rückgabetyp nicht mit den Funktionstyp überein (Fehler)? Dank!!!
-
@C-Sepp sagte in Zeiger und Vektoren:
Warum stimmt der Rückgabetyp nicht mit den Funktionstyp überein (Fehler)? Dank!!!
Der genaue Wortlaut der Fehlermeldung wäre doch angebracht.
Per Copy&Paste.Andererseits bringt das auch nichts, da s1 eine lokale Variable ist, die beim Beenden der Funktion (das macht
return
) nicht mehr existiert.
Die Adresse davon ist nicht sinnvoll.
-
Die Fehlermeldung lautet:
"const char *const *" kann nicht in "const char **" konvertiert werden
-
Dann ist
const char *const *
was du als Rückgabetyp angeben solltest.Ist aber nicht sinnvoll.
-
@C-Sepp sagte in Zeiger und Vektoren:
Super...wieder etwas dazugelernt. Gut zu wissen auch, dass der Schreibschutz eines konstanten Zeigers im Gegensatz zu strings nicht unterlaufen werden kann. Sprich ich kann einen konstanten Zeiger keinen nicht konstanten Zeiger zuweisen:
???
const string s1 ="Ein Stringstest"; string s2 = s1;
Das hier in Zeile 2 erzeugt eine KOPIE von s1! s1 wird nicht geändert und somit auch kein const-Schutz unterlaufen! Die Kopie in s2 kannst du natürlich ändern. Das änder aber nicht s1.
In diesem Code:
char* temp = s; // Fehler
Hier machst du keine Kopie des Strings, sondern versuchst, den Pointer zu kopieren, aber nicht den String selbst. Wenn das erlaubt wäre, würdest du durch Ändern von
temp[0]
auchs[0]
ändern.Auch mit char-Pointern darfst du natürlich Kopien anlegen. Dies muss aber mit
strcpy
oder verwandten Funktionen geschehen. Und dann darfst du auch const nach nicht-const kopieren.
-
Okay!
Wenn bei char* temp = s nur der Zeiger kopiert wird, warum erscheint dann in dem Fall (Zeiger auf konstantes Objekt) eine Fehlermeldung, welche das const ankreidet??
Das heißt wenn Pointer und Wert kopiert werden würden, könnte ich durch Ändern von temp[0] auch s[0] ändern, obwohl nur eine Kopie angelegt wird...wie geht denn das. Das entspricht doch eigentlich den Prinzip einer Referenz??
-
Nein, es wird keine Kopie des Strings angelegt, sondern nur der Zeiger (die Adresse) kopiert. Und weil ein String-Literal (
"..."
) nicht verändert werden kann, mußt du einenconst char *
verwenden (also dessen Inhalt dann eben nicht per z.B. Indexoperator verändert werden kann).
-
@C-Sepp sagte in Zeiger und Vektoren:
Das heißt wenn Pointer und Wert kopiert werden würden, könnte ich durch Ändern von temp[0] auch s[0] ändern, obwohl nur eine Kopie angelegt wird...wie geht denn das.
Nein, wenn Du auch den Wert kopierst, ist
temp[0] != s[0]
kopierst Du nur den Zeiger, ist
temp[0] == s[0]
Der Wert ist ja dann nur einmal vorhanden, es wird aber von zwei verschiedenen Zeigervariablen drauf gezeigt.
-
Hey Leute,
bin auf eine Funktion gestoßen, zu der sich eine Frage ergeben hat. Sie lautet:
double* Summe(double* a, double* b, int length) { double* sum = new double[length]; for (int i=0; i<length; i++) { sum[i] = a[i] + b[i]; } return sum; }
Addiert also Schritt für Schritt die Werte zweier an a und b übergebener Array's. Warum kann ich mit a[i] und b[i] die Werte zur Addition ansprechen? Eigentlich wurde bei Zeigern doch immer der *-Operator verwendet. Vielen Dank!
-
Der Indexoperator
a[n]
ist äquivalent zu*(a + n)
.Alternativ kann man auch die Funktion so deklarieren:
double* Summe(double a[], double b[], int length)
Wie du siehst, kein Unterschied zwischen Array- und Zeigerzugriff.
-
Aber noch was: diese Funktion hat ein schlimmes Design, weil sie sich innerhalb einer Berechnung Speicher holt und diesen als non-owning Pointer zurückgibt. Das ist eine schlechte Idee, weil man dieser Funktion das nicht ansieht. Dinge wie Verschachteln von mehreren Aufrufen der Funktion Summe führen sofort zu Speicherlecks.
Also dieses hier:
auto gesamt = Summe(a, Summe(b, c, 10), 10);
addiert zwara+b+c
korrekt, aber gibt Speicher nicht korrekt frei.In C++ würde ich eher 2 Iteratorpaare als Parameter erwarten, sowas in der Art von:
std::transform( a.begin(), a.end(), // 1. Range b.begin(), // 2. Range, Länge wie 1 outputIterator, std::plus<double>());
-
@Th69 sagte in Zeiger und Vektoren:
Der Indexoperator a[n] ist äquivalent zu *(a + n).
Und aus a+n = n+a folgt
*(a+n) = *(n+a) = n[a]
(= ist Gleichheit, keine Zuweisung)
-
Vielen Dank für eure schnellen Antworten. Bei Verwendung der folgenden Klasse ist jetzt noch eine weitere kleinere
Frage entstanden:class IntArr { private: int* ptrArr; int len; public: IntArr(int len); ~IntArr(); int length() const { return len;} void compress(); }; IntArr::IntArr(int len) { this->len = len; ptrArr = new int[len]; for (int i = 0; i < len; i++) { ptrArr[i] = 0; } } IntArr::~IntArr() { delete [] ptrArr; } void IntArr::compress() { int count=0; int index=0; for (int i = 0; i < len; i++) { if (ptrArr[i] != 0) count++; } int* ptrArr1 = new int[count]; for (int i = 0; i < len; i++) { if (ptrArr[i] != 0) ptrArr1[index] = ptrArr[i]; index += 1; } delete[] ptrArr; ptrArr = ptrArr1; len = count; }
Aufruf:
int main() { IntArr arr(100); srand((unsigned int)time(NULL)); for (int i = 0; i < arr.legnth(); i++) { arr[i] = rand(); } }
Warum kann man den einzelnen Objekt arr[i] einfach so einen Wert zuweisen? Müsste dazu nicht ein geeigneter Konstruktor vorhanden sein, um zunächst ein passendes Objekt vom Typ der Klasse anzulegen, welches dem Objekt dann zugewiesen werden kann? Wenn das so klappt, wo wird der Wert von rand() dann reingeschrieben? Vielen Dank!!
-
@C-Sepp sagte in Zeiger und Vektoren:
Warum kann man den einzelnen Objekt arr[i] einfach so einen Wert zuweisen?
Das geht gar nicht - und zwar, weil dein Code nicht vollständig ist. Dazu müsste nämlich irgendwo Code für den
operator[]
vorhanden sein. In diesem müsste der entsprechende Code implementiert sein.Müsste dazu nicht ein geeigneter Konstruktor vorhanden sein, um zunächst ein passendes Objekt vom Typ der Klasse anzulegen, welches dem Objekt dann zugewiesen werden kann?
Der Konstruktor existiert doch - in deinem IntArr::IntArr:
ptrArr = new int[len];
Damit werden alle len Integer default-initialisiert (tut nix). Wenn du geschwungene Klammern hinzufügstptrArr = new int[len]{};
, würdest du value-initialisieren (setzt auf 0) und könntest die folgende Schleife, die alle auf 0 Werte setzt, einsparen.PS: Verwende
std::vector<int>
stattIntArr
. Verwendenullptr
stattNULL
. Verwende https://en.cppreference.com/w/cpp/numeric/random stattrand
.
-
Das geht auch nicht ohne weiteres - dazu müßtest du schon den Index-Operator
[]
überladen.
Bei deinem Code kommt auch eine entsprechende Fehlermeldung: Ideone-Code (den Schreibfehler beilength
habe ich mal korrigiert):error: no match for ‘operator[]’ (operand types are ‘IntArr’ and ‘int’)
arr[i] = rand();