Anfänge in C - Strings, Pointer, Array

leon_20v

Erfahrenes Mitglied
Hallo,

ich studiere gerade Informatik, hatte aber zuvor noch nie was mit Programmiern am Hut. Jetzt wollt ich mal euch Fragen ob ihr mir evtl. 3 Fragen beantworten könnt.

1. Warum eigentlich Pointer? Ich weiß das man mit malloc einen Bereich reservieren kann und dann auf diesen mit einem Pointer zeigen kann, aber warum? Das kann ich doch auch mit einem normalen Array? Worin liegt der Sinn?

2. Wie muss ich ein Array deklarieren wenn ich ein Wort z.b. "Auto" oder "Golf" in myArray[0] speichern möchte? "int car" ist doch nur für einen Buchstaben? habt ihr mir hier vielleicht ein Code Schnipsel? Nennt sich das dann String?

3. Wie kann ich dann z.B. wenn ich dann mit "scanf" das Wort Auto eingegeben habe, hier dann mit den Zahlen rechnen die Dahinter stehen? Also dem Ansi-Code? und wie kann ich die z.b. anzeigen lassen?


Wäre cool wenn ihr mir evtl. einen kleinen Codeschnipsel zu jedem geben könntet, damit es anschaulicher ist :)

Vielen Dank, meine Kollegen in der Schule können mir auch nicht richtig weiterhelfen, wir sind eben alle blutige Anfänger :)
 
Hi leon_20v,

1. malloc() ist dynmisch. Angenommen du willst eine Datei komplett einlesen, weißt aber nicht wie groß diese ist wenn du das Programm schreibst. Du könntest hier ein Array mit einer Riesen-Größe erstellen, jedoch ergeben sich dadurch 2 Nachteile:
  • Ist die Datei nur ein paar Byte groß, verschwendest du einen großen Brocken an Speicher
  • Ist die Datei größer als dein Array, kannst du die nicht komplett einlesen
Mit malloc() holst du dir einfach soviel wie du grad brauchst, unabhängig davon wie groß die Datei ist. Da malloc() dynamisch Speicher in einem Block allokiert (reserviert), kann es natürlich auch passieren dass kein so großes Stück mehr im Arbeitsspeicher vorhanden ist. Daher muss man das, was malloc zurückgibt immer überprüfen, ob es auch auf einen gültigen Bereich zeigt:
C:
// wir erstellen ein array mit 1000 Elementen aus Integer-Zahlen.
// Der Cast muss sein, da malloc() einen void* zurückgibt. Diesen musst
//    du dann in den Typ umwandlen, den du brauchst.
// da die Größe einer Integerzahl nicht fix definiert ist und variieren kann,
//    wird hier auch mit sizeof() die Größe eines Integer ermittelt und dann
//    mit 1000 Elementen multipliziert. Somit reservieren wir genug Speicher
//    um 1000 Elemente zu speichern, egal wie groß ein Integer ist.
int* test1 = (int*) malloc(sizeof(int) * 1000);

if(test1 == NULL) {  // Wenn malloc() NULL zurückgibt, dann konnte kein Speicher allokiert werden.
  puts("Speicher konnte nicht allokiert werden.");
} else {
  printf("Die Adresse des Speicherbereichs ist: %p\n", test1);
}

2. Für den Computer sind Buchstaben eigentlich auch nur Zahlen. Falls du es etwas präziser brauchst, dann kannst du ja mal nach ASCII-Tabellen suchen ;)
Für diese ASCII-Zeichen gibt es nun einen speziellen Datentyp, nämlich "char". Ein C-String ist eine Verkettung von chars und wird mit einem '\0' abgeschlossen.
Etwas anders ausgedrückt ist ein C-String ein "Null-Terminiertes Array aus chars".
C:
char test1[6] = "Hallo";
// 6 Zeichen weil ja das '\0' auch noch dazu muss

char* test2 = "Hallo";
// ist das selbe wie oben, du hast dann ein Array aus 6 Elementen.

3. Da ja ein String wie oben beschrieben aus Zahlen besteht, kannst du damit ganz einfach rechnen:
C:
char* test3 = "Auto";

int zaehler;
int summe;

// durchlaufen aller Zeichen des Strings (hier halt fix für das Wort "Auto" mit der Länge 4)
summe = 0;
for(zaehler=0; zaehler<4; zaehler++) {
  summe = summe + test3[zaehler];
}
printf("Die Summe der ASCII-Codes ist (statisch): %u\n", summe);

// oder dynamisch:
summe = 0;
for(zaehler=0;; zaehler++) {
  if(test3[zaehler] == '\0') break;  // Abbruch bei \0 (Am Ende des Strings)
  summe = summe + test3[zaehler];
}

printf("Die Summe der ASCII-Codes ist (dynamisch): %u\n", summe);

Gruß
BK
 
Zuletzt bearbeitet:
Danke für deine Super antworten! Sehr ausführlich :)

Wo hast du denn den Code her? hast du den gerade Selber geschrieben oder hast du den aus einem Internet Wiki?

Dann muss ich leider nochmal was fragen :(.

was ist "puts"?

und zu den Arrays:
char test1[6] = "Hallo";

6 ist doch eins zuviel? ich fang doch bei test1[0] an und da steht das H, oder habe ich da was falsch verstanden?
 
Hi,

die Codes hab ich schnell selbst geschrieben und teils aus meinen Sourcen rauskopiert ;)
Wenn du über eine Funktion stolperst, die du noch nicht kennst, dann würde ich dir diese Online-Referenz empfehlen, die hat mir schon oft geholfen:
puts()

Wie ich oben schon gesagt habe, ist ein C-String immer durch ein \0 abgeschlossen. Dieses Zeichen musst du immer hinzurechnen wenn du mit Strings arbeitest.
"Hallo" selbst belegt somit 6 chars ('H', 'a', 'l', 'l', 'o', '\0').
Der Hintergrund ist, dass das Programm ja nicht weiß, wie lange der String ist. Wenn das \0 fehlen würde, dann würde dein Programm mit einem Speicherzugriffsfehler (oft auch "liebevoll" segfault genannt) abstürzen, da das Programm den Speicher so lange weiterlaufen würde, bis er ein \0 findet. Und da ist die Gefahr recht groß, dass er wo hin gelangt wo er nicht hin dürfte.
Beispiel:
C:
char* my_string;

my_string = (char*) malloc(sizeof(char) * 4); // Wir reservieren 4 Zeichen
strncpy(my_string, "test", 4);  // Wir kopieren die 4 Zeichen von "test" in den String

// Und nun BAM:
puts(my_string); // Könnte abstürzen und noch ein paar wirre Zeichen ausgeben!

Wenn du in C / C++ einen String mit doppelten Anführungszeichen schreibst, so ist dieser automatisch richtig terminiert.

// Edit: Das obige Beispiel zeigt nicht ganz, was passieren kann (evtl hat das mein Compiler auch "richtig hin" optimiert), hier nochmal ein vollständiges Programm:
C:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

int main(void) {
  char my_string[4] = {'a', 'b', 'c', 'd'};
 
  // Eigentlich sollte er ja nur 4 Zeichen ausgeben, oder?
  puts(my_string);

  printf("\n\nGröße von my_string laut strlen(): %u\n", strlen(my_string));

  return 0;
}

// Edit-2:
Ach ja, bei malloc() fällt mir noch ein, dass du da den Speicher auch wieder freigeben musst / solltest.
C:
char* test = (char*) malloc(12345);

// ... damit arbeiten

free(test);

Gruß
BK
 
Zuletzt bearbeitet:
C:
char test1[6] = "Hallo";
// 6 Zeichen weil ja das '\0' auch noch dazu muss

char* test2 = "Hallo";
// ist das selbe wie oben, du hast dann ein Array aus 6 Elementen.
Das ist falsch. Ein Array ist kein Zeiger. Ersteres ist ein Array, das Untere ein Zeiger. Außerdem ist das Array test1 hier beschreibbar, ein Schreibzugriff auf den test2-Zeiger ist gemäß ANSI C Standard nicht definiert.
Probiere mal
C:
assert(sizeof test1==sizeof test2);
Außerdem ist statt
C:
char test1[6] = "Hallo";
viel robuster
C:
char test1[] = "Hallo";
weil der Compiler AUTOMATISCH ein abschließendes '\0' Zeichen anfügt und somit auch die Länge des Arrays richtig festlegt. Wenn du "Hallo" mal in irgendetwas anderes änderst, macht der Compiler automatisch die Definition und Initialisierung richtig, d.h. hängt ein '\0' an und setzt die Größe, anders als wenn du z.B.
C:
char test1[5]="Hallo";
gemacht hättest, wobei KEIN '\0' angehängt wird, was sicher nicht beabsichtigt ist.

Dein Auto-Array könnte mit Initialisierung z.B. so aussehen:

C:
char *autos[] = {"Golf","Daimler","Toyota"};
size_t i, groesse = sizeof autos/sizeof*autos;
for( i=0; i<groesse; ++i )
  puts(autos[i]);
Wenn du variable Daten in ein Array übernehmen möchtest, must du anders vorgehen.
puts ist eine ANSI C Standardfunktion, die den übergebenen String zuzüglich '\n' auf stdout ausgibt.
 
[...] Außerdem ist das Array test1 hier beschreibbar, ein Schreibzugriff auf den test2-Zeiger ist gemäß ANSI C Standard nicht definiert.[...]

Warum sollten die Elemente, auf die der Zeiger zeigt nicht beschreibbar sein? Es ist Zeiger auf chars, wobei die chars nicht const sind.

[...]
viel robuster
C:
char test1[] = "Hallo";
[...]
Ist mir schon klar dass das "robuster" ist, da man bei einer Änderung nicht auch noch die Anzahl der Zeichen in die Klammern schreiben muss. Das hat aber nur indirekt mit dem Compiler zu tun. Sobald eine Zeichenkette in C mit doppelten Anführungszeichen umschlossen ist, wird diese laut Standard als String behandelt und automatisch mit einem \0 versehen, egal ob das Array nun groß genug ist oder nicht.
Das mit der Element-Zahl bei den Arrays könnte man weglassen (wie du vorgeschlagen hast), daran hab ich aber in dem Moment gar nicht gedacht, danke ;)

[...]Das ist falsch. Ein Array ist kein Zeiger. Ersteres ist ein Array, das Untere ein Zeiger.[...]
Beim Zugriff im obigen Beispiel ist es egal, ob es nun ein Array (von fixer Größe) oder ein Zeiger ist.
Das mit dem assert ist mir auch klar, das sizeof ist ein Compiler-Makro. Bei einem Array mit fixer Größe ist dem Compiler bekannt, wie viele Elemente darin gespeichert sind. Somit liefert er diese Anzahl auch zurück. Wenn du das mit einem char* als Zeiger machst dann liefert er die Größe des Zeigers, da er ja nicht weiß wieviele Elemente darin gespeichert sind

@Trulleberg:
Ich habe mich eher darauf beschränkt, das wichtigste und offensichtlichste zu schildern, natürlich könnte man zu den Themen Seitenweise Text schreiben um wirklich alles abzudecken. Da leon aber erst mit dem Thema angefangen hat, wollte ich da nicht allzu sehr in die Tiefe gehen.

Gruß
BK
 
Warum sollten die Elemente, auf die der Zeiger zeigt nicht beschreibbar sein? Es ist Zeiger auf chars, wobei die chars nicht const sind.
Trulleberg hat Recht.

Du hast die Variable zwar als "char*" deklariert, diese zeigt allerdings auf ein Stringliteral, welches laut ANSI C Standard konstant ist. Modifikationen des Speichers über die Variable resultieren in undefiniertem Verhalten.
Ist mir schon klar dass das "robuster" ist, da man bei einer Änderung nicht auch noch die Anzahl der Zeichen in die Klammern schreiben muss. Das hat aber nur indirekt mit dem Compiler zu tun. Sobald eine Zeichenkette in C mit doppelten Anführungszeichen umschlossen ist, wird diese laut Standard als String behandelt und automatisch mit einem \0 versehen, egal ob das Array nun groß genug ist oder nicht.
Die Zeichenkette selbst ist richtig mit \0 terminiert. Allerdings erstellst du ein Array, welches mit dem Inhalt der Zeichenkette initialisiert wird. Und das Array ist (interpretiert als C-String) demnach nicht ordentlich mit \0 terminiert (falls du die Anzahl der Zeichen falsch gezählt hast).

Gruß
 
Zuletzt bearbeitet:
okay okay, aber meine frage ist irgendwo noch immer nicht geklärt:

warum ist
Code:
char test1[5]="Hallo";
falsch?

test1[0] = "H"
test1[1] = "a"
test1[2] = "l"
test1[3] = "l"
test1[4] = "o"
test1[5] = '/0'


Das sind für mich 5 und nicht 6?

wenn ich hallo per schleife ausgebe, dann lasse ich die doch auch 5 mal durchlaufen, beginnend bei i=0?
 
warum ist
Code:
char test1[5]="Hallo";
falsch?

test1[0] = "H"
test1[1] = "a"
test1[2] = "l"
test1[3] = "l"
test1[4] = "o"
test1[5] = '/0'


Das sind für mich 5 und nicht 6?
Die Zahl in den eckigen Klammern bei der Deklaration eines Arrays gibt nicht den maximalen Index an, sondern die Anzahl der Elemente. Und die ist in diesem Fall eindeutig 6.

Grüße,
Matthias
 
Zurück