C Programmierung Array und strings

Gast #4138912
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.
Gast #4138955
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.
Gast #4138976
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.
Gast #4138978
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.
Gast #4138989
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?!
Gast #4139128
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.
Gast #4139138
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?)
Persönliche Seite #4139231
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.
#4139248
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.
Gast #4139425
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?
#4139437
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";
#4139612
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'
#4139637
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...
#4139654
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.
Gast #4139920
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.
#4139947
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?
#4142932
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren