Beispiel mit Zeigern - einige Fragen

Schnelle objektorientierte, kompilierende Programmiersprache.
forumnewbie
Beiträge: 80
Registriert: Di Jan 15, 2013 9:02 pm

Beispiel mit Zeigern - einige Fragen

Beitrag von forumnewbie » Fr Mär 07, 2014 1:01 pm

Hallo!

folgendes Beispiel vertauscht zwei String miteinander:

Code: Alles auswählen

#include <stdio.h>

void strturn(char*, char*);

int main()
{
  setbuf(stdout, NULL);
  char  Text1[50], Text2[50];
  puts("Gib einen Text ein:");
  gets(Text1);
  strturn (Text1, Text2);
  printf("Das ist verkehrt:\n%s", Text2);
  
  return 0;
}

void strturn(char *String1, char *String2)
{
  char *Start = String1;
  printf("Start: %s\n", Start);
  while (*String1 != '\0') String1++;
  String1--;
  while (String1 >= Start){
	printf("String1: %s  String2: %s\n",String1, String2);
	*String2++ = *String1--;
  }
  *String2 = '\0';
  printf("String1: %s  String2: %s\n",String1, String2);
}
Folgende Fragen habe ich dazu:
1.

Code: Alles auswählen

printf("String1: %s  String2: %s\n",String1, String2);
Warum darf ich bei printf()-Funktion nicht *String1 und *String2 schreiben (mit dem Zeigeroperator)? Schließlich will ich ja hier den Inhalt der Speicherstelle anzeigen und nicht die Adresse, worauf der Zeiger zeigt. Wenn ich mit * versuche, stürzt das Programm ab.
2.

Code: Alles auswählen

printf("String1: %s  String2: %s\n",String1, String2);
Warum sehe ich beim String2 keine richtige Ausgabe, sondern nur irgendwelche Zeichen, aber mit String1 klappt es einwandfrei.
Gib einen Text ein:
asdfg
Start: asdfg
String1: g String2: ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠
╠asdfg
String1: fg String2: ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠
╠asdfg
String1: dfg String2: ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠
╠asdfg
String1: sdfg String2: ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠
╠asdfg
String1: asdfg String2: ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠
╠asdfg
String1: ╠asdfg String2:
Das ist verkehrt:
gfdsa
3. Wie ist die Reihenfolge bzw. was wird zuerst gemacht?
*String2++ = *String1--;
Wird zuerst String1 dekrementiert, dann String2 inkrementiert und erst dann die Zuweisung gemacht? Verändern String2 und String1 ihre Adressen VOR der Zuweisung, oder erst danach, wenn die Anweisung abgeschlossen ist - also nach Semikolon?

4.

Code: Alles auswählen

  *String2 = '\0';
  printf("String1: %s  String2: %s\n",String1, String2);
Warum ist bei der letzten printf()-Funktion die Ausgabe von String2 komplett leer (siehe oben)?

5. Warum muss ich bei der Parameterübergabe mit Arrays ohne Adressoperatoren arbeiten?

Code: Alles auswählen

  strturn (Text1, Text2);
Also warum geht &Text1, &Text2 nicht (mit dem Adressoperator)? Ich weiß, dass Arrays Zeiger sind, und Zeiger haben 3 Werte: ihre eigene Adresse im Speicher, Adresse auf die sie Zeigen und den Inhalt der Speicherstelle. Und wenn ich an den Parameter ihre eigene Adresse weitergebe, und nicht die Adresse wohin sie zeigen, was passiert dann (also statt funk(Zeiger) -> funk(&Zeiger)?

Danke!

PS: Wo/wie kann ich hier einstellen, dass ich C-Code benutze? Damit die Formatierung besser aussieht.

Benutzeravatar
cloidnerux
Moderator
Beiträge: 3125
Registriert: Fr Sep 26, 2008 4:37 pm
Wohnort: Ram (Gibts wirklich)

Re: Beispiel mit Zeigern - einige Fragen

Beitrag von cloidnerux » Fr Mär 07, 2014 1:18 pm

Warum darf ich bei printf()-Funktion nicht *String1 und *String2 schreiben (mit dem Zeigeroperator)? Schließlich will ich ja hier den Inhalt der Speicherstelle anzeigen und nicht die Adresse, worauf der Zeiger zeigt. Wenn ich mit * versuche, stürzt das Programm ab.
Weil du mit Pointern arbeitest.
String1 ist vom Typ "char*", *String1 ist vom Typ char, printf möchte aber eben einen String haben, dass ist Datentyp char*. Deswegen stürzt auch dein Programm ab, da mit *String1 das erste Zeichen als Pointer interpretiert wird und das nix geben kann.
Warum sehe ich beim String2 keine richtige Ausgabe, sondern nur irgendwelche Zeichen, aber mit String1 klappt es einwandfrei.
Weil dem String das "Null-Byte" fehlt('\0'). Mit dem Null-Byte wird das Ende des Strings gekennzeichnet, dementsprechend werden so lange Zeichen aus dem Speicher ausgegeben, bis printf ein Null-Byte findet, das führt zu undefinierten Ausgaben und auch zum Absturz. Die Ausgabe des Inhalts von String2 ist in der while schleife:

Code: Alles auswählen

while (String1 >= Start){
   printf("String1: %s  String2: %s\n",String1, String2);
   *String2++ = *String1--;
  }
nicht empfehlenswert.
Wird zuerst String1 dekrementiert, dann String2 inkrementiert und erst dann die Zuweisung gemacht? Verändern String2 und String1 ihre Adressen VOR der Zuweisung, oder erst danach, wenn die Anweisung abgeschlossen ist - also nach Semikolon?
Jemand anders kann dir das sicherlich beantworten(mfro?), ich würde das explizit aufschreiben:

Code: Alles auswählen

while (String1 >= Start)
{
   *String2 = *String1;
   String2++;
   String--;
}
Warum ist bei der letzten printf()-Funktion die Ausgabe von String2 komplett leer (siehe oben)?
Weil es das ist, was du da programmiert hast und hängt wieder zusammen mit dem Null-Byte.
Du schreibst an die Stelle, an die der Pointer gerade zeigt ein Null-Byte. printf nimmt den Pointer, liest das Null-Byte und gibt dementsprechend nichts aus.
Also warum geht &Text1, &Text2 nicht (mit dem Adressopeartor)? Ich weiß, dass Arrays Zeiger sind, und Zeiger haben 3 Werte: ihre eigene Adresse im Speicher, Adresse auf die sie Zeigen und den Inhalt der Speicherstelle. Und wenn ich an den Parameter ihre eigene Adresse weitergebe, und nicht die Adresse wohin sie zeigen, was passiert dann (also statt funk(Zeiger) -> funk(*Zeiger)?
Dann arbeitet deine Funktion strturn mit irgendwelchen werten.
Was du hier machst ist ja eine Funktion zu schreiben, die zum einen einen Pointer auf einen Text erwartet und einen auf ein stück Speicher in den man schreiben kann.
Wenn du dieser Funktion die Adressen deiner Pointer übergibst, wird halt damit gearbeitet. Da aber an der Stelle, wo dein Pointer im Speicher liegt, nicht dein Text liegt, passiert irgendwas. Deswegen übergibst du die Pointer ohne zusätzliche referenzierungsparameter: Du willst die Adresse des Speichers haben, auf die der Pointer zeigt.
Redundanz macht wiederholen unnötig.
quod erat expectandum

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8862
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: Beispiel mit Zeigern - einige Fragen

Beitrag von Xin » Fr Mär 07, 2014 1:21 pm

forumnewbie hat geschrieben:Hallo!
Ich will Deine Frage nicht abwürgen, aber Dich einfach mal auffordern die ersten drei Kapitel hiervon zu lesen:

c:tutorial:advanced
forumnewbie hat geschrieben:PS: Wo/wie kann ich hier einstellen, dass ich C-Code benutze? Damit die Formatierung besser aussieht.
Der 'code'-Tag passt schon.
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

forumnewbie
Beiträge: 80
Registriert: Di Jan 15, 2013 9:02 pm

Re: Beispiel mit Zeigern - einige Fragen

Beitrag von forumnewbie » Fr Mär 07, 2014 2:20 pm

@cloidnerux, danke für deine Erklärung.

@Xin, ich habe bereits 2 Programmierbücher (C und C++) gelesen (auf Deutsch). Das große Problem bei diesen Büchern ist, dass dort ganz einfache Sachen extrem ausführlich erklärt werden, aber sobald es zu wirklich komplexen Sachen kommt, wie Zeiger, Rekursionen (Algorithmen) usw sind die Erklärungen so dürftig und knapp und mit ganz wenigen Beispielen (manchmal nur eins), dass danach noch ganz viele offene Fragen bleiben. Man denkt man versteht es, aber sobald man andere/solche Beispiel wie oben trifft, stößt man auf Schwierigkeiten. Ich werde mir die Tutorials trotzdem durchlesen und mich ggf nochmal hier melden, wenn etwas noch unklar sein sollte.

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8862
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: Beispiel mit Zeigern - einige Fragen

Beitrag von Xin » Fr Mär 07, 2014 2:58 pm

forumnewbie hat geschrieben:@Xin, ich habe bereits 2 Programmierbücher (C und C++) gelesen (auf Deutsch).
Und... bringt das was? ^^

Leg die Bücher weg, und benutz die Tastatur. Programmierung ist Handwerk und Kunst. Also ausprobieren, begreifen, wegwerfen, besser machen.
Und so hin und wieder guckst Du in ein Buch, um Dich inspirieren zu lassen, Deine Kunst weiter zu entwickeln.
forumnewbie hat geschrieben:Das große Problem bei diesen Büchern ist, dass dort ganz einfache Sachen extrem ausführlich erklärt werden, aber sobald es zu wirklich komplexen Sachen kommt, wie Zeiger, Rekursionen (Algorithmen) usw sind die Erklärungen so dürftig und knapp und mit ganz wenigen Beispielen (manchmal nur eins), dass danach noch ganz viele offene Fragen bleiben. Man denkt man versteht es, aber sobald man andere/solche Beispiel wie oben trifft, stößt man auf Schwierigkeiten. Ich werde mir die Tutorials trotzdem durchlesen und mich ggf nochmal hier melden, wenn etwas noch unklar sein sollte.
Lies Dir das Tutorial durch und gib mir Feedback, was du nicht verstehst. Ich habe früher C unterrichtet und hoffe mit dem ersten beiden Blöcken die wichtigsten Verständnis-Fragen umschifft zu haben. Das ist kein reiner Spaßkurs, sondern ich lege großen Wert darauf, Begriffe und deren Bedeutung sehr scharf zu skizzieren und abzugrenzen. Das kann man als zu theoretisch empfinden, wenn ich unterrichtet habe, hat sich aber gezeigt, dass es der schnellste Weg ist zu verstehen, was man tut.

Ansonsten ist Programmieren lernen durchaus auch ein Selbsterfahrungskurs: Man rennt solange mit dem Kopf gegen die Wand, bis man versteht, wie eine Tür funktioniert und sobald man das verstanden hat, ist es eigentlich doch ganz einfach. Aber so isses halt - da muss man halt durch.
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

mfro
Beiträge: 346
Registriert: Mi Jan 16, 2013 4:58 pm

Re: Beispiel mit Zeigern - einige Fragen

Beitrag von mfro » Fr Mär 07, 2014 3:54 pm

cloidnerux hat geschrieben:
Wird zuerst String1 dekrementiert, dann String2 inkrementiert und erst dann die Zuweisung gemacht? Verändern String2 und String1 ihre Adressen VOR der Zuweisung, oder erst danach, wenn die Anweisung abgeschlossen ist - also nach Semikolon?
Das ist eigentlich ganz einfach - steht das "++" oder "--" vor der Variable, wird erst inkrementiert (bzw. dekrementiert) und dann der Wert verwendet (Präinkrement), steht es dahinter, wird erst der Wert verwendet und dann inkrementiert (bzw. dekrementiert).

Beispiel:

Code: Alles auswählen

int i = 0, j = 1;

printf("%d\n", i++ + ++j);
Weil hier "++" nach dem "i" steht, ist der linke Teil der Addition beim printf "0", der rechte entsprechend (weil "++" vor dem "j" steht) 2.

So was kann, darf und sollte man also auch genau so schreiben, das ist eindeutig.

Knifflig wird's erst, wenn Seiteneffekte auftreten, die je nach Auswertereihenfolge unterschiedliche Ergebnisse ergeben können:

Code: Alles auswählen

int i = 0;

printf("%d\n", i++ + i + 2);
Die Ausführungsreihenfolge ist hier unbestimmt - die Addition ist kommutativ, es bleibt also dem Compiler überlassen, ob er vor der Addition erst den rechten oder erst den linken Teilausdruck berechnet.
Von daher gesehen, könnte hier sowohl 2 als auch 3 rauskommen.

Der Standard macht sich's einfach und sagt: Ergebnis ist unbestimmt. Der Compiler darf hier also auch 99 ausrechnen, wenn er will (so was hat man also ganz klar bleiben zu lassen bzw. durch Klammern eindeutig zu machen).

So ist das eben mit C: man muß wissen, was man macht. Garbage in, Garbage out.
It's as simple as that. And remember, Beethoven wrote his first symphony in C.

forumnewbie
Beiträge: 80
Registriert: Di Jan 15, 2013 9:02 pm

Feedback Teil 1

Beitrag von forumnewbie » Fr Mär 07, 2014 5:49 pm

@mfro, danke für deine Antwort!

@Xin
Dann fange ich mal an. Zunächst Arrays + Strings. (Mit "Lob" meinte ich die Stellen, die BESONDERS gut zum Verständnis beitragen, was nicht bedeutet, dass andere Stellen schlecht sind :) )
Arrays
http://www.proggen.org/doku.php?id=c:tutorial:arrays
Hier haben wir also einen Identifier eins vom Typ int und einen Identifier vier vom Typ int[4], vier ist also viermal so groß wie eins.
In den eckigen Klammern wird wieder ein Expression des Typs Integer erwartet.
Wenn man auf einen englischen Begriff verzichten kann, bitte diesen durch einen deutschen Begriff ersetzen oder am Anfang irgendwo erklären was das genau ist/heißt - auch wenn man ihn mit mehreren deutschen Wörtern umschreiben/erklären muss. Dafür ist dann aber jedem klar was genau das bedeutet und ein englisches Wort hat in der deutschen Sprache in der Regel viele (auch unterschiedliche) Bedeutungen.
int object = array[ 17 ]
In den eckigen Klammern wird wieder ein Expression des Typs Integer erwartet. Übergeben wird ein Index und dieser muss ja abzählbar sein. Dass es ein Expression ist bedeutet, dass wir eine Konstante, wie in diesem Beispiel 17, angeben können, eine Variable, wie i im Beispiel zuvor oder auch eine Formel beliebiger Länge zur Berechnung des Indexes wird akzeptiert:
Ich finde - kompliziert geschrieben. Du meinst, wir weisen einfach der Variable objekt einen Wert mit der Index 17 aus dem Vektor array zu?

Beim Wort Array würde ich auch einen Bezug zur Mathematik herstellen und sagen, dass ein Array nichts anderes als ein Vektor, bzw. Matrix/Tabelle mit 1 Spalte und n Zeilen oder 1 Zeile und n Spalten ist. Dann hat man sofort ein Bild im Kopf, weil man das schon x mal auf dem Taschenrechner benutzt hat. Und wenn nicht, dann kann sich jeder eine Tabelle mit Daten bestimmt vorstellen.

Nehmen wir nun an, dass wir ein sehr großes Array haben, dass es einfach zuviel Aufwand macht, es so zu initialisieren. Wir wissen ja bereits, dass sich C um die Initialisierung von Variablen nicht kümmert, also kümmert es sich auch nicht um die Initialisierung von vielen Variablen. Auch hier müssen wir also selbst ran und hierfür bietet sich natürlich eine Schleife an:
Warum muss ein Array in einer Schleife vorher mit Nullen initialisiert werden? Kann man es nicht später mit verschiedenen Werten initialisieren, wenn wir diese auch brauchen?

Lob: Tabelle "Wie funktioniert ein Arrays" mit Erklärung ist sehr gut!
Mit sizeof kann man auch die Größe eines Arrays in Arrays bestimmmen.
Wie ist das gemeint mit "Arrays in Arrays"?
Wenn man wissen möchte, wie groß ein Array ist, muss man allerdings die Größe des Datentypes mit verrechnen.
Meinst du du hier die Anzahl der Elemente in einem Array und nicht die Größe des Arrays (bezogen auf dein Beispiel)?

Strings
http://www.proggen.org/doku.php?id=c:tutorial:cstrings
for( i = 0; text; i = i + 1 )

text vielleicht in text!=0 ändern, da man sonst eventuell die Abbruchbedingung nicht versteht
und i = i+1 in i++ ändern. Könnte einen Anfänger verwirren, da in der Literatur der Zählschritt/Anweisung mit ++ in der for-Schleife gemacht wird.

Für den Compiler spielt es keine Rolle, ob Du 0 oder '\0' schreibst, in beiden Fällen wird ein char mit 0 beschrieben.

Eventuell dazu schreiben, dass wenn man aber '0' schreibt die Zahl NULL im Text erscheint.
Laut ASCII steht dez 0 für das Zeichen NUL^@. NUL^@ entspricht dann dem Zeichen \0 ? Warum steht in der Tabelle nicht einfach \0?

while( text[length] )
length = length + 1;

Die Schleife wird abgebrochen, sobald das Zeichen \0 gefunden wird, weil wir als Rückgabewert beim text[length] kein Zeichen sondern eine Dezimalzahl 0 zurückbekommen, und somit die Abbruchbedingung erfühlt ist. Richtig?
Ich würde diesen Zusammenhang mit der Schleife und Zeichen \0 => dez 0 an dieser Stelle nochmal erwähnen, dh die Logik in den Beispielen an schwierigen Stellen mit 1-2 Sätzen näher erläutern. Für einen Anfänger ist das nicht sofort klar, wenn überhaupt.

Lob: Einzelne Zeichen suchen und austauschen - ein super Beispiel!


So, nun brauche ich eine Pause :) Hoffe, dass ich bei meinem Feedback kein Blödsinn geschrieben habe - manchmal passiert das, wenn man zu lange auf den Monitor starrt :)
Was Bücher angeht - da bin ich einer anderen Meinung. Mir haben sie am Anfang trotzdem sehr geholfen - auch wenn sie leider nicht perfekt sind. Tutorials sind im Vergleich zu den Büchern deutlich kürzer - was nicht schlecht sein muss. Tutorials sind meiner Meinung nach für Benutzer mit Grundkenntnissen geeignet, für blutigen Anfänger sind aber Bücher besser - da man dort Sachen am Anfang viel ausführlicher behandelt und mehrmals wiederholt.

Ein Vorschlag: Eure Tutorials könnte man noch besser machen, wenn man nach jedem Tutorial mehrere Aufgaben dazu gibt, die ein Leser selbständig versuchen kann zu lösen. Und natürlich Lösungen dazu wenn man nicht weiterkommt und für den Vergleich.

forumnewbie
Beiträge: 80
Registriert: Di Jan 15, 2013 9:02 pm

Feedback Teil 2

Beitrag von forumnewbie » Fr Mär 07, 2014 11:24 pm

So, hier der versprochene Teil 2. War fleißig :)
Zeiger
http://www.proggen.org/doku.php?id=c:tutorial:pointer
Und hier finden wir die 0 schon wieder. Und diesmal tritt sie nicht als 0 oder '\0' (Nullbyte) auf, für Zeiger schreibt man NULL. NULL wird komplett groß geschrieben und ist kein Schlüsselwort von C. Um es verwenden zu können, muss man eine Standardbibliothek wie die stdlib oder die stdio einbinden. Auch NULL hat natürlich wieder den Wert 0, aber der Entwickler kann im Quelltext das Wort NULL sofort erkennen und weiß, dass es sich um einen Nullzeiger handelt und nicht um ein Nullbyte:
Kann ich statt NULL einfach 0 benutzen und das würde das selbe bedeuten? Dann könnte ich über #define NULL 0 auch ohne Standardbibliothek auskommen?

In der stdlib findet sich auch die Erklärung, was der Rückgabewert EXIT_SUCCESS in C bedeutet. Er sollte von der main()-Funktion zurückgegeben werden, wenn das Programm erfolgreich beendet wurde.
Welcher Zusammenhang besteht zwischen EXIT_SUCCESS und einem Zeiger? Zuvor steht Erklärung zum NULL Zeiger gefolgt von einem Doppelpunkt und dann plötzlich EXIT_SUCCESS.
Aus dem Datentyp (char **) wird durch den Dereferenzierungsoperator der „*“ weggenommen: man erhält den Datentyp (char *). Aus dem Datentyp (char *) wird der Datentyp (char).
Warum fällt jedes mal ein * weg und wofür macht man das überhaupt? Welchen praktischen Nutzen hat es ** zu benutzen?
Also warum soll man so etwas machen char ** pointerPointer = &pointer; ? Habe das leider nicht verstanden. Vielleicht könnte man das irgendwie anschaulich/grafisch erklären.

Lob: Verwandtschaft von Arrays und Zeigern - gutes Beispiel! Zwar am Anfang nicht leicht zu verstehen, aber man sieht wie man über die Adresse des Zeigers auf ein Array-Element mittels eines Zeigeroperators zugreifen kann.

array[index] entspricht *(zeiger+index)
&array[index] entspricht zeiger+index
Richtig? Gibt es auch *array[index]? Wäre das vielleicht **(zeiger+index)?

Wo ist dann der Unterschied? Ein Array mit angegebener Größe stellt Speicher zur Verfügung - ein Pointer nicht.
char * pointer; // Kein eigener Speicher, zeigt irgendwo hin
Ein (leerer) Zeiger müsste aber doch auch irgendwo im Arbeitsspeicher sein, oder nicht? Wenn ich &Zeiger verwende, dann erhalte ich seine persönliche Speicheradresse, oder verstehe ich das falsch?

Konstante Strings
Nochmal zum Geschwindigkeitsunterschied zwischen char text[] = "proggen.org"; und char const * text = "proggen.org";
Bei der ersten Variante muss zuerst ein Array aus n Elementen angelegt werden und in jedes Array-Element wird ein Zeichen reinkopiert und anschließend im Arbeitsspeicher komplett abgelegt, nachdem das Array erstmal alle seine "Spalten" gefüllt hat?
Und bei der zweiten wird nur die Anfangsadresse an den Zeiger zugewiesen und anschließend sofort das Array/Text in dem Arbeitsspeicher abgelegt. Somit spart man sich n-1 Kopiervorgänge. Verstehe ich das richtig?
Wenn ja, dann würde ich das in der Beschreibung so ausführlicher erklären, wenn nein, anders erklären - dann habe ich es nicht richtig verstanden :)
Sie eignen sich also dazu, um bei der Initialisierung eines Arrays in das Array kopiert zu werden, so dass wir dann unser Array verändern können, ...
Habe diesen Satz erst nach mehreren Versuchen verstanden, was hier gemeint ist. Eventuell bisschen einfacher formulieren.
Wir haben uns vorhin die Adresse wie einen Index in einem Array vorgestellt. Und das trifft es sehr gut, denn ein Array ist für viele Entwickler einfacher zu verstehen, als ein Pointer, doch in Wirklichkeit ist ein Array komplizierter als ein Zeiger.
Für wenn ist ein Array komplizierter und warum? Arrays/Vektoren kenne ich aus der Mathematik, und mit Zeigern habe ich immer noch Schwierigkeiten. Für mich sind Zeiger viel abstrakter und somit komplizierter.

Bei diesem Zeiger-Tutorial habe ich an einigen Stellen Verständnisprobleme. Es wäre super, wenn jemand mir diese Fragen - zumindest zum Zeiger-Tutorial beantworten könnte. Und dann ggf. die Erklärungen im Toturial verbessern/anpassen.

forumnewbie
Beiträge: 80
Registriert: Di Jan 15, 2013 9:02 pm

Re: Beispiel mit Zeigern - einige Fragen

Beitrag von forumnewbie » Sa Mär 08, 2014 12:52 am

Und nun nochmal zurück zu meinem Beispiel.

Erklärung auf Frage 1 leider nicht verstanden.
Wenn ich bei printf() eine Variablen mit einem Wert benutze, dann übergebe ich diese Variable als Wert an die Funktion printf() und nicht ihre Adresse. Beim String übergebe ich aber die Adresse des ersten Zeichen einer Zeichenkette an die Funktion printf(). Erkennt die Funktion printf() anhand von %s, dass es sich um einen String handeln MUSS und verlangt deshalb unbedingt eine Adresse der Zeichenkette und nicht deren Inhalt? Der Grund dafür ist, dass die Funktion sonst nicht weiß wie lange der String ist und das Ende dann selbst anhand von /0 ermittelt, um damit den kompletten Satz vollständig auf dem Bildschirm anzuzeigen und nicht nur ein einzelnes Zeichen. Wenn ja, dann habe ich es verstanden.

Erklärung auf Frage 2 fast verstanden.
Solange dort keine /0 steht, kennt die Funktion das Ende nicht. Warum sieht man aber bei der Ausgabe von String2 in der while()-Schleife nach den komischen Zeichen den kompletten Inhalt des String1(asdfg) jedes mal? Ich hätte erwartet, dass die Ausgabe von String2 in der while()-Schleife so aussieht: 1: |||||||||, 2:g||||||||| 3:f||||||||| 4:d|||||||| etc. Also zuerst lesbare Zeichen und dann Datenmüll.

Erklärung 3 hoffentlich verstanden.
Ich fasse zusammen: der Inhalt der letzten Adresse des String1 wird in die erste(Index 0) Adresse des String2 geschrieben und erst nach dieser Zuweisung verändert beide Zeiger ihre Adressen. Wenn falsch, bitte korrigieren. Dh der Zuweisungsoperator hat an dieser Stelle höhere Priorität als die Postinkrement und Postdekrement Operatoren.

Erklärung auf Frage 4 fast verstanden.
Warum funktioniert die Ausgabe von Text2 in der main()-Funktion? Weil ich die Adressen von Text2 nicht inkrementiert habe, wie bei String2, und Text2 die ganze Zeit auf die erste Adresse mit der Index 0 zeigt?

Erklärung 5 verstanden.
Wenn eine Funktion als Parameter einen Zeiger erwartet, dann muss ich dieser Funktion die Adresse des Speicherstelle übergeben, damit sie den Inhalt dieser Speicherstelle verändern kann. Den Zeiger selbst zu verändern - also über seine eigene physische Adresse, macht keinen Sinn. Ich kann dort dann keine Zahlenwerte oder sogar Zeichenketten abspeichern.


Danke!!!

mfro
Beiträge: 346
Registriert: Mi Jan 16, 2013 4:58 pm

Re: Beispiel mit Zeigern - einige Fragen

Beitrag von mfro » Mi Mär 12, 2014 8:49 am

Ich hätte hier eine Anregung: ich habe mir das Tutorial durchgelesen und finde das Schlüsselwort "const" offen gestanden nicht wirklich gut erklärt.

Hier wird impliziert, als ob "const" mit "Konstante" zu übersetzen wäre und diese "Tatsache" anschließend mit der einen von tausend Verwendungsmöglichkeiten des Schlüsselwortes "bewiesen", bei der das tatsächlich (ausnahmsweise) so ist: bei der Initialisierung von String-Arrays (bei der C und C++ - falls möglich - wirklich dafür sorgen, daß die Zeichen in einem nicht beschreibbaren Segment landen).

"const" ist m.E. ein sehr unglücklich gewähltes Schlüsselwort, weil es - natürlich - "konstant" (=unveränderbar) impliziert. Was man damit aber als Programmierer lediglich machen kann, ist (außer der "einen Ausnahme") zu erklären, daß man vorhat, auf diese Variable über diesen Zugriffspfad nur lesend zuzugreifen (demnach hätte das Schlüsselwort eigentlich konsequenter- (und richtiger) weise "readonly" heissen müssen). "const" ist also eigentlich nur eine "Willenserklärung".

Genauso war die ursprüngliche Bedeutung in C (und genauso sollte sie meiner Ansicht nach auch erklärt werden - sonst hat man spätestens dann ein Problem, wenn man einem Anfänger anschließend erklären will, warum man Variablen "ungestraft" const volatile (??) deklarieren kann - und daß das auch tatsächlich zu etwas gut ist).

Wenn ich's richtig weiß, gab es bei der Standarddefinition tatsächlich sogar mal Bemühungen, "const" durch "readonly" zu ersetzen, die "Verfechter der reinen Lehre" konnten sich gegen die "Bewahrer der Kompatibilität" aber offensichtlich nicht durchsetzen.

C++ hat die Sache eigentlich nur noch schlimmer gemacht, indem es versucht hat (dort, wo es ging) "const"-Variablen noch ein wenig "konstanter" zu machen. Trotzdem kann man ein "const" (meistens) immer noch "wegcasten" oder noch schlimmer, auf der Linker-Ebene versehentlich oder absichtlich dafür sorgen, daß aus "const" plötzlich doch "nicht const" wird (in .o-Dateien ist die "const"-Information - bis auf "die Ausnahme" - verschwunden, so daß man Module immer so zusammenlinken kann, daß aus einer als Konstante beabsichtigten Variable eine veränderbare wird).
It's as simple as that. And remember, Beethoven wrote his first symphony in C.

Antworten