Forum: PC-Programmierung C Programmierung Array und strings


von iGast (Gast)


Lesenswert?

Hallo,

beschäftige mich gerade mit der  Array Programmierung von strings in C, 
aber irgendwie will es nicht funktionieren.
Habe viele Beispiele gefunden aber nur für c++.
Wie wird in C die strings in Array geschrieben?
Habe z.B. char string [5]={`test1','test2','test3'}; schon versucht, 
aber ohne Erfolg.

: Verschoben durch Moderator
von Peter II (Gast)


Lesenswert?

iGast schrieb:
> char string [5]={`test1','test2','test3'}; schon versucht,
> aber ohne Erfolg.

ist ja auch falsch.

char* werte[] = {"test1","test2","test3"};

von iGast (Gast)


Lesenswert?

Welche Bedeutung hat der * bei char?

von Grundlagen (Gast)


Lesenswert?

iGast schrieb:
> Welche Bedeutung hat der * bei char?
So deklarierst du einen Pointer.
Grundlagen Kinder, Grundlagen...

von Peter II (Gast)


Lesenswert?

iGast schrieb:
> Welche Bedeutung hat der * bei char?

die gleiche Bedeutung wie bei jedem anderen Datentyp. Damit definiert 
man einen Zeiger.

Eventuell ist es zeit für ein C-Buch.

von Rene S. (Firma: BfEHS) (rschube)


Lesenswert?

Für den Anfang zu empfehlen: C von A bis Z von Jürgen Wolf
Das umfassende Handbuch für Linux, Unix und Windows

findest du auch kostenlos über die Suchmaschine deines Vertrauens oder 
hier http://www.win-tux.de/

: Bearbeitet durch User
von lazarus (Gast)


Lesenswert?

Grundlagen schrieb:
> iGast schrieb:
>> Welche Bedeutung hat der * bei char?
> So deklarierst du einen Pointer.


Den er hier gar nicht braucht.
1
#include <stdio.h>
2
3
int main(void)
4
{
5
  char str1[][10] = {"eins", "zwei", "drei", "zehn", "zwanzig", "dreissig"};
6
7
  printf("%s\n", str1[0]);
8
  printf("%s\n", str1[1]);
9
10
  return 0;
11
}

Aber wie immer werden dem Anfänger hier gerne bewusst Steine in den Weg 
gelegt.

von Peter II (Gast)


Lesenswert?

lazarus schrieb:
> Aber wie immer werden dem Anfänger hier gerne bewusst Steine in den Weg
> gelegt.

dein Beispiel ist natürlich viel besser, jedem Anfänger ist sofort klar 
war das die 10 hier bedeutet.

char str1[][10]

von lazarus (Gast)


Lesenswert?

Peter II schrieb:
> dein Beispiel ist natürlich viel besser, jedem Anfänger ist sofort klar
> war das die 10 hier bedeutet.
>
> char str1[][10]

Es ist immerhin besser als euer sinnloses "nim nen Pointer" Gehabe mit 
Verweis auf "lies die Grundlagen".

Er hat so ein einfaches, funktionierendes Beispiel. Einmal im Debugger 
angeschaut und er weiß bescheid.

Ihr seid halt einfach zu verbildet und habt den Blick fürs Einfache 
längst verloren.

von Peter II (Gast)


Lesenswert?

lazarus schrieb:
> Es ist immerhin besser als euer sinnloses "nim nen Pointer" Gehabe mit
> Verweis auf "lies die Grundlagen".

> Er hat so ein einfaches, funktionierendes Beispiel. Einmal im Debugger
> angeschaut und er weiß bescheid.


und was passt dir daran nicht?

> char* werte[] = {"test1","test2","test3"};

liefert im Debugger genau das gleiche wie dein Programm.

von lazarus (Gast)


Lesenswert?

Peter II schrieb:
> und was passt dir daran nicht?
>
>> char* werte[] = {"test1","test2","test3"};
>
> liefert im Debugger genau das gleiche wie dein Programm.

Er weiß anscheinend noch nicht was ein Pointer ist. Sinnvoller halte ich 
den Weg ihm erst mal ein einfaches Beispiel ohne "unbekanntes Sternchen" 
zu zeigen. Er kommt schon noch früh genug auf Pointer. Du fängst in der 
Mathe doch auch nicht bei den komplexen Zahlen C an, sondern erst mal 
mit den natürlichen Zahlen N oder etwa nicht?!

von Frank (Gast)


Lesenswert?

Das wusste ja vorher keiner, dass er es nicht weis.
Er hätte es ja wissen können ;-)

von Noch einer (Gast)


Lesenswert?

Tja, C ist eine extrem anfängerfeindliche Sprache.

Im einem Zusammenhang ist "char *var[10]" und "char var[][10]" das 
gleiche. Im andern Zusammenhang muss man da unterscheiden.

Erstaunlich, dass die Unis ausgerechnet diese Sprache für 
Anfängerübungen ausgewählt haben.

von Tombo (Gast)


Lesenswert?

char str1[][10] = {"eins", "zwei", "drei", "zehn", "zwanzig", 
"dreissig"};

ist ein zweidimensionales array..mit
 char str1[] = {"eins"};
wäre es ein einfaches array

das [] dient bei c dafür das C selber die länge des Strings ermittelt, 
ist vermutlich die sicherste Art das zu evrwenden, weil es schief geht, 
wenn der Strin kurz definiert wird
[10] wiederrum bedeutet das es ei String mit einer Zeichenlänge von 10 
Zeichen ist im Gegensatz zu Pascal nicht /0 terminiert(richtig?)

von Dirk B. (dirkb2)


Lesenswert?

Tombo schrieb:
> [10] wiederrum bedeutet das es ei String mit einer Zeichenlänge von 10
> Zeichen ist im Gegensatz zu Pascal nicht /0 terminiert(richtig?)

Ist wohl etwas missverständlich.

In C sind die Strings und Stringliterale nullterminiert.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Tombo schrieb:
> das [] dient bei c dafür das C selber die länge des Strings ermittelt,

Genauer: Der Compiler ermittelt die Anzahl der Arrayelemente. Mit 
Strings hat das zunächst nichts zu tun.

> ist vermutlich die sicherste Art das zu evrwenden, weil es schief geht,
> wenn der Strin kurz definiert wird

Das nun nicht.

> [10] wiederrum bedeutet das es ei String mit einer Zeichenlänge von 10
> Zeichen ist im Gegensatz zu Pascal nicht /0 terminiert(richtig?)

Es bedeutet, daß das betreffende Array mit Platz für zehn Elemente 
angelegt wird, ob die nun initialisiert (d.h. ab Start mit einem Wert 
belegt) werden oder nicht.

Empfehlenswert, dringend empfehlenswert ist die Lektüre eines Buchs 
über C.
Der Klassiker "Brian Kerninghan & Dennis Ritchie: Programmieren in C" 
ist zwar schon recht alt und gibt nicht die neueste Inkarnation des 
Sprachstandards wieder, aber damit ist das, was im Buch steht, weder 
falsch noch überholt.

Und so grundlegende Sprachelemente wie Pointer, Arrays und deren 
Initialisierung sind auch im eng mit C verwandten C++ nicht elementar 
anders.

von (prx) A. K. (prx)


Lesenswert?

Noch einer schrieb:
> Erstaunlich, dass die Unis ausgerechnet diese Sprache für
> Anfängerübungen ausgewählt haben.

Die normative Kraft des Faktischen
           -oder-
man nimmt das, was man selber kennt.

Vor zig Jahren hatte ein Informatik-Prof die Programmiersprache seines 
programmierbaren TI Taschenrechners (der TI-59 wars wohl) als Basis des 
Prinzips von Assembler-Programmierung eingesetzt.

: Bearbeitet durch User
von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Noch einer schrieb:
> Erstaunlich, dass die Unis ausgerechnet diese Sprache für
> Anfängerübungen ausgewählt haben.

Ja, Praxisorientierung ist im universitären Umfeld auch eher 
überraschend.

von vloki (Gast)


Lesenswert?

Hat der TS irgendwo uni erwähnt ???

von iGast (Gast)


Lesenswert?

Vielen Dank an Lazarus für seinen Beitrag. Wie ich sehe ist dies nicht 
so einfach wie es manche kommentiert haben. Auch in meinem Buch, und das 
ist Einsteiger und Grundlagen wird dies nicht richtig erklärt. Ich 
denke, jetzt habe ich es verstanden. Können auch mehrdimensionale Felder 
mit strings?

von Daniel A. (daniel-a)


Lesenswert?

Tombo schrieb:
> char str1[][10] = {"eins", "zwei", "drei", "zehn", "zwanzig",
> "dreissig"};
>
> ist ein zweidimensionales array.

Wobei zu beachten ist das die 6 char[10] immer 10 zeichen enthalten. Bei 
einem grösseren c-string von z.B. 10 zeichen Würde dass durch den 
c-string angefügte nullbyte (11tes zeichen im char array) abgeschnitten. 
Grössere char-arrays/c-strings werden genauso abgeschnitten. Kleinere 
strings verbrauchen trotzdem 10 zeichen platz. Das obere array 
verbraucht 10*6*sizeof(char)=60 bytes speicherplatz.

>mit
>  char str1[] = {"eins"};
> wäre es ein einfaches array

Nein, dass ist der versuch ein char mit einem char[5] zu befüllen.
Das muss so aussehen:
char str1[] = "eins";

von Daniel A. (daniel-a)


Lesenswert?

Ich versuche mich mal an einer Erklärung für Arrays und Pointer.

Arrays:

Eindimensionale und Mehrdimensionale Arrays sind das selbe. Der Kompiler 
kennt ihren type, die Addresse des Ersten Elements, und die Grösse aller 
oder von allen auser der letzten Dimension.

Der Compiler kennt bei der Definition immer die Grösse aller 
Dimensionen. Das braucht er um die Grösse des benötigten 
Speicherbereichs zu Berechnen. Die Grösse des Speicherbereichs ist die 
Summe der Grösse der Dimensionen des Arrays mal die Grösse seines 
Datentyps.

Wenn man ein Element eines n-Dimensionalen Arrays abfragt Rechnet der 
Compiler einfach folgendes (glaub ich zumindest):
Wobei:
 n = Anzahl der Dimensionen
 i = index in der Arraydimension
 d = grösse der dimensionen

Beispile:
1
char x[][5][4];
2
char y = x[2][3][4];
1
 n = 4
2
 d = {?,5,4}
3
 i = {2,3,4}
4
 resultat:
5
   4 + (4*5)*2 + (5)*3 = 59
Also das Element Nummer 59 man könnte in c nun auch schreiben:
1
char y = x[0][0][59];
und hätte das selbe Element.
Oder anders ausgedrückt: Das speicherlayout von char[5][4] ist identisch 
zum Speicherlayout von char[5*4], nur der Datentyp ist etwas anders, 
aber den kennt das Programm nach dem Compilieren nichtmehr.

Ein Array besitzt also: Dimensionen, einen Type und eine Startadresse.

Pointer:

Nun zu den Pointern. Ein Pointer zeigt auf eine Speicheradresse. Er hat 
einen Datentype. Seine grösse ist immer sizeof(void*). Sein wert ist die 
Stelle wo er hinzeigt. Pointer können auch auf Pointer zeigen. Mit 
ausname eines Pointers auf void ist jeder Pointer ein Arithmetischer 
Datentype (man kann damit rechnen).

Ein Beispiel:
1
char x = 123; // Kompiler reserviert speicher für ein char, steicherstelle enthält 123
2
char* y = &x; // Kompiler reserviert speicher für einen Pointer, steicherstelle enthält einen Pointer auf Speicherstelle von x
3
char** z = &y; // Kompiler reserviert speicher für einen Pointer, steicherstelle enthält einen Pointer auf Speicherstelle von y

z zeigt nun auf y welches auf x zeigt.
**z = dereferenzieren von z (zweiter stern), gibt wert von y, und danach 
deferenzieren des erhaltenen wertes (*y oder zweiter stern von **z), 
gibt wert von x.
Alternative screibweise von **z ist z[0][0].

Man kann einem Pointer die Adresse eines Arrays zuweisen.
Beispiel:
1
char x[5][4][];
2
char* y = x[0][0];
3
char* z = &x[0][0][0];
4
char* w = (char*)x;
Hier haben y, z und w den selben Wert, sie zeigen auf das erste Element 
von x.

Pointerarithmetik:
Das ist einfach. Man referenziert mit einem & und dereferenziert mit 
einem *. Beim Rechnen wird der Datentyp als Einheit genommen.

Beispiel:
1
const char* x = "test abc";
2
const char* y = x + 5;
3
const char* z = &y[5];
Hier zeigt x auf das 't' in "test abc".
Es zeigen y und z auf das 'a' in "test abc".
Der Compiler rechnet den Wert von x  den Wert von x in beiden fällen + 
5*sizeof(char), wobei bei z=&y[5] y[5] den erhaltenen wert 
dereferenziert und das & referenziert die stelle wo der wert steht dann 
wieder. Natürlich MUSS der Compiler diese überflüssige rechenoperation 
wegoptimieren. Das dereferenzieren mit einem [] wird vor der 
dereferenzierung mit einem * ausgeführt.

Und jetzt nehmen wir das gesammte gesammelte Wissen und kombinieren 
Pointer mit Mehrdimensionalen Arrays auf möglichst komplizierte weise:
1
int main(){
2
  const char* a[2][3] = {
3
    { "1", "2", "3"},
4
    { "4", "5", "6"}
5
  };
6
  const char* b[2][3] = {
7
    { "7", "8", "9"},
8
    {"x0","y1","z2"}
9
  };
10
  const char* (*c)[2][3] = &a;
11
  const char* (*d)[2][3] = &b;
12
  const char*(*e[2])[2][3] = {c,d};
13
  const char x = **((*e[1])[1]+1);
14
15
  printf("x ist: %c\n",x);
16
17
  return 0;
18
}
Die Ausgabe ist: "x ist: y"
So berechnet das der Compiler:
Das zweite Element von e, also e[1] oder *(e+1) ist ein Pointer auf den 
Speicherbereich von d der die Adresse des Arrays von b als pointer 
enthalt. Wir dereferenziert d und erhalten die Adresse von b, sie hat 
folgenden type: const char*[2][3]. Bei (*e[1])[1] oder b[1] erhalten wir 
die Adresse von {"x0","y1","z2"} mit dem type const char*[3]. Der 
Compiler Rechnet dabei ((const char*[3])(*e[1])+(1*3)+1) oder ((const 
char*[3])(*e[1])+4) oder ((const char**)(*e[1])+4) oder (const 
char**)b+4. Das wird dan dereferenziert. Wir erhalten b[1][1] oder 
*((const char**)b+4) mit type const char*. Das ist der Eintrag "y1" 
(zeigt auf das y von "y1"). Wir dereferenzieren das *"y1" und erhalten 
'y'

von Daniel A. (daniel-a)


Lesenswert?

Und dann ist noch anzumerken, dass Arrays und Funktionspointer eine 
besonderheit aufweisen: da ihre Adresse nirgens gespeichert ist kann man 
sie strengenommen nicht referenzieren/dereferenzieren:
1
  const char test[] = "test";
2
  const char (*test2)[] = &test;
3
//  const char (*test3)[] = test; // geht nicht, falscher datentype
4
  printf("%p %p %p %p\n",test,&test,test2,&test2); // Alle nummern ausser die letzte sind gleich
Das heist, ein Pointer auf ein Array und die Adresse eines arrays 
unterscheiden sich nur dadurch, ob diese tatsächlich irgendwo im 
Speicher liegen oder nicht. Es gibt durchaus fälle wo das zu Problemen 
führen kann. Bei Funktionspointern waren die c leute aus unerfindlichen 
gründen etwas weniger streng:
1
void x(){} // type void()()
2
void(*z)()=&x; // type void(*)()
3
void(*y)()=x; // geht auch...

von Sebastian V. (sebi_s)


Lesenswert?

Daniel A. schrieb:
> Und dann ist noch anzumerken, dass Arrays und Funktionspointer eine
> besonderheit aufweisen: da ihre Adresse nirgens gespeichert ist kann man
> sie strengenommen nicht referenzieren/dereferenzieren:

Das Fachwort dafür heißt "Array to Pointer decay" und ich hätte es 
anders beschrieben als "ihre Adresse ist nirgens gespeichert". 
Eigentlich sagt die Regel nur aus, dass wenn man den Namen eines Arrays 
benutzt, dieser in fast allen Fällen zu einem Pointer auf das erste 
Element zerfällt. Ausnahmen sind wenn man den Address-Of-Operator 
benutzt, also &array_name liefert auch einen Pointer aufs erste Element. 
Die Adresse eines Arrays ist also gleich der Adresse des ersten 
Elements. Die andere Ausnahme ist sizeof.

Ähnliches gibts für Funktionen. Wenn man den Namen einer Funktion 
benutzt aber dann keinen Funktionsaufruf macht verhält es sich wie ein 
Pointer auf die besagte Funktion.

: Bearbeitet durch User
von W.S. (Gast)


Lesenswert?

Daniel A. schrieb:
> Also das Element Nummer 59 man könnte in c nun auch schreiben:char y =
> x[0][0][59];und hätte das selbe Element.
> Oder anders ausgedrückt: Das speicherlayout von char[5][4] ist identisch
> zum Speicherlayout von char[5*4], nur der Datentyp ist etwas anders,
> aber den kennt das Programm nach dem Compilieren nichtmehr.

Ach ja?

Ich geb dir mal was zu bedenken, also was ist:
char otto[3];
denn nun?
Wenn ich also bei einer Funktion string_out(char* s) hinschreibe
string_out(otto);
was ist dann otto? Jaja, ein Zeiger ist das, der auf das Feld otto[3] 
zeigt.


Und nun die Preisfrage:
was ist otto, wenn ich schreibe:
char otto[5][3];

Nun?
Es mag durchaus sein, daß die Elemente aller finalen Felder direkt 
aneinander grenzen, aber das MUSS NICHT zwingend so sein.

W.S.

von Daniel A. (daniel-a)


Lesenswert?

W.S. schrieb:
> Ich geb dir mal was zu bedenken, also was ist:
> char otto[3];
> denn nun?
Ein char array mit 3 elementen.
> Wenn ich also bei einer Funktion string_out(char* s) hinschreibe
> string_out(otto);
> was ist dann otto? Jaja, ein Zeiger ist das, der auf das Feld otto[3]
> zeigt.
Nein. otto ist immernoch ein char Array mit 3 elementen. otto[3] ist ein 
bufferoverflow. s würde die addresse von otto, also eine referenz auf 
otto[0], das erste element von otto, enthalten. s bleibt dabei ein 
pointer auf chars. Steht oben alles schön nach themen sortiert.
>
> Und nun die Preisfrage:
> was ist otto, wenn ich schreibe:
> char otto[5][3];
>
> Nun?
Ein Mehrdimensionales Array von char elementen.
> Es mag durchaus sein, daß die Elemente aller finalen Felder direkt
> aneinander grenzen, aber das MUSS NICHT zwingend so sein.
Es ist ein mehrdimensionales Array, also muss es so sein. Vermischt du 
hier vileicht Mehrdimensionale Arrays und Arrays von Pointern gedanklich 
auf falsche weise?

PS: Was meinst du mit final im bezug auf felder und welchen Preis hab 
ich gewonnen?

von Rolf M. (rmagnus)


Lesenswert?

lazarus schrieb:
> Peter II schrieb:
>> dein Beispiel ist natürlich viel besser, jedem Anfänger ist sofort klar
>> war das die 10 hier bedeutet.
>>
>> char str1[][10]
>
> Es ist immerhin besser als euer sinnloses "nim nen Pointer" Gehabe mit
> Verweis auf "lies die Grundlagen".

Ja, wen interessieren heutzutage schon noch Grundlagen?

Sebastian V. O. schrieb:
> Ähnliches gibts für Funktionen. Wenn man den Namen einer Funktion
> benutzt aber dann keinen Funktionsaufruf macht verhält es sich wie ein
> Pointer auf die besagte Funktion.

Da gibt's aber einen Unterschied zu Arrays. Bei Funktionen kann man 
sowohl ein &, als auch ein *, oder sogar beliebig viele davon vor den 
Namen schreiben. Rauskommen tut dabei immer ein Zeiger auf die Funktion.

W.S. schrieb:
> Es mag durchaus sein, daß die Elemente aller finalen Felder direkt
> aneinander grenzen, aber das MUSS NICHT zwingend so sein.

Doch, muss es. Ein Array "Typ[n] arr" ist immer sizeof(Typ) * n Bytes 
groß. Dabei spielt es keine Rolle, ob Typ ein char oder selbst wieder 
ein Array ist.

Daniel A. schrieb:
> Nein. otto ist immernoch ein char Array mit 3 elementen. otto[3] ist ein
> bufferoverflow. s würde die addresse von otto, also eine referenz auf
> otto[0], das erste element von otto, enthalten.

Das ist nicht das gleiche. Die Speicherzelle, auf die referenziert wird, 
ist zwar gleich, aber der Typ ist verschieden. Bestandteil grunsätzlich 
jedes Werts in C ist ein Typ. In dem Sinne ist es nicht die Adresse des 
Arrays, sondern die Adresse von dessen erstem Element.

von c-hater (Gast)


Lesenswert?

Daniel A. schrieb:

> Die Grösse des Speicherbereichs ist die
> Summe der Grösse der Dimensionen des Arrays mal die Grösse seines
> Datentyps.

Das kann sich ja wohl nur um einen Witz handeln.

Falls nicht: Geh' sterben. Aber bitte umgehend.

von Daniel A. (daniel-a)


Lesenswert?

c-hater schrieb:
> Daniel A. schrieb:
>
>> Die Grösse des Speicherbereichs ist die
>> Summe der Grösse der Dimensionen des Arrays mal die Grösse seines
>> Datentyps.
>
> Das kann sich ja wohl nur um einen Witz handeln.
>
> Falls nicht: Geh' sterben. Aber bitte umgehend.

Lieber nicht... Summe und Produkt kann doch jeder mal verwechseln...
Aber gut aufgepasst!

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.