Abend,
Definiert ist in einer C Code Zeile folgendes:
1
char*Personenname="Meyer";
Sicher ganz einfach für die C Gemeinde, aber entschuldigt bitte, meine
Kenntnis dazu ist dürftig, darum frage ich mal:
Was ist das für eine Variable?
Ein Pointer, ein String, eine Constante?
Und wie kann man den später ändern?
Mit strcpy("Huber", *Personenname); klappt es nicht.
Welches Format kann man später anwenden für printf?
%s oder %c
fragend schrieb:> Was ist das für eine Variable?
ein Zeiger auf eine Konstante
> Und wie kann man den später ändern?
gar nicht.
(Man kann den Zeiger ändern, aber nicht die Kontante.)
fragend schrieb:> Was ist das für eine Variable?
Das ist ein Pointer auf einen konstante Zeichenkette.
fragend schrieb:> Welches Format kann man später anwenden für printf?> %s oder %c
%s weil es ein C-String ist.
fragend schrieb:> Mit strcpy("Huber", *Personenname); klappt es nicht.
Der * muss weg, dann compiliert es, aber beim ausführen gibt es einen
Speicherzugriffsfehler.
Peter II schrieb:> fragend schrieb:>> Was ist das für eine Variable?> ein Zeiger auf eine Konstante
Für mich ist das einfach nur ein Zeiger auf eine Zeichenkette mit 5+1
Zeichen (Meyer + \0)
Da kein const vorangestellt ist lässt sich die Zeichenkette auch ohne
Weiteres nachträglich noch ändern solange sie nicht länger als 5+1
Zeichen ist.
Mit const würde der Compiler bei einem direktem Schreibzugriff meckern.
Kaj schrieb:> fragend schrieb:>> Mit strcpy("Huber", *Personenname); klappt es nicht.> Der * muss weg, dann compiliert es, aber beim ausführen gibt es einen> Speicherzugriffsfehler.
Genau so ist es. Daher habe ich hier gefragt.
Mein Wissen endet da.
Peter II schrieb:> fragend schrieb:>> Was ist das für eine Variable?> ein Zeiger auf eine Konstante
Ok, daher der Speicherzugriffsfehler.
>>> Und wie kann man den später ändern?> gar nicht.> (Man kann den Zeiger ändern, aber nicht die Kontante.)
Kann man der Pointer Constanten ( ist das der richtige Ausdruck ? )
einen anderen Pointer unterjubeln, die auch eine Pointer Constante wäre,
z.B.
1
char*Person2="Huber";
Und der Pointer Constanten *Personenname dann die Addresse von *Person2
zuweisen? Und wie würde das aussehen?
Danke!
Timmo H. schrieb:> Da kein const vorangestellt ist lässt sich die Zeichenkette auch ohne> Weiteres nachträglich noch ändern solange sie nicht länger als 5+1> Zeichen ist.
Da irrst Du. Der Text ist eine Stringkonstante, und die ist
automatisch "const".
Rufus Τ. F. schrieb:> Da irrst Du. Der Text ist eine Stringkonstante, und die ist> automatisch "const".
Das hört sich nach Norm an, kann man das irgendwo nachlesen?
Wie kann man das Wissen "nachlernen"
Rufus Τ. F. schrieb:> Timmo H. schrieb:>> Da kein const vorangestellt ist lässt sich die Zeichenkette auch ohne>> Weiteres nachträglich noch ändern solange sie nicht länger als 5+1>> Zeichen ist.>> Da irrst Du. Der Text ist eine Stringkonstante, und die ist> automatisch "const".
Wenn man wirklich einen veränderbaren String haben möchte muss man es
explizit als Array deklarieren:
1
charPersonenname[]="Meyer";
Das ursprüngliche Beispiel ist auf jeden Fall const und compliert in C++
auch gar nicht ohne const. Wenn man es doch versucht zu ändern kann
alles mögliche passieren, daher sollte man sich angewöhnen immer mit
const zu schreiben:
Sebastian V. schrieb:> Das ursprüngliche Beispiel ist auf jeden Fall const und compliert in C++> auch gar nicht ohne const.
klar macht es da, es kommt nur eine Warnung.
Danke an alle.
Hier meine Lösung , das Kapitel Pointer und Addressen hatte
weitergeholfen - in den doppelt verketteten Listen
Denke jetzt ist mir das besser klar.
Bekomme ich ein Lob?
Oder was passt nicht so?
Sebastian V. schrieb:> Rufus Τ. F. schrieb:>> Timmo H. schrieb:>>> Da kein const vorangestellt ist lässt sich die Zeichenkette auch ohne>>> Weiteres nachträglich noch ändern solange sie nicht länger als 5+1>>> Zeichen ist.>>>> Da irrst Du. Der Text ist eine Stringkonstante, und die ist>> automatisch "const".>> Wenn man wirklich einen veränderbaren String haben möchte muss man es> explizit als Array deklarieren:>
1
charPersonenname[]="Meyer";
> Das ursprüngliche Beispiel ist auf jeden Fall const und compliert in C++> auch gar nicht ohne const. Wenn man es doch versucht zu ändern kann> alles mögliche passieren, daher sollte man sich angewöhnen immer mit> const zu schreiben:>
1
constchar*Personenname="Meyer";
Tatsache... mein gcc schmeisst mit -Wall zwar kein Fehler aber das
Programm schmiert ab
Timmo H. schrieb:> Tatsache... mein gcc schmeisst mit -Wall zwar kein Fehler aber das> Programm schmiert ab
liegt aber mehr oder weniger an deinem System.
Der Absturz kommt, weil der String in einer Read-Only-Page im Speicher
liegt. Bei Systemen die so etwas nicht haben, würde es funktionieren.
Peter II schrieb:> Der Absturz kommt, weil der String in einer Read-Only-Page im Speicher> liegt. Bei Systemen die so etwas nicht haben, würde es funktionieren.
Wenn ich jetzt meine zweite "Stringkonstante" als normalen String
definiere, dann sollte es funktionieren, wenn ich den Value der normalen
String Variable über die Addresse der Stringkonstante zuweise, aber ein
Speicherzugrifffehler auftauchen, wenn ich es in andere Richtung
versuche zu ändern`?
Die beiden Stringkonstanten scheinen sich aber ändern zu lassen, obwohl
im Read Only.. ah! Ich verstehe! Da wird nicht der Value geändert,
sondern nur die Addressen, deshalb läuft es.
Jetzt muss ich doch mal versuchen den Value zu ändern wie ich im ersten
Absatz geschrieben habe, wird das Programm "abschmieren"?
Timmo H. schrieb:> Tatsache... mein gcc schmeisst mit -Wall zwar kein Fehler aber das> Programm schmiert abPeter II schrieb:> liegt aber mehr oder weniger an deinem System.>> Der Absturz kommt, weil der String in einer Read-Only-Page im Speicher> liegt. Bei Systemen die so etwas nicht haben, würde es funktionieren.
Mit diesem Werkzeug kann man leicht feststellen, ob und wo dieses
Programm laufen würde:
http://www.gesellschaftsspiele.de/wp-content/uploads/knobelbecher-w%C3%BCrfelbecher-261x300.jpg
Mir läuft es kalt den Rücken herunter, wenn ich mir vorstelle, daß
eventuell das Funktionieren von Steuergeräten in Fahrzeugen von so einem
wacvkligen Zeug abhängt.
SCNR
Paul
Paul B. schrieb:> Mir läuft es kalt den Rücken herunter, wenn ich mir vorstelle, daß> eventuell das Funktionieren von Steuergeräten in Fahrzeugen von so einem> wacvkligen Zeug abhängt.
Darum wird beim Militär und Luftfahrt auch oftmals nach wie vor mit Ada
programmiert.
Dennoch wundert es mich ein wenig warum in dem Beispiel keine Warnung
kommt. Der Compiler/Linker weiß doch eigentlich mit welcher Art von
Speicher er arbeitet. Ich habe es scheinbar bisher unterbewusst richtig
gemacht, aber jetzt wo ich es "extra" falsch mache, kommt keine Warnung
aber ein Absturz... etwas beunruhigend ist das schon.
Meine Schüchternheit überwunden das zu fragen:
Bitte nicht erschlagen-
Wieso ist es möglich, eine Konstante ( auf diesem Weg ) doch zu ändern?
Entweder ist etwas konstant oder nicht?!
Gibt es eine Programmiersprache, die das verhindert oder darauf
aufpasst?
Wie heissen solche?
Gibt es Mechanismen ( Compiler Aufruf ) die darauf abgestimmt sind und
eine Warnung oder einen Fehler melden?
Nachdem ich die Fragen geschrieben habe, frage ich mich gerade, wie man
sowas trennen will, wenn man einen Ringbuffer programmiert, ist ja fast
das gleiche, da werden ja auch nur Lese und Send Buffer getauscht anhand
den Addressen.
Mich hat die Zeile irretiert, der mich zu dem Posting veranlasst hat
1
char*Personenname="Meyer";
Kein Anzeichen von einem Array,
keine Stringlänge,
und dann noch ein Sternchen ( Pointer )
Jetzt weiss ich, dass es sich um eine konstante Zuweisung handelt und es
ein Pointer auf das erste Zeichen ist. Das war mir zuvor nicht klar.
Vieleicht hätte man das wissen müssen in diesem Kapitel, aber ich habe
ein paar überflogen; ich werde noch mal zurückblättern.
Danke allen! Habt mir sehr geholfen.
fragend schrieb:> Nachdem ich die Fragen geschrieben habe, frage ich mich gerade, wie man> sowas trennen will, wenn man einen Ringbuffer programmiert, ist ja fast> das gleiche, da werden ja auch nur Lese und Send Buffer getauscht anhand> den Addressen.
Nein - nachdem ich das online jetzt lese stelle ich fest, dass mein Satz
völliger Quatsch ist: das sind ja keine Konstanten Variablen.
Ok. Ich mach Schluss für heute, war bischen viel für den Kapitel Anfang
gute Nacht!
Der entscheidende Unterschied zwischen char* x ="test" und char
x[]="test" ist, dass char* einen Zeiger beschreibt, der auf die Adresse
des c-strings gesetzt wird. char*x="test" ist fast das selbe wie
char*x=(const char[]){'t','e','s','t',0}; Erst wird irgendwo ein const
char array erstellt, dann dessen Adresse zugewiesen. Daran sieht man
auch, warum man das nicht machen sollte, weil const char* nunmal nicht
char* ist. Andererseits ist char x[]="test" eher wie char
x[]={'t','e','s','t',0}; Hier wird ein neues Array vom typ char[]
erstellt, und nicht const char. Das Stringliteral fungiert quasi
initialiser list. Deshalb kann man dort die Elemente verändern.
Auch interresannt ist die Adressierung der generierten Symbole. char x[]
generiert ein Symbol x, das das die Adresse des ersten Zeichens des
Arrays repräsentiert. Aber char* x generiert ein Symbol x, welches die
Adresse repräsentiert, an welchem die Adresse worauf x zeigt gespeichert
ist, in dem fall die Adresse des String literals.
Wen man mit char x[] irgendwas macht, wie z.B. damit Rechnen oder einer
Funktion übergeben, nimmt der Compiler die Adresse des Symbols. Die
Grösseninformation geht dabei verloren. Deshalb hat hier x und &x die
selbe adresse, wenn man diese ausgibt. Im ersten fall nimmt der Compiler
die Adresse des Symbols, im zweiten nimmt man selbst die Adresse des
Symbols, deshalb das selbe. Der typ von &x ist dann übrigens char(*x)[].
Hätte man jedoch char* x, wäre x bei der Ausgabe der wert von x, welcher
die Adresse des ersten Elements des c-strings ist. Die ausgabe von &x
ist dann nicht das selbe, weil diese die Adresse von x ist, welches
wiederum die Adresse ist an der wert von x, also die Adresse des
Strings, steht.
fragend schrieb:> Wieso ist es möglich, eine Konstante ( auf diesem Weg ) doch zu ändern?
weil es 2 teile sind.
Es ist eine Variabel die nicht Konstant ist die aber auf eine Konstante
Variabel zeigt. Wenn es wirklich konstant sein soll muss man schreiben.
const char* const text = "ich bin wirklich konstant";
Na, ich setzt mich dann mal voll ins Fettnäpfchen... Also los!
fragend schrieb:> Wieso ist es möglich, eine Konstante ( auf diesem Weg ) doch zu ändern?
Kannst du ja nicht, denn beim Versuch diese zu ändern gibt es einen
Segmentation fault (SIGSEGV).
Der Inhalt, auf den der Pointer zeigt, wurde nicht verändert.
1
1│#include<stdio.h>
2
2│#include<string.h>
3
3│
4
4│
5
5│intmain(void)
6
6│{
7
7│char*name="Meyer";
8
8│
9
9├>strcpy(name,"Huber");
10
10│return0;
11
11│}
1
Reading symbols from main...done.
2
(gdb) break 9
3
Breakpoint 1 at 0x4004b2: file pointer.c, line 9.
4
5
(gdb) run
6
Starting program: main
7
Breakpoint 1, main () at pointer.c:9
8
9
(gdb) print name
10
$1 = 0x400554 "Meyer"
11
12
(gdb) step
13
14
Program received signal SIGSEGV, Segmentation fault.
15
0x00000000004004b6 in main () at pointer.c:9
16
17
(gdb) print name
18
$2 = 0x400554 "Meyer"
19
20
(gdb) step
21
22
Program terminated with signal SIGSEGV, Segmentation fault.
23
The program no longer exists.
Der Speicher ist unverändert.
Dann das ganze nochmal mit Valgrind:
1
valgrind --leak-check=yes ./main
2
...
3
Process terminating with default action of signal 11 (SIGSEGV): dumping core
4
Bad permissions for mapped region at address 0x400554 at 0x4004B6: main (pointer.c:9)
Aha! Wenn man also mehrere Tools nutzt, steigt die Chance Fehler zu
finden. Man müsste diese Tools nur nutzen wollen...
fragend schrieb:> Gibt es eine Programmiersprache, die das verhindert oder darauf> aufpasst?> Wie heissen solche?
C#, Java, Python, usw.
Und auch da kann man genug anderen mist machen, der nicht durch einen
absturz auffällt.
Sowas kostet aber Performance (im sinne von Laufzeit) und genau darum
geht es bei C: Performance.
Man kann in jeder Sprache schlechten und fehlerhaften Code Schreiben,
egal wie viele und welche Schutzmechanismen eine Sprache bietet.
fragend schrieb:> Gibt es Mechanismen ( Compiler Aufruf ) die darauf abgestimmt sind und> eine Warnung oder einen Fehler melden?
Ja, z.B. wenn man das ganze als C++ compiliert:
1
Warnung: ISO C++ verbietet, eine Zeichenkettenkonstante in »char*« zu konvertieren [-Wwrite-strings]
Man muss sich halt mit dem Werkzeug, was man gedenkt zu nutzen,
beschäftigen.
Oder anders gesagt:
Man kann sich auch mit einer Bastelschere für 3 jährige die Augen
ausstechen, wenn man nicht weiß, was man da macht.
Und ich glaube es gibt kaum eine andere Sprache für die es mittlerweile
soviele Bücher, Tools (cppcheck, valgrind, etc.) und Papers, mit
Hinweisen auf genau solche Fallstricke, gibt, wie für C.
Peter II schrieb:> Wenn es wirklich konstant sein soll muss man schreiben.
1
intmain(void)
2
{
3
constcharconsttext[]="Ich bin wirklich konstant";
4
5
printf("%s\n",text);
6
7
char*p=(char*)&text[0];
8
strcpy(p,"Nein, bist du nicht!");
9
10
printf("%s\n",text);
11
12
return0;
13
}
Mit voller Absicht von hinten durch die Brust ins Auge.
Soweit ich weiß, gibt es in C kein echtes const.
Lass ich den Umweg über den extra Pointer weg, gibt es aber auch nur ne
Warnung beim Compilieren:
1
Warnung: Übergabe des Arguments 1 von »strcpy« entfernt Kennzeichner »const« von Zeiger-Ziel-Typ [-Wdiscarded-qualifiers]
2
strcpy(text, "Nein, bist du nicht!");
3
^~~~
4
In file included from pointer.c:2:0:
5
/usr/include/string.h:125:14: Anmerkung: »char * restrict« erwartet, aber Argument hat Typ »const char * const«
Beim Ausführen gibt es natürlich wieder einen SIGSEGV.
Peter II schrieb:> Wenn es wirklich konstant sein soll muss man schreiben.>> const char* const text = "ich bin wirklich konstant";
Hat den selben Effekt, als wenn man const weglassen würde, da es eine
Stringkonstante ist.
fragend schrieb:> Wieso ist es möglich, eine Konstante ( auf diesem Weg ) doch zu ändern?>> Entweder ist etwas konstant oder nicht?!
Oder beides ;-)
Aus historischen Gründen widerspricht sich C in diesem Punkt selber:
Grundsätzlich dürfen In C Stringliterale nicht überschrieben werden
(sonst führt das laut ISO-Norm zu "undefined behavior"). Deswegen
müssten sie konsequenterweise vom Typ
1
constchar*
sein. Folglich müsste eine Zeigervariable, die darauf zeigen soll,
ebenfalls mit const deklariert werden:
1
constchar*s="hallo";
Ließe man das const weg, würde der Compiler meckern.
Bis dahin war alles im Konjunktiv geschrieben, denn die Realität sieht
ganz anders aus:
In frühen Versionen von C gab es das const aber noch nicht, weswegen es
viele ältere (aber auch neuere) C-Programme gibt, in denen Zeiger auf
Stringliterale ohne const deklariert sind:
1
char*s="hallo";
Diese Deklaration wird selbst dann verwendet, wenn der Programmierer gar
nicht beabsichtigt, das Stringliteral zu überschreiben.
Damit diese Software auch von aktuellen Compilern akzeptiert wird, hat
das C-Normungskomitee beschlossen, dass Stringliterale formal vom Typ
1
char*
(ohne const) sein sollen, auch wenn dies eigentlich unlogisch ist und
dazu führt, dass der folgender fehlerhafte Code vom Compiler akzeptiert
wird:
1
char*s="hallo";
2
s[1]='e';// formal korrekt, aber trotzdem fehlerhaft
Es obliegt also letzendlich dem Programmierer, aufzupassen, dass
Stringliterale nicht überschrieben werden.
Das ist einer der dunklen Punkte von C, der leider für viel Verwirrung
sorgt, wenn man das erste Mal darüberstolpert.
fragend schrieb:> Gibt es Mechanismen ( Compiler Aufruf ) die darauf abgestimmt sind und> eine Warnung oder einen Fehler melden?
Für Programmierer, die bereit sind, ihren Quellcode überall an den
erforderlichen Stellen mit consts zu spicken, gibt es beim GCC die
Option -Wwrite-strings. Damit werden die Stringliterale const, und der
Compiler kann Versuche, ein Stringliteral zu überschreiben, erkennen. Er
verhält sich dann diesbezüglich wie in C++, wo Stringliterale ebenfalls
const sind.
Paul B. schrieb:> Mir läuft es kalt den Rücken herunter, wenn ich mir vorstelle, daß> eventuell das Funktionieren von Steuergeräten in Fahrzeugen von so einem> wacvkligen Zeug abhängt.
Mir läufts noch kälter den Rücken runter, wenn ich dran denke, dass
kritische Industrieanlagen in Step7/AWL programmiert werden.
Nase schrieb:> Mir läufts noch kälter den Rücken runter, wenn ich dran denke, dass> kritische Industrieanlagen in Step7/AWL programmiert werden.
Naja -da gibt es ja noch andere Möglichkeiten des Programmierens, wenn
man
mit der Anweisungsliste nicht zurecht kommt. FUP und KOP gehen auch.
Da ist es auch keine Glückssache, wie etwas von der Steuerung ausgelegt
wird. Man zeichnet sich in der Not einen Schaltplan, so wie man ihn
kennt, mit Relais, deren Kontakten usw. und "spielt" ihn im Kopf durch.
Dann teilt man das Ganze in kleinere Einheiten (wenn es zu umfangreich
ist), dreht den Schaltplan um 90 Grad und schon kann man seine
Anweissungsliste rein wämmern. Das ist nicht so ein schwammiges Zeug wie
das hier, wo es von Rechner, Kompiler und Tagesform abhängt, OB und WIE
ein Programm läuft.
MfG Paul
Peter II schrieb:> Timmo H. schrieb:>> Tatsache... mein gcc schmeisst mit -Wall zwar kein Fehler aber das>> Programm schmiert ab>> liegt aber mehr oder weniger an deinem System.>> Der Absturz kommt, weil der String in einer Read-Only-Page im Speicher> liegt. Bei Systemen die so etwas nicht haben, würde es funktionieren.
Man kann auch auf Systemen mit Speicherschutz dafür sorgen, dass das
Stringliteral in einem beschreibbaren Bereich abgelegt wird und damit
den Segfault umgehen. Dann passieren aber evtl. andere seltsame Dinge.
Beispiel (mit GCC 6.2.1 unter Linux):
1
#include<stdio.h>
2
#include<string.h>
3
4
voidtest1(void){
5
char*text="LANGSAM AUF DEN AUSSICHTSTURM";
6
7
printf("Test 1: Versuch, ein Stringliteral zu überschreiben:\n\n");
8
9
printf("Vorher: %s\n",text);
10
strcpy(text,"HINUNTER ABER GANZ GESCHWIND");
11
printf("Nachher: %s\n",text);
12
13
printf("\nJuhu, es funktioniert tatsächlich!\n\n\n");
14
}
15
16
voidtest2(void){
17
constchar*sturm="STURM";
18
19
printf("Test 2: Mehrere Versuche, mal ordentlich STURM zu machen:\n\n");
20
21
printf("Versuch 1: ");
22
printf("%s\n",sturm);
23
printf("Nein, der ist viel zu schwach.\n");
24
printf("Wahrscheinlich wird der Sturm vom langsamen printf ausgebremst.\n\n");
25
26
printf("Versuch 2: ");
27
puts(sturm);
28
printf("Hmm, wieder nichts.\n");
29
printf("Die Leckströme der Speicherzellen der Variable 'sturm' scheinen\n");
30
printf("zu hoch zu sein.\n\n");
31
32
printf("Versuch 3: ");
33
puts("STURM");
34
printf("Häh? An der Variable lag's also wohl doch nicht.\n");
35
printf("Vermutlich kann ein echter Sturm aus Sicherheitsgründen nicht in\n");
36
printf("einem Stück ausgegeben werden.\n\n");
37
38
printf("Versuch 4: ");
39
putchar(sturm[0]);
40
putchar(sturm[1]);
41
putchar(sturm[2]);
42
putchar(sturm[3]);
43
putchar(sturm[4]);
44
putchar('\n');
45
printf("Jawohl, daran lag's wohl.\n");
46
printf("Da behaupte noch einer, C sei eine unsichere Programmiersprache ;-)\n");
47
}
48
49
intmain(void){
50
51
test1();
52
test2();
53
54
return0;
55
}
Kompilieren und Linken mit Standardoptionen:
1
$ gcc -Wall -O2 -o stringtest stringtest.c
2
$ stringtest
3
Test 1: Versuch, ein Stringliteral zu überschreiben:
4
5
Vorher: LANGSAM AUF DEN AUSSICHTSTURM
6
Segmentation fault (core dumped)
Das war zu erwarten, da Stringliterale normalerweise in der .rodata-
Section abgelegt werden, in die nicht geschrieben werden kann.
Verwendet man die Linkeroption -N, sind alle Sections und damit auch die
Stringliterale beschreibbar:
Test 1: Versuch, ein Stringliteral zu überschreiben:
4
5
Vorher: LANGSAM AUF DEN AUSSICHTSTURM
6
Nachher: HINUNTER ABER GANZ GESCHWIND
7
8
Juhu, es funktioniert tatsächlich!
9
10
11
Test 2: Mehrere Versuche, mal ordentlich STURM zu machen:
12
13
Versuch 1: WIND
14
Nein, der ist viel zu schwach.
15
Wahrscheinlich wird der Sturm vom langsamen printf ausgebremst.
16
17
Versuch 2: WIND
18
Hmm, wieder nichts.
19
Die Leckströme der Speicherzellen der Variable 'sturm' scheinen
20
zu hoch zu sein.
21
22
Versuch 3: WIND
23
Häh? An der Variable lag's also wohl doch nicht.
24
Vermutlich kann ein echter Sturm aus Sicherheitsgründen nicht in
25
einem Stück ausgegeben werden.
26
27
Versuch 4: STURM
28
Jawohl, daran lag's wohl.
29
Da behaupte noch einer, C sei eine unsichere Programmiersprache ;-)
Also: In ernsthaften Programmen nie versuchen, Stringliterale oder
andere Konstanten zu überschreiben, auch dann nicht, wenn der Compiler
den Versuch gnädigerweise durchgehen lässt.
Yalu X. schrieb:> Dann passieren aber evtl. andere seltsame Dinge.
Nettes Beispiel. Hat einen Moment gedauert bis ich verstanden habe was
da vor sich geht...
fragend schrieb:> Das hört sich nach Norm an, kann man das irgendwo nachlesen?
Du kannst die Norm
ISO/IEC 9899:2011: Programming languages – C
hier erwerben:
http://www.iso.org/iso/iso_catalogue/catalogue_tc/catalogue_detail.htm?csnumber=57853
Falls du keine 198 Schweizer Franken locker machen willst, gibt es zudem
den letzten Committee Draft mit der Bezeichnung N1570 kostenlos zum
Herunterladen.
Um Normen wie diese zu lesen, sollte man aber schon etwas tiefer in der
Materie stecken. Das Meiste, was man als Anfänger wissen muss, erfährt
man leichter aus einschlägigen Lehrbüchern.
fragend schrieb:> Wieso ist es möglich, eine Konstante ( auf diesem Weg ) doch zu ändern?> […]> Gibt es eine Programmiersprache, die das verhindert oder darauf> aufpasst?
Wenn du so fragst:
Am eindeutigsten ist die Sache mit den Konstanten in rein funktionalen
Sprachen wie bspw. Haskell geregelt. Die ganz einfache Regel lautet
dort:
Alle Variablen sind unveränderlich.
Variablen werden direkt bei ihrer Geburt initialisiert (im Fachjargon
"gebunden") und können dann bis zu ihrem Ableben nicht mehr geändert
werden. Das ist so, als wenn man in C alle Variablen mit const
deklarieren würde. Deswegen gibt es auch keine Zuweisungsoperationen.
Und weil es diese nicht gibt, muss der Compiler die Einhaltung der
obigen Regel nicht einmal explizit kontrollieren (und ggf. eine
Fehlermeldung ausgeben).
In Haskell ist es viel schwerer als in anderen Sprachen, Fehler zu
machen. Allerdings ist es mitunter auch etwas schwerer, damit überhaupt
ein kompilierfähiges Programm hinzubekommen ;-)
Yalu X. schrieb:> Alle Variablen sind unveränderlich.
Das widerspricht sich aber. Variablen heißen Variablen eben weil sie
veränderbar, also variabel sind.
Alles andere sind Konstanten, nicht aber konstante Variablen.
Yalu X. schrieb:> Am eindeutigsten ist die Sache mit den Konstanten in rein funktionalen> Sprachen wie bspw. Haskell geregelt. Die ganz einfache Regel lautet> dort:>> Alle Variablen sind unveränderlich.>> Variablen werden direkt bei ihrer Geburt initialisiert (im Fachjargon> "gebunden") und können dann bis zu ihrem Ableben nicht mehr geändert> werden. Das ist so, als wenn man in C alle Variablen mit const> deklarieren würde. Deswegen gibt es auch keine Zuweisungsoperationen.> Und weil es diese nicht gibt, muss der Compiler die Einhaltung der> obigen Regel nicht einmal explizit kontrollieren (und ggf. eine> Fehlermeldung ausgeben).>> In Haskell ist es viel schwerer als in anderen Sprachen, Fehler zu> machen. Allerdings ist es mitunter auch etwas schwerer, damit überhaupt> ein kompilierfähiges Programm hinzubekommen ;-)
Kann man damit ein Programm schreiben, das etwas sinnvolles tut?
Mir kommt es so vor, als wenn man ein Kind in eine Zwangsjacke steckt,
damit es nicht in der Nase bohren kann.
Jobst Q. schrieb:> Kann man damit ein Programm schreiben, das etwas sinnvolles tut?
Warum nicht? In Java sind z.B. auch Strings unveränderbar. Trotzdem kann
man damit Stringhandling machen.
Timmo H. schrieb:> Yalu X. schrieb:>> Alle Variablen sind unveränderlich.> Das widerspricht sich aber. Variablen heißen Variablen eben weil sie> veränderbar, also variabel sind.
Sie sind auch variabel, aber nicht so, wie du gerade denkst.
Variablen in althergebrachter Programmierung sind Schachteln, in die man
etwas hineintun kann. Speicherstellen.
Variablen in funktionaler Programmierung sind hingegen das, was auch in
der Mathematik als Variable bezeichnet wird: eine Größe, die zunächst
beim Hinschreiben nicht näher bestimmt sein muß:
x = 2 + 3 ist auch nicht veränderlich. x ist immer fünf. Aber es ist ein
Platzhalter, der konzeptionell variabel ist, im Gegensatz zu der 2 und
der 3, die fest bestimmt sind. Aufgabe des Mathematikers ist nun, die
Gleichung zu lösen und dadurch den Wert von x zu ermitteln.
Das Gleichungssystem
x = 2 + 3
x = 2 + 4
hat hingegen keine Lösung. Auch das ist genau wie in funktionalen
Programmiersprachen. Dort meckert der Compiler.
Wie auch in der Mathematik bedeutet das nicht, daß x 6 wäre, nur weil
die Zeile x = 2 + 4 später kommt.
Timmo H. schrieb:> Yalu X. schrieb:>> Alle Variablen sind unveränderlich.> Das widerspricht sich aber. Variablen heißen Variablen eben weil sie> veränderbar, also variabel sind.
Tim hat es ja schon gut erklärt. Hier ist noch mein Senf dazu:
Ja, etymologisch ist das ein Widerspruch. Den Begriff haben die
Mathematiker geprägt, die damit ausdrücken wollen, dass eine Variable
zwar unterschiedliche Werte annehmen kann, die sich innerhalb eines
Theorems oder einer Definition nicht verändern. Mehr dazu hier:
https://de.wikipedia.org/wiki/Variable_(Mathematik)> Alles andere sind Konstanten, nicht aber konstante Variablen.
Eine Konstante ist in der Mathematik etwas, das nicht frei wählbar ist
und sich auch nie ändert, wie bspw. π und e.
Auch in C und C++ sind mit const deklarierte Variablen nach ISO-Norm
zwar unveränderlich, aber mmer noch Variablen und keine Konstanten.
Konstanten sind hier bspw. 123, 4.5 und 'A'.
Wie schon von Tim aufgezeigt, entsprechen die Variablen in Haskell aber
eher denen in der Mathematik. Während der Ausdruck a=a+1 in C eine
Erhöhung von a um 1 bedeutet, ist das in der Mathematik je nach Kontext
entweder eine unlösbare Gleichung oder eine (wenig sinnvolle) rekursive
Definition. In Haskell ist das ebenfalls eine rekursive Definition, die
aber bei ihrer Anwendung zu einer Endlosschleife führt.
Jobst Q. schrieb:> Kann man damit ein Programm schreiben, das etwas sinnvolles tut?
Natürlich, siehe Beiträge von Kaj und Rolf.
> Mir kommt es so vor, als wenn man ein Kind in eine Zwangsjacke steckt,> damit es nicht in der Nase bohren kann.
Dein Vergleich ist sehr treffend: Dass das Kind nicht in der Nase bohren
kann, reduziert die Gefahr, dass es ein Nasenloch überdehnt oder sich
gar den Finger bricht. Das geht natürlich auf Kosten des Komforts :)
Auch Haskell ist ziemlich restriktiv, was manchmal für den Programmierer
unbequem sein kann. Im Gegenzug werden aber einige der in anderen
Programmiersprachen typische Fehlerquellen entweder komplett beseitigt
oder zumindest deutlich entschärft, was die Sicherheit erhöht. Der damit
reduzierte Komfort wird an anderer Stelle durch spezielle Features, die
das Programmieren erleichtern, großteils wieder kompensiert.
Haskell ist also, um bei deinem Vergleich zu bleiben, eine Zwangsjacke
mit integriertem MP3-Player und Klimaanlage :)
Carl D. schrieb:> Vielleicht wäre der Begriff Einmalvariable passender, denn ganz> unveränderlich sind sie ja nicht. Aber eben nur einmal beschreibbar.
Also muss für jedes Zwischenergebnis eine neue Variable angelegt werden.
Da ist ja eine großartige Effizienz zu erwarten.
Jobst Q. schrieb:> Kann man damit ein Programm schreiben, das etwas sinnvolles tut?
Die meisten modernen Compiler machen sogar aus deinem Code voller
"echter" Variablen genau das: Die SSA-Form ist eine beliebte
Zwischendarstellung für Code, weil sie viele Optimierungen und
Codetransformationen relativ einfach ermöglicht. "SSA" steht dabei für
"static single assignment" und damit eben für "Einmalzuweisung".
Aus deinem C-Code
int x = 1;
x += y;
x += z;
wird damit intern folgendes:
int x0 = 1;
int x1 = x0 + y;
int x2 = x1 + z;
yes, we often can get the performance of C, while working at a higher
2
altitude. And to do so we really want to exploit all the additional
3
information we have at hand when working more abstractly.
So, und jetzt hör endlich auf gegen Dinge zu bashen, die du nicht
kennst, und vor denen du angst hast, weil du sie nicht verstehst. Anders
kann ich mir diese Pöbelei nämlich nicht erklären...
Schon mal Rust angeschaut? Da sind auch alle Variablen per se nicht
veränderbar. Willst du eine Variable, die man verändern kann, so musst
du das extra sagen. Praktisch der umgekehrte Weg von const in C.
Und Rust kommt auch an die Performance von C heran, aber mit komfort den
man eher aus Python und Co. kennt.
Aber das ist ja Teufelszeug, und kann gar nicht sein. Das muss ja
automatisch schlecht sein...
Jobst Q. schrieb:> Kann man damit ein Programm schreiben, das etwas sinnvolles tut?>> Mir kommt es so vor, als wenn man ein Kind in eine Zwangsjacke steckt,> damit es nicht in der Nase bohren kann.
Lese Dir mal "Structure and Interpretation of Computer Programs"
https://mitpress.mit.edu/sicp/full-text/book/book.html durch.
Dann wirst Du sehen, dass man Zuweisungen und Schleifen nicht
unbedingt braucht, um sinnvolle Programme zu schreiben.
SICP verwendet scheme ein lisp Dialekt, in dem man u.a. wie in haskell
funktional programmieren kann.
Z.B. der emacs ist z.T. in lisp geschrieben.
Carl D. schrieb:> Vielleicht wäre der Begriff Einmalvariable passender, denn ganz> unveränderlich sind sie ja nicht. Aber eben nur einmal beschreibbar.
Soviel Aufmerksamkeit für eine Zeile Code.
Und fast genau soviele Ansichten. Ihr macht mir Freude.
Danke an alle! Viel dabei das sich lohnt zu vertiefen.
Ich hoffe nur, das Buch "C von A bis Z" (J.Wolf) das ich habe,
wurde nach eine Norm entworfen.
Dankeschön!
Jobst Q. schrieb:> Da ist ja eine großartige Effizienz zu erwarten.
Du solltest dich von der Vorstellung lösen, das solche oberflächlichen
Aspekte einer Sprache hinterher im umgesetzten Maschinencode
Niederschlag fänden.
Selbstverständlich wirst du im Maschinencode hinterher haufenweise
Speicherstellen und Register finden, die vom Compiler immer wieder neu
belegt werden, wie sollte es auch anders sein.
Nur der Programmierer kodiert das nicht so.