PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Frage zum Zählen von Array-Inhalten


Gast
2010-04-16, 20:11:18
Haudi !

Ich hab da mal wieder ne Frage:


typedef struct {
unsigned char vorname[20];
unsigned char nachname[20];
unsigned char stadt[15];
unsigned char plz[5];
} ADRESSEN;

So, nun erstell ich nen Adresssatz:

ADRESSEN adr1;

...und befüll das Ding mit Daten:

memcpy(adr1.vorname, "Horst", 20);
memcpy(adr1.nachname, "Walter Fritzenburger", 20);
memcpy(adr1.stadt, "Bonn", 15);
memcpy(adr1.plz, "12345", 5);

So, nun wollt ich spaßeshalber die Längen der Inhalte zählen:

unsigned char vor = (unsigned char) strlen((char*) adr1.vorname);
unsigned char nach = (unsigned char) strlen((char*) adr1.nachname);
unsigned char stadt = (unsigned char) strlen((char*) adr1.stadt);


Der Nachname ist das einzige Array, das komplett voll is. Kann mir nun einer erklären, wieso das hier im Debugger steht:

http://www.pictureupload.de/originals/pictures/160410200838_blaaa.jpg

Was hat "Bonn" im Nachnamen zu suchen, obwohl es in der Detailansicht (Byte für Byte im Array) garnicht drinsteht ? Warum passiert das nur mit Elementen (Stadt), die nach einem komplett gefüllten Element (Nachname) kommen ?

Herzlichen Dank schonmal !

Neomi
2010-04-16, 20:37:53
Das sind C Strings, also nullterminierte Strings, die sind so lang bis eine terminierende '\0' (identisch zur 0, aber in dem Fall eindeutig ein char) den String beendet. Wie viel Platz der teilweise ausgefüllte Buffer bietet, ist für die Länge des tatsächlichen Strings uninteressant, solange der Buffer groß genug ist. Wenn du z.B. eine 5 in einem int speicherst, steht da ja auch die 5 drin, nicht die größtmögliche per int darstellbare Zahl.

Ein paar Fehler sind da noch:

1. Der Typ eines C Strings ist char* bzw. char[], ganz ohne unsigned. Dann kannst du dir auch das explizite Casten sparen, das in der genutzten Form nicht typsicher ist. Wenn du castest, um Fehlermeldungen des Compilers zu beseitigen, hast du nur die Symptome bekämpft, nicht das Problem. Tu sowas nicht, solange du nicht wirklich weiß, was du tust.

2. C Strings kopiert man mit strcpy oder einer entsprechenden Abwandlung, die kopieren dann auch nur bis zur terminierenden 0. Mit memcpy kannst du, wenn du über das Stringende hinaus liest, sogar eine Read Access Violation bekommen, je nach Platzierung des Strings im Speicher.

3. Es muß immer Platz für eine terminierende 0 sein. Der Nachname wird deshalb auch nicht terminiert, weil die erste 0 nach dem Start des Nachnamens beim Ende der Stadt ist. Hier brauchst du 21 Zeichen. Bei der PLZ ist das gleiche Problem, da wird sogar über das Ende der Struktur hinaus gelesen und es zählen die Daten, die danach zufällig stehen. Hier brauchst du 6 Zeichen Platz.

Für dich bedeutet das erstmal: Dokus wälzen, besonders die Stringfunktionen. Bei unbekannten Dingen keine Annahmen treffen, sondern in der Doku nachschauen. Solange du diese Art von String nicht verinnerlicht hast, nutze std::string für alle Zeichenketten. Wenn du C Strings und sämtliche damit einhergehenden Fallstricke verstanden hast, benutze weiter std::string für fast alles. Die C Strings sind nur für die Stellen, an denen das letzte bischen Performance wichtig ist (du dir also keine Heapoperationen leisten kannst) und die maximale Länge bekannt und relativ klein ist.

Gast
2010-04-16, 20:51:08
Soweit schonmal danke !

Wie greif ich denn dann auf die Elemente zu wenn ich "char *" statt "unsigned char" verwende ?

strcpy(adr1.vorname, "Horst");

gibt immer ne Zugriffsverletzung.

Gast
2010-04-16, 20:54:44
Also so meinte ich:

typedef struct {
char* vorname;
char* nachname;
char* stadt;
char* plz;
} ADRESSEN;

Stimmt das so überhaupt ?

Markus89
2010-04-16, 21:01:16
Du kannst genauso gut

char vorname[20]

benutzen. So wie du es machst steht ja nicht fest, wie groß dein String sein soll, denn "char* vorname" ist nur ein Pointer. Deswegen gibt es auch eine Zugriffsverletzung. Da müsstest du vorher noch Speicher reservieren.

Neomi
2010-04-16, 21:01:27
Sorry, ich hatte noch Infos dazueditiert, weil ich nicht dachte, daß du so schnell schon wieder reinschaust.

In einem char* speicherst du keinen String, sondern die Adresse eines Strings, dafür müßtest du erstmal den Speicher allokieren. Speicherverwaltung ist ein großes Thema für sich, da müßtest du dich erst länger mit beschäftigen. Wenn ich dir jetzt einfach ein paar Funktionen nennen würde, würdest du dir nur weitere Probleme aufhalsen (u.a. Speicherlecks). In deinem Fall wäre z.B. ein "char plz[6];" besser. Kein unsigned und die richtige Länge beachten.

Du solltest aber wirklich dringend zu std::string wechseln. Da kannst du dich dann nicht in der Größe vertun und mußt auch kein manuelles Speichermanagement durchführen.

Gast
2010-04-16, 21:05:55
Aber was ist gegen das "unsigned" zu sagen, wenn ich nur Werte zwischen 0 und 255 zulassen will ? :)

Neomi
2010-04-16, 21:17:52
Es ist ganz einfach der falsche Typ. Aus logischer Sicht bestehen Strings nicht aus einer Reihe von Zahlen, sondern aus Zeichen.

Gnafoo
2010-04-16, 23:06:33
Eine Anmerkung noch: man sollte generell strncpy verwenden und nicht strcpy, weil ersteres überprüft, ob der String überhaupt in den Zielpuffer hineinpasst. Ansonsten kann es sein, dass über den Puffer hinaus geschrieben wird. Beispielsweise würde hier die Stadt überschrieben, wenn der Nachname zu lang wäre. Wenn man es darauf anlegt, könnte man über den Eingabestring auch Schadcode einschleusen.

Der Nachteil ist, dass man die Größe des Puffers immer mit angeben muss. Aber wie Neomi schon gesagt hat, sollte man generell zu std::string greifen, wenn die Möglichkeit dazu besteht. Damit erspart man sich das ganze Gefriemel mit den Puffergrößen und es ist auch intuitiver.