-
Notifications
You must be signed in to change notification settings - Fork 2
/
Chapter8_String_german.tex
1255 lines (1015 loc) · 47.3 KB
/
Chapter8_String_german.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
%!TEX root = Main_german.tex
% LaTeX source for textbook ``How to think like a computer scientist''
% Copyright (C) 1999 Allen B. Downey
% This LaTeX source is free software; you can redistribute it and/or
% modify it under the terms of the GNU General Public License as
% published by the Free Software Foundation (version 2).
% This LaTeX source is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
% General Public License for more details.
% Compiling this LaTeX source has the effect of generating
% a device-independent representation of a textbook, which
% can be converted to other formats and printed. All intermediate
% representations (including DVI and Postscript), and all printed
% copies of the textbook are also covered by the GNU General
% Public License.
% This distribution includes a file named COPYING that contains the text
% of the GNU General Public License. If it is missing, you can obtain
% it from www.gnu.org or by writing to the Free Software Foundation,
% Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
\chapter{Strings and things}
\label{strings}
\section{Darstellung von Zeichenketten}
In den vorangegangenen Kapiteln haben wir vier Arten von Daten kennengelernt
--- Zeichen, Ganzzahlen, Gleitkommazahlen und Zeichenketten (Strings) --- aber nur drei Datentypen
für Variablen: {\tt char}, {\tt int} und {\tt double}. Bis jetzt haben wir keine
Möglichkeit kennengelernt eine Zeichenkette in einer Variable zu speichern
oder Zeichenketten in unserem Programm zu manipulieren.
In diesem Kapitel soll dieser Misstand behoben werden und
ich kann jetzt das Geheimnis lüften was das Besondere der Strings ist.
\index{String}
\index{String!Begrenzungszeichen}
\index{Typ!String}
Strings werden in C als Arrays von Zeichen gespeichert. Dabei wird das Ende der
Zeichenkette in dem Array durch ein besonderes Zeichen (das Zeichen {\tt '\textbackslash 0'})
markiert, was dazu führt, dass das {\tt char}-Array immer mindestens 1 Zeichen länger sein
muss als die zu speichernde Zeichenkette.
Mittlerweile können wir mit dieser Erklärung etwas anfangen und es wird klar, dass wir
erst ein ganze Menge über die Funktionsweise der Sprache lernen mussten,
bevor wir unsere Aufmerksamkeit den Strings und Stringvariablen zuwenden konnten.
Im vorigen Kapitel haben wir gesehen, dass Operationen
über Arrays nur minimale Unterstützung durch die Programmiersprache C
erhalten und wir die erweiterten Funktionen für den Umgang mit Arrays
selbst programmieren mussten.
Glücklicherweise liegen die Dinge etwas besser, wenn es um die Manipulation
dieser speziellen {\tt char}-Arrays geht, die wir zur Darstellung von Zeichenketten nutzen.
Es existiert eine Anzahl von Bibliotheksfunktionen in {\tt string.h}
welche uns die Handhabung und Verarbeitung von Strings etwas einfacher
machen, als die Verarbeitung von reinen Arrays.
\index{<string.h>}
\index{Header-Datei!string.h}
\index{Bibliothek!string.h}
Trotzdem muss man feststellen, dass die Stringverarbeitung in
C um einiges komplizierter und mühsamer ist als in anderen Programmiersprachen.
Die sorgfältige Beachtung dieser Besonderheiten ist notwendig um
die Verarbeitung von Zeichenketten nicht zu einer potentiellen Fehlerquelle in
unseren Programmen werden zu lassen.
\section{Stringvariablen}
Wir können eine Stringvariable als ein Array von Zeichen in der folgenden Art erzeugen:
\begin{verbatim}
char first[] = "Hello, ";
char second[] = "world.";
\end{verbatim}
Die erste Zeile erzeugt eine Stringvariable {\tt first} und weist ihr den Wert {\tt ''Hello,~''} zu.
In der zweiten Zeile deklarieren wir eine zweite Stringvariable. Erinnern wir uns,
die gleichzeitige Deklaration und Zuweisung eines Wertes zu einer Variablen
wird als Initialisierung bezeichnet.
Nur zum Zeitpunkt der Initialisierung können wir dem String einen Wert direkt zuweisen
(so wie bei Arrays im Allgemeinen). Die Initialisierungsparameter werden in der Form
einer Stringkonstanten in Anführungszeichen ({\tt''}\ldots {\tt''}) angegeben.
Wie wir zu einem späteren Zeitpunkt der Stringvariablen einen neuen Wert
zuweisen können, erfahren wir erst im Abschnitt~\ref{Zuweisung von neuen Werten an Stringvariablen}.
Auffallend ist der Unterschied in der Syntax der Initialisierung von Arrays und Strings.
Wir könnten den String auch in der normalen Arraysyntax initialisieren:
\begin{verbatim}
char first[] = {'H','e','l','l','o',',',' ','\0'};
\end{verbatim}
Es ist allerdings viel bequemer einen String als Stringkonstante zu schreiben.
Das sieht nicht nur natürlicher aus, sondern ist auch sehr viel einfacher einzugeben.
Wenn wir die Stringvariable direkt zum Zeitpunkt der Deklaration auch gleich initialisieren, ist
es normalerweise nicht notwendig, die Größe des Arrays mit anzugeben.
Der Compiler ermittelt die notwendige Größe des Arrays anhand der
angegebenen Stringkonstante. Wir können allerdings die Größe
der Stringvariable auch selbst definieren. Ich erkläre gleich, was wir dabei beachten müssen.
\index{String!Begrenzungszeichen}
Erinnern wir uns, was ich über den Aufbau eines Strings gesagt habe.
Es ist ein Array vom Typ {\tt char} in dem ein Begrenzungszeichen das Ende der
Zeichenkette markiert: das Begrenzungszeichen {\tt '\textbackslash 0'}. Dieses Zeichen
darf nicht mit dem Zeichen {\tt 0} verwechselt werden. Das Zeichen {\tt 0} wird in der ASCII-Tabelle
(siehe Anhang~\ref{ASCII-Table}) mit dem Wert 48 kodiert. Bei dem Zeichen {\tt '\textbackslash 0'}
handelt es sich um den Wert 0 in der Tabelle.
Normalerweise müssen wir das Begrenzungszeichen nicht selbst mit angeben.
Der Compiler versteht unseren Programmcode und fügt diese Zeichen automatisch ein.
Allerdings haben wir in dem vorigen Beispiel einen String genau wie ein Array behandelt
und in diesem Fall müssen wir uns selbst um das Einfügen des Begrenzungszeichen
kümmern.
Wenn wir eine Stringvariable benutzen, um
während des Programmablaufs unterschiedlich große Strings zu speichern,
müssen wir bei der Definition des Arrays die Größenangabe so wählen, dass
auch genügend Platz für die längste Zeichenkette reserviert wird.
Wir müssen weiterhin beachten, dass auch Platz für das Begrenzungszeichen
übrig bleibt, dass heißt, unser Array muss exakt ein Zeichen länger sein, als
die größte zu speichernde Zeichenfolge.
Wir können Strings auf dem Bildschirm ausgeben, in dem wir den Variablennamen
an die {\tt printf()} Funktion übergeben. Dazu verwenden wir den
Formatierungsparameter {\tt \%s}:
\begin{verbatim}
printf("%s", first);
\end{verbatim}
%%
\section{Einzelne Zeichen herauslösen}
Zeichenketten werden \emph{Strings} genannt, weil sie aus einer Folge (engl: string) von
Zeichen bestehen.
%Elektronische Datenverarbeitung - Stringmanipulationen.
Die erste Aufgabe die wir lösen wollen, besteht darin
aus einer Zeichenkette ein bestimmtes Zeichen herauszulösen.
Da wir wissen dass die Zeichenkette in C als Array gespeichert ist
können wir einen Index in eckigen Klammern ({\tt [} und {\tt ]}) für diese Operation benutzen:
\begin{verbatim}
char fruit[] = "banana";
char letter = fruit[1];
printf ("%c\n", letter);
\end{verbatim}
%
Der Ausdruck {\tt fruit[1]} gibt an, dass ich das Zeichen mit der Indexnummer~1
aus dem String namens {\tt fruit} ermitteln möchte. Das Resultat wird in einer {\tt
char}-Variable namens {\tt letter} gespeichert. Wenn wir uns anschließend den Wert
der Variable {\tt letter} anzeigen lassen, so sollte es nicht überraschen, dass dabei
der folgende Buchstabe auf dem Bildschirm ausgegeben wird:
\begin{verbatim}
a
\end{verbatim}
%
{\tt a} ist nicht der erste Buchstabe von {\tt ''banana''}. Wie wir bereits im letzten
Kapitel besprochen haben, nummerieren Informatiker die Elemente eines Arrays
immer beginnend mit 0. Das gilt auch für Stringvariablen.
Der erste Buchstabe von {\tt ''banana''} ist {\tt b} und hat den Index 0. Der zweite Buchstabe
{\tt a} den Index 1 und der Buchstabe mit dem Index 2 ist das {\tt n}.
Wenn wir also den ersten Buchstaben eines Strings ermitteln wollen müssen wir
die Null als Index in eckige Klammern setzen:
\begin{verbatim}
char letter = fruit[0];
\end{verbatim}
\section{Die Länge eines Strings ermitteln}
\index{String!Länge ermitteln}
\index{Länge ermitteln!String}
\index{<string.h>}
\index{Bibliothek!string.h}
Um die Länge eines Strings herauszufinden (die Anzahl der Zeichen die dieser String enthält),
können wir die Funktion {\tt strlen()} nutzen. Die Funktion wird aufgerufen indem wir den String
als Argument benutzen:
\begin{verbatim}
#include <string.h>
int main(void)
{
int length;
char fruit[] = "banana";
length = strlen(fruit);
return EXIT_SUCCESS;
}
\end{verbatim}
%
Der Rückgabewert von {\tt strlen()} ist in diesem Fall 6. Wir weisen diesen Wert der
Variablen {\tt length} zu, um ihn später weiter zu nutzen.
Um dieses Programm zu kompilieren, müssen wir die {\tt string.h} Bibliothek in unser
Programm einbinden. Diese Bibliothek stellt eine große Anzahl von nützlichen
Funktionen für den Umgang und die Bearbeitung von Zeichenketten bereit.
Es ist sinnvoll, sich mit diesen Funktionen vertraut zu machen, weil sie uns helfen
können unsere Programmieraufgaben einfacher und schneller zu lösen.
%The type of the {\tt strlen()} is {\tt size_t}, an unsigned value large enough to
%enumerate any object that the system can handle (such as a string).
%for our example we can safely assume that the size of the string object
%in our example will never
%exceed the range of the integer type.
%Notice
%that it is legal to have a variable with the same name as a function.
Um das letzte Zeichen in einem String zu finden mag es naheliegen
die folgenden Anweisungen einzugeben:
\begin{verbatim}
int length = strlen(fruit);
char last = fruit[length]; /* WRONG!! */
\end{verbatim}
%
Das funktioniert leider nicht. Der Grund dafür liegt wieder darin, dass {\tt fruit} ein
Array darstellt und einfach kein Buchstabe unter dem
Index {\tt fruit[6]} in {\tt ''banana''} gespeichert ist.
Wir erinnern uns: Der Index eines Arrays wird beginnend von 0 gezählt und
die 6 Buchstaben tragen deshalb die Indexnummern 0 bis 5. Um den letzten Buchstaben zu erhalten
müssen wir den Wert von {\tt length} um 1 verringern:
\begin{verbatim}
int length = strlen(fruit);
char last = fruit[length-1];
\end{verbatim}
\section{Zeichenweises Durchgehen}
%\index{traverse}
Eine häufig vorkommende Aufgabe besteht darin einen
String zeichenweise durchzugehen. Wir wählen das
ersten Zeichen eines Strings aus, führen irgendwelche
Operationen durch und wiederholen dieses Vorgehen, bis
wir beim letzten Zeichen des Strings angekommen sind.
Wir können diese Aufgabe sehr einfach mit Hilfe einer {\tt for} Schleife
erledigen:
\begin{verbatim}
int index;
for (index = 0; index < strlen(fruit); index++)
{
char letter = fruit[index];
printf("%i. Buchstabe: %c\n" , index+1, letter);
}
\end{verbatim}
%
\index{Schleifenvariable}
\index{Variable!Schleife}
\index{Index}
Der Name unserer Schleifenvariablen ist {\tt index}. Ein {\bf
Index} ist eine Variable oder ein Wert der ein bestimmtes Element
einer geordneten Menge identifiziert --- in unserem Fall der Menge
von Zeichen in einem String.
Der Index gibt dabei an, um welches Zeichen der Menge es sich dabei
handelt. Die Menge muss geordnet sein, so dass
jedem Buchstaben des Strings ein Index und jedem Index genau
ein Buchstabe eineindeutig zugewiesen ist.
Die Schleife geht den String zeichenweise durch und gibt
jeden Buchstaben auf einer eigenen Bildschirmzeile aus.
Um die natürliche Zählweise einzuhalten, geben wir den
Wert des Index erhöht um 1 aus (das hat keinen Einfluss auf
den Wert von {\tt index}).
Beachtenswert ist weiterhin die Formulierung der Bedingung unser Schleife
{\tt index < strlen(fruit)}. Wenn
der Wert von {\tt index} gleich der Länge des Strings ist, wird
die Bedingung falsch und der Schleifenkörper verlassen.
Das letzte Zeichen, welches wir ausgeben hat somit den
Index {\tt strlen(fruit)-1}.
\begin{description}
\item[AUFGABE:] Schreiben Sie eine Funktion welche einen {\tt string}
als Argument übernimmt und dann alle Buchstaben des Strings auf einer
Bildschirmzeile rückwärts ausgibt.
\end{description}
%\section{A run-time error}
%\index{error!run-time}
%\index{run-time error}
%Way back in Section~\ref{run-time} I talked about run-time errors,
%which are errors that don't appear until a program has started
%running.
%So far, you probably haven't seen many run-time errors, because we
%haven't been doing many things that can cause one. Well, now we are.
%If you use the {\tt []} operator and you provide an index that is
%negative or greater than {\tt length-1}, you will get a run-time
%error and a message something like this:
%\begin{verbatim}
%index out of range: 6, string: banana
%\end{verbatim}
%%
%Try it in your development environment and see how it looks.
%%
\section{Ein Zeichen in einem String finden}
\label{Finding a character in a string}
Wenn wir in einem String nach einem bestimmten Zeichen
suchen, müssen wir die gesamte Zeichenkette durchgehen
und die Position ermitteln, an der sich das Zeichen befindet.
Hier ist eine mögliche Implementation dieser Funktion:
\begin{verbatim}
int LocateCharacter(char *s, char c)
{
int i = 0;
while (i < strlen(s))
{
if (s[i] == c) return i;
i = i + 1;
}
return -1;
}
\end{verbatim}
Wenn wir diese Funktion aufrufen, übergeben wir der Funktion den
zu durchsuchenden String als erstes, und das zu suchende
Zeichen als zweites Argument.
Die Funktion gibt dann die erste ermittelte Position des Zeichens zurück.
Es kann allerdings auch vorkommen, dass das zu suchende
Zeichen gar nicht in dem String enthalten ist.
Für diesen Fall ist es sinnvoll einen Wert zurückzugeben, der
normalerweise nicht vorkommt und einen Fehlerfall signalisiert.
Wurde das Zeichen nicht gefunden so gibt die Funktion den Wert
{\tt -1} an die aufrufende Funktion zurück.
%%
\section{Pointer und Adressen}
\label{Pointers and Addresses}
\index{Pointer}
\index{Adresse}
Wenn wir uns die Definition der {\tt LocateCharacter()} Funktion
anschauen werden wir feststellen, dass die Angabe des Parameters {\tt char *s} ungewöhnlich aussieht.
Erinnern Sie sich noch daran, wie wir in Kapitel~~\ref{Passing an array to a function}
darüber gesprochen haben wie wir ein Array an eine Funktion übergeben?
Ich sagte, dass anstelle einer Kopie des Arrays ein Verweis auf
das Array an die Funktion übergeben wird. Ich habe aber nicht genau
erklärt, worum es sich bei dem Verweis genau handelt. Das werde ich jetzt nachholen.
%Remember, when we discussed how we had to pass
%an array to a function, back in Section~\ref{Passing an array to a function},
%we said that instead of copying the array, we only pass a reference to the function.
%Back then, we did not say exactly what this reference was.
C ist eine der wenigen High-level Programmiersprachen welche die direkte
Manipulation von Datenobjekten im Speicher des Computers unterstützt.
Um den direkten Zugriff auf Speicherobjekten durchzuführen, müssen
wir die Position der Objekte im Speicher kennen: ihre Adresse.
Genau wie andere Daten auch, können wir diese Adressen in Variablen
speichern und an Funktionen übergeben, denn so eine Adresse stellt ja selbst
wieder einen Wert dar. Adressen werden in Variablen eines speziellen Typs
gespeichert. Diese Variablen zeigen auf andere Objekte im
Speicher, wie zum Beispiel Integer-Werte, Arrays, Strings, usw. und werden deshalb
{\bf Pointer}-Variablen genannt.
%Adresses can be stored in variables of a special type.
%These variables that point to other objects in memory
%(such as variables, arrays and strings)
%are therefore called {\bf pointer} variables.
\index{Pointer}
\index{Zeiger|see {Pointer}}
Ein Pointer (manchmal auch als Zeiger bezeichnet) verweist auf die Stelle im Speicher des Computers, wo sich das
jeweilige Objekt befindet. Wenn wir einen Pointer definieren, müssen wir auch
immer angeben welchen Typ das Objekt besitzt, auf das verwiesen wird.
Die Definition einer Pointervariablen auf ein ganzzahliges Speicherobjekt vom Typ
{\tt int} kann folgendermaßen vorgenommen werden:
\begin{verbatim}
int *i_pointer;
\end{verbatim}
Diese Deklaration sieht ähnlich aus wie andere Variablendeklarationen, mit einem
Unterschied - dem Sternchen vor dem Variablennamen. Damit geben wir
dem Compiler bekannt, dass es sich bei {\tt i\_pointer} um eine Pointervariable
handelt.
Anders als bei normalen Variablen bezieht sich der angegebene Typ {\tt int} nicht
auf den Pointer selbst, sondern definiert auf welche Art von Objekten
dieser Pointer zeigen soll (in diesem Fall auf Objekte vom Typ {\tt integer}).
%. The type specification has nothing to do
%with the pointer itself, but rather defines which object this pointer is
%supposed to reference (in this case an {\tt integer}).
Das ist notwendig, damit mit der Compiler weiß, wie er das Speicherobjekt
behandeln soll, auf das der Pointer verweist. Der Pointer selbst ist normalerweise
nur eine Adresse im Speicher des Computers.
%This allows the compiler to do some type checking on, what would
%otherwise be, an anonymous reference.
Ein Pointer nur für sich allein hat keine sinnvolle Bedeutung. Wir benötigen immer
auch ein Objekt auf das der Pointer verweist:
\begin{verbatim}
int number = 5;
int *i_pointer;
\end{verbatim}
Diese Programmzeilen definieren eine {\tt int} Variable und einen Pointer.
Momentan besteht noch keinerlei Verbindung zwischen diesen beiden Variablen.
\section{Adress- und Indirektionsoperator}
\index{Pointer!Indirektionsoperator}
\index{Pointer!Adressoperator}
Um eine Verbindung zwischen einem Speicherobjekt und einem Pointer herzustellen
müssen wir den \textbf{Adressoperator}~{\tt \&} benutzen. Damit können wir
die Speicherstelle der Variablen {\tt number} ermitteln und dem Pointer
zuweisen. Man sagt: ``der Pointer {\tt i\_pointer} zeigt auf {\tt number}'':
\begin{verbatim}
i_pointer = &number;
\end{verbatim}
%{\tt}
%Pointer {\tt i\_p} now references integer variable {\tt number}.
Ab jetzt können wir auf den Wert der Variable {\tt number} auch über den
Pointer zugreifen. Dazu benötigen wir den \textbf{Indirektionsoperator}~{\tt *}.
Damit greifen wir auf das Objekt zu, auf das unser Pointer zeigt:
\begin{verbatim}
printf("%i\n", *i_pointer);
\end{verbatim}
Diese Programmzeile gibt {\tt 5} auf dem Bildschirm aus, welches dem
Wert der Variable {\tt number} entspricht.
Bei der Verwendung des Indirektionsoperators~{\tt *} müssen wir sehr gut
aufpassen. Wir dürfen niemals den Indirektionsoperator mit einem Pointer verwenden
der noch nicht initialisiert wurde:
\hint
\begin{verbatim}
int *z_pointer;
printf("%i\n", *z_pointer); /*** WRONG ***/
\end{verbatim}
Das Gleiche gilt wenn wir über einem Pointer dem Speicherobjekt einen neuen
Wert zuweisen wollen und der Pointer nicht initialisiert wurde. Hierbei kann es
zu Programmfehlern oder sogar zum Programmabsturz kommen:
\begin{verbatim}
int *z_pointer;
*z_pointer = 1; /*** WRONG ***/
\end{verbatim}
Beim Lesen von Programmen müssen wir aufpassen, dass wir
den vorangestellten Indirektionsoperators~{\tt *} nicht mit der
Deklaration von Pointern verwechseln. Auch bei der Deklaration verwenden
wir einen {\tt *}, der aber nur dazu dient die Variable als Pointervariable kenntlich
zu machen.
Leider ergibt sich daraus für Pointervariablen
der Umstand, dass eine direkte Initialisierung der Pointervariablen eine andere Syntax aufweist, als
eine spätere Zuweisung im Programm:\hint
\begin{verbatim}
int number = 5;
int *i_pointer = &number; /* Initialisierung des Pointers */
i_pointer = &number; /* gleicher Effekt! */
\end{verbatim}
Mit Pointern können wir also direkt auf Speicherstellen zugreifen und diese natürlich auch
verändern:
\begin{verbatim}
*i_pointer = *i_pointer + 2;
printf("%i\n", number);
\end{verbatim}
Unsere Variable {\tt number} hat jetzt den Wert {\tt 7} und wir fangen so langsam
an zu verstehen, wie es der {\tt LocateCharacter()} Funktion möglich ist
auf die Werte unserer Stringvariablen mittels eines {\tt char} -Pointers
zuzugreifen.
Viele C Programme benutzen Pointer und wir haben gerade einmal
die Oberfläche des Themas angekratzt. Durch die Verwendung von
Pointern können bestimmte Probleme sehr effizient gelöst werden,
allerdings kann der direkte und unkontrollierte Zugriff auf den Computerspeicher
auch zu schwerwiegenden Programmfehlern führen, wenn nicht
sehr sorgfältig gearbeitet wird, oder die Verwendung von Pointern nicht
komplett verstanden wurde. Der Compiler hat in diesen Fällen keine Möglichkeit
das richtige Programmverhalten zu prüfen.
Aus diesem Grund untersagen viele andere Programmiersprachen
die direkte Manipulation von Speicherstellen über Pointer.
%If we are looking for a letter in an {\tt string}, we may
%not want to start at the beginning of the string. One way
%to generalize the {\tt find} function is to write a version
%that takes an additional parameter---the index where we should
%start looking. Here is an implementation of this function.
%\begin{verbatim}
% int Find (char *s, char c, int i)
% {
% while (i < strlen(s))
% {
% if (s[i] == c) return i;
% i = i + 1;
% }
% return -1;
% }
%\end{verbatim}
%
%We have to pass the {\tt string}
%as the first argument. The other arguments are the character
%we are looking for and the index where we should start.
%%
%\section{Looping and counting}
%\label{loopcount}
%\index{traverse!counting}
%\index{loop!counting}
%The following program counts the
%number of times the letter {\tt 'a'} appears in a string:
%\begin{verbatim}
% char fruit[] = "banana";
% int length = strlen(fruit);
% int count = 0;
% int index = 0;
% while (index < length)
% {
% if (fruit[index] == 'a')
% {
% count ++;
% }
% index++;
% }
% printf ("%i\n", count);
%\end{verbatim}
%%
%This program demonstrates a common idiom, called a {\bf counter}. The
%variable {\tt count} is initialized to zero and then incremented each
%time we find an {\tt 'a'}. (To {\bf increment} is to increase by one;
%it is the opposite of {\bf decrement}.) When we exit the loop, {\tt count}
%contains the result: the total number of a's.
%\index{counter}
%As an exercise, encapsulate this code in a function named
%{\tt CountLetters()}, and generalize it so that it accepts the
%string and the letter as arguments.
%% müssen wir die Länge vorher ermitteln und übergeben?
%\index{encapsulation}
%\index{generalization}
%As a second exercise, rewrite this function so that instead
%of traversing the string, it uses the version of
%{\tt find} we wrote in the previous section.
%\section{The {\tt strchr} function}
%\index{find}
%
% The {\tt strchr} function is like the opposite of the
%{\tt []} operator. Instead of taking an index and extracting the
%character at that index, {\tt strchr} takes a character and finds the
%index where that character appears.
%\begin{verbatim}
% char fruit[] = "banana";
% int index = strchar(fruit,'a'));
%\end{verbatim}
%%
%This example finds the index of the letter {\tt 'a'} in the string.
%In this case, the letter appears three times, so it is not obvious
%what {\tt find} should do. According to the documentation, it returns
%the index of the {\em first} appearance, so the result is 1. If the
%given letter does not appear in the string, {\tt find} returns -1.
%In addition, there is a
%version of {\tt find} that takes another {\tt string} as
%an argument and that finds the index where the substring
%appears in the string. For example,
%\begin{verbatim}
% apstring fruit = "banana";
% int index = fruit.find("nan");
%\end{verbatim}
%%
%This example returns the value 2.
%%
%\pagebreak[4]
\section{Verkettung von Strings}
Im Abschnitt~\ref{Finding a character in a string} können wir sehen, wie
man eine Suchfunktion implementiert die ein {\tt character} in einem {\tt string}
findet.
Eine nützliche Operation für Strings ist deren {\bf Verkettung}.
Darunter verstehen wir die Verknüpfung des Endes eines
Strings mit dem Anfang des nächsten Strings. So wird zum Beispiel aus: {\tt auf}
und {\tt passen} wird {\tt aufpassen}.
\index{<string.h>}
\index{Bibliothek!string.h}
\index{String!Verkettung}
Glücklicherweise müssen wir nicht alle diese nützlichen Funktion selbst schreiben.
Die {\tt string.h} Bibliothek stellt verschiedene Funktionen bereit die wir für die
Bearbeitung von Strings nutzen können.
Um zwei Strings miteinander zu verketten können wir die {\tt strncat()} Funktion benutzen:
\begin{verbatim}
char fruit[20] = "banana";
char bakedGood[] = " nut bread";
strncat(fruit, bakedGood, 10);
printf ("%s\n", fruit);
\end{verbatim}
%
Diese Programmzeilen erzeugen die Ausgabe: {\tt banana nut bread}.
Wenn wir Bibliotheksfunktionen benutzen ist es sehr wichtig, dass wir
alle notwendigen Argumente der Funktion kennen und ein Grundverständnis
der Arbeitsweise der Funktion besitzen.
% are using library functions it is important to completely understand
%all the necessary arguments and to have a complete understanding
%of the working of the function.
Man könnte jetzt annehmen, dass die {\tt strncat()} Funktion zwei Strings
nimmt, sie zusammenfügt und einen neuen, zusammengesetzten String
erzeugt. So ist es aber nicht. Die Funktion kopiert nämlich den Inhalt des zweiten
Strings an das Ende des ersten Strings.
Dieser kleine Unterschied hat für uns als Programmierer wichtige
Konsequenzen. Wir selbst müssen dafür sorgen, dass die erste Stringvariable auch
lang genug ist um auch den zweiten String mit aufzunehmen.
Aus diesem Grund habe ich die erste Stringvariable {\tt fruit} als Array von 20 Zeichen definiert
{\tt char fruit[20]}. Dieses Array bietet Platz für 19 Zeichen + 1 Begrenzungszeichen.
Das dritte Argument von {\tt strncat()} gibt an, wie viele Zeichen aus dem
zweiten in den ersten String kopiert werden. Dabei ist darauf zu achten, dass nur
so viele Zeichen in das erste Array kopiert werden wie dort hineinpassen. Weiterhin muss noch
Platz für das Begrenzungszeichen bleiben. Werden mehr Zeichen kopiert können Speicherbereiche
überschrieben werden.
%It is also possible to concatenate a character onto the
%beginning or end of an {\tt string}. In the following example, we
%will use concatenation and character arithmetic to output
%an abecedarian series.
%``Abecedarian'' refers to a series or list in which the elements
%appear in alphabetical order. For example, in Robert McCloskey's book
%{\em Make Way for Ducklings}, the names of the ducklings are Jack,
%Kack, Lack, Mack, Nack, Ouack, Pack and Quack. Here is a loop that
%outputs these names in order:
%\begin{verbatim}
% char name[5];
% char suffix[] = "ack";
% char letter = 'J';
% name[0] = letter;
% name[1] = '\0';
%
% while (letter <= 'Q')
% {
% /* Wrong, does not work, string gets longer and longer...*/
% printf("%s\n", strncat (name, suffix, 3));
% letter++;
% name[0] = letter;
% }
%\end{verbatim}
%%
%The output of this program is:
%\begin{verbatim}
%Jack
%Kack
%Lack
%Mack
%Nack
%Oack
%Pack
%Qack
%\end{verbatim}
%%
%Of course, that's not quite right because I've misspelled ``Ouack''
%and ``Quack.'' As an exercise, modify the program to correct
%this error.
%Again, be careful to use string concatenation only with {\tt apstring}s
%and not with native C strings. Unfortunately, an expression like
%{\tt letter + "ack"} is syntactically legal in C++, although it
%produces a very strange result, at least in my development environment.
%%
%\section{{\tt string}s are mutable}
%\index{immutable}
%\index{string}
%You can change the letters in an {\tt string} one at a time
%using the {\tt []} operator on the left side of an assignment.
%For example,
%\begin{verbatim}
% char greeting[] = "Hello, world!";
% greeting[0] = 'J';
% printf ("%s", greeting);
%\end{verbatim}
%
%produces the output {\tt Jello, world!}.
\section{Zuweisung von neuen Werten an Stringvariablen}
\label{Zuweisung von neuen Werten an Stringvariablen}
\index{Zuweisung!String}
\index{String}
Bisher haben wir gesehen, wie eine Stringvariable während
der Deklaration initialisiert wird.
Da es sich bei Strings im Grunde um ein mehr oder weniger
normales Array handelt gilt auch hier, dass wir nicht den
Zuweisungsoperator benutzen können um dem String einen
neuen Wert zuzuweisen.
%As with arrays in general, it is not
%legal to assign values directly to strings, because it is
%not possible to assign a value to an entire array.
\begin{verbatim}
fruit = "apple"; /* Wrong: Direkte Zuweisung nicht möglich! */
\end{verbatim}
Wir können allerdings die {\tt strncpy()} Funktion benutzen um
dem String einen neuen Wert zuzuweisen:
\begin{verbatim}
char greeting[15];
strncpy (greeting, "Hello, world!", 13);
\end{verbatim}
{\tt strncpy()} kopiert 13 Zeichen aus dem zweiten Argument-String
in den ersten Argument-String und somit haben wir der Stringvariable
einen neuen Wert zugewiesen.
Allerdings müssen wir dabei wieder einige Einschränkungen beachten.
Die {\tt strncpy()} Funktion kopiert genau 13 Zeichen aus dem
zweiten String in den ersten String. Was passiert dabei aber mit dem
Begrenzungszeichen {\tt '\textbackslash 0'}?
%\pagebreak[4]
Die Funktion setzt das Begrenzungszeichen \textbf{nicht} automatisch.
Wir könnten unsere Kopieranweisung so ändern, dass 14 statt 13 Zeichen
kopiert werden. In diesem Fall wird das unsichtbare Begrenzungszeichen
einfach mitkopiert und unser String {\tt greeting} wäre wieder korrekt:
\begin{verbatim}
strncpy (greeting, "Hello, world!", 14);
\end{verbatim}
oder wir benutzen die {\tt strlen()} Funktion um {\tt strlen() + 1} Zeichen zu kopieren:
\begin{verbatim}
strncpy (greeting, "Hello, world!", strlen("Hello, world!")+1);
\end{verbatim}
Wenn wir allerdings nur einen Teil des zweiten Strings in den ersten
String kopieren wollen, so müssen wir selbst dafür sorgen, dass das
Zeichen n+1 im String {\tt greeting[15]}
hinterher auf den Wert {\tt \textbackslash 0} gesetzt wird:
\begin{verbatim}
strncpy (greeting, "Hello, world!", 5); /*kopiert nur Hello */
greeting[5] = '\0'; /*das 6. Zeichen hat den Index 5*/
\end{verbatim}
\vskip 1.5em
{\bf Achtung!} In den letzten beiden Abschnitten haben wir die
{\tt strncpy()} und die {\tt strncat()} Funktion benutzt, bei der wir
immer genau angeben müssen, wie viele Zeichen aus dem zweiten
in den ersten String kopiert werden oder an diesen angehangen werden.\hint
Die {\tt string.h} Bibliothek stellt uns noch weitere Funktionen zur Verfügung
die so ähnlich arbeiten. Es gibt zum Beispiel auch noch die {\tt strcpy()} und
die {\tt strcat()} Funktion. Bei diesen beiden Funktionen wird die Anzahl der
zu kopierenden Zeichen nicht mit angegeben. Die Funktionen
kopieren so lange Zeichen aus dem zweiten in den ersten String, bis in
dem zweiten String ein Begrenzungszeichen gefunden wird.
Von der Benutzung dieser Funktionen wird dringend abgeraten!
Die Benutzung dieser Funktionen hat zu einer großen Anzahl von
Sicherheitsproblemen in C Programmen geführt. C überprüft
keine Array-Grenzen und wenn in dem zweiten String das Begrenzungszeichen
zum Beispiel wegen einer fehlerhaften Benutzereingabe fehlt, so kann es leicht vorkommen,
dass Zeichen über den Speicherbereich der ersten Stringvariable
hinaus in den Speicher geschrieben werden und dadurch andere Daten überschrieben
werden.
%check array boundaries and will continue copying characters
%into computer memory even past the length of the variable.
%%
\section{Strings sind nicht direkt vergleichbar}
\label{incomparable}
\index{String!Vergleichen}
\index{String}
Unsere Vergleichsoperatoren die wir für den Vergleich von {\tt int}s und
{\tt double}s benutzt haben können wir nicht auf Strings anwenden. So könnten
wir auf die Idee kommen die folgenden Programmzeilen zu schreiben um
zwei Strings miteinander zu vergleichen:
\begin{verbatim}
if (word == "banana") /* Wrong! */
\end{verbatim}
Dieser Test schlägt leider jedes Mal fehl.
%
Wir können aber die {\tt strcmp()} Funktion benutzen um zwei Strings
miteinander zu vergleichen. Die Funktion hat einen Rückgabewert von {\tt 0}
wenn die zwei Strings identisch sind, einen negativen Wert, wenn der
erste String 'alphabetisch kleiner' ist als der zweite
(wenn er in einem Wörterbuch vor dem anderen String gelistet würde) oder einen
positiven Wert, wenn der zweite String 'größer' ist.
Bitte beachten Sie, dass der Rückgabewert nicht der normalen Interpretation der
Wahrheitswerte normaler Vergleichsoperatoren entspricht, wobei der Rückgabewert
{\tt 0} als 'Falsch' interpretiert wird. \hint
Mit der {\tt strcmp()} Funktion können wir sehr einfach beliebige Wörter
in ihre alphabetische Reihenfolge bringen:
\begin{verbatim}
if (strcmp(word, "banana") < 0)
{
printf( "Your word, %s, comes before banana.\n", word);
}
else if (strcmp(word, "banana") > 0)
{
printf( "Your word, %s, comes after banana.\n", word);
}
else
{
printf ("Yes, we have no bananas!\n");
}
\end{verbatim}
%
Dabei müssen wir aber wieder aufpassen. Die {\tt strcmp()} Funktion
behandelt Großbuchstaben und Kleinbuchstaben anders als
wir das normalerweise gewöhnt sind.
Alle Großbuchstaben kommen bei einem Vergleich vor den
Kleinbuchstaben. Der Grund dafür liegt in der Art der verwendenden
Zeichenkodierung (siehe Anhang:~\nameref{ASCII-Table}).
Dort haben Großbuchstaben einen kleineren Wert als Kleinbuchstaben
und produzieren das folgende Ergebnis:
\begin{verbatim}
Your word, Zebra, comes before banana.
\end{verbatim}
%
Ein oft genutzter Ausweg aus diesem Dilemma besteht darin
Strings in einer einheitlichen Formatierung zu vergleichen.
Zuerst werden alle Zeichen der Strings zu Klein- oder
Großbuchstaben gewandelt und erst danach wird
der Vergleich durchgeführt. Der nächste Abschnitt zeigt wie
das gemacht wird.
%%
\section{Klassifizierung von Zeichen}
Es ist of nützlich den Wert, der in einer \texttt{char} -Variable gespeichert ist,
im Programm untersuchen zu können und zu entscheiden, ob es
sich dabei um einen Groß- oder Kleinbuchstaben, oder
um eine Ziffer oder ein Sonderzeichen handelt.
C stellt auch dafür wieder eine Bibliothek von unterschiedlichen
Funktionen bereit, welche eine solche Klassifizierung von Zeichen
ermöglichen. Um diese Bibliotheksfunktionen in unserem Programm nutzen
zu können, müssen wir die Header-Datei {\tt ctype.h} in unser Programm
aufnehmen.
\index{<ctype.h>}
\index{Header-Datei!ctype.h}
\index{Bibliothek!ctype.h}
\index{Zeichen!Klassifizierung}
\index{Zeichen!Klassifizierung!islower()}
\index{Zeichen!Klassifizierung!isupper()}
\index{Zeichen!Klassifizierung!isspace()}
\index{Zeichen!Klassifizierung!isalpha()}
\index{Zeichen!Klassifizierung!isdigit()}
\begin{verbatim}
char letter = 'a';
if (isalpha(letter))
{
printf("The character %c is a letter.", letter);
}
\end{verbatim}
%
Der Rückgabewert der {\tt isalpha()} Funktion ist ein \texttt{int}
und besitzt den Wert \texttt{0} wenn das Funktionsargument kein
Buchstabe ist und einen von Null verschiedenen Wert, wenn
die Funktion mit einem Buchstaben aufgerufen wurde.
Wir können daher die Funktion direkt als Bedingung der \texttt{if} -Anweisung
einsetzen, wie in unserem Beispiel zu sehen.
Der Rückgabewert \texttt{0} wird als {\tt false} interpretiert und lässt die Bedingung
fehlschlagen. Alle anderen Rückgabewerte werden als {\tt true} interpretiert.
Weitere Funktionen zur Klassifizierung von Zeichen sind {\tt isdigit()}, welche
dazu dient die Ziffern 0 bis 9 zu identifizieren, {\tt isspace()} identifiziert
nicht druckbare Zeichen wie zum Beispiel Leerzeichen, Tabulatoren, Zeilenumbrüche.
Es gibt außerdem die {\tt isupper()} und {\tt islower()} Funktion, welche
zwischen Buchstaben in Groß- und Kleinschreibung unterscheidet.
\index{Zeichen!Umwandlung}
\index{Zeichen!Umwandlung!toupper}
\index{Zeichen!Umwandlung!tolower}
Schließlich gibt es auch zwei Funktionen, welche sich zur Umwandlung zwischen
Groß- und Kleinschreibung nutzen lassen. Sie heißen {\tt toupper()} und {\tt tolower()}.
Beide Funktionen erwarten ein einzelnes Zeichen als Argument
und geben ein (möglicherweise umgewandeltes) Zeichen zurück.
\begin{verbatim}
char letter = 'a';
letter = toupper (letter);
printf("%c\n", letter);
\end{verbatim}
%
Die Ausgabe dieser Programmzeilen ist {\tt A}.
\begin{description}
\item[Aufgabe:] Benutzen Sie die Funktionen der \texttt{ctype.h}
Bibliothek um zwei Funktionen mit den Namen {\tt StringToUpper()} und
{\tt StringToLower()} zu schreiben. Beide Funktionen sollen einen
einzelnen Parameter vom Typ String besitzen und diesen
String so modifizieren, dass im Anschluss alle Zeichen des Strings
Groß- oder Kleinbuchstaben sind. Zeichen die keine Buchstaben sind,
sollen nicht verändert werden. Der Rückgabewert der Funktionen soll
{\tt void} sein.
\end{description}
%%%
%\section{Other {\tt string} functions}
%This chapter does not cover all the {\tt apstring} functions.
%Two additional ones, {\tt c\_str} and {\tt substr}, are covered
%in Section~\ref{finput} and Section~\ref{parsing}.
\section{Benutzereingaben im Programm}
\label{input}
\index{Benutzereingabe}
\index{Input!Keyboard}
Die meisten Programme die wir bisher geschrieben haben, verhalten
sich sehr berechenbar. Sie führen bei jedem Programmstart die selben Anweisungen aus.
Das liegt unter anderem daran, dass wir es bisher vermieden