Frage zu einer Codezeile in C

Gast #4731096
Lesenswert?

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
Gast #4731118
Lesenswert?

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.
#4731123
Lesenswert?

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.
Gast #4731126
Lesenswert?

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!
#4731142
Lesenswert?

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
char Personenname[] = "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
const char* Personenname = "Meyer";
Gast #4731153
Lesenswert?

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?
1
#include <stdio.h>
2

3
char *Person1 = "Meyer";
4
char *Person2 = "Huber";
5

6
void tausche(char **a, char **b) {
7
  char * t = *a;
8
  *a = *b;
9
  *b = t;
10
}
11

12
void main ( void ) {
13

14
printf ("Person1: %s\n", Person1);  // Meyer
15
printf ("Person2: %s\n", Person2);  // Huber
16

17
printf ("Tauschen\n");
18
tausche (&Person1, &Person2);
19

20
printf ("Person1: %s\n", Person1);  // Huber
21
printf ("Person2: %s\n", Person2);  // Meyer
22
 
23
}
#4731155
Lesenswert?

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
char Personenname[] = "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
const char* Personenname = "Meyer";

Tatsache... mein gcc schmeisst mit -Wall zwar kein Fehler aber das 
Programm schmiert ab
1
char *stringding1 = "Hallo";
2
char stringding2[] = "Hallo";
3
int main()
4
{
5
    strcpy(stringding1, "bla"); //schmiert ab
6
    printf("%s\n",stringding1);
7

8
    strcpy(stringding2, "bla"); //schmiert nicht ab
9
    printf("%s\n",stringding2);
10
    return 0;
11
}
Gast #4731156
Lesenswert?

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.
Gast #4731161
Lesenswert?

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"?
#4731183
Lesenswert?

Timmo H. schrieb:
> Tatsache... mein gcc schmeisst mit -Wall zwar kein Fehler aber das
> Programm schmiert ab

Peter 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
#4731190
Lesenswert?

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.
Gast #4731198
Lesenswert?

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.
Gast #4731202
Lesenswert?

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!
#4731206
Lesenswert?

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.
Gast #4731212
Lesenswert?

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";
Gast #4731254
Lesenswert?

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 int main(void)
6
 6 {
7
 7     char *name = "Meyer";
8
 8
9
 9>    strcpy(name, "Huber");
10
10     return 0;
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
int main(void)
2
{
3
    const char const text[] = "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
    return 0;
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«
6
 extern char *strcpy (char *__restrict __dest, const char *__restrict __src)
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.
Moderator #4731305
Lesenswert?

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
  const char *

sein. Folglich müsste eine Zeigervariable, die darauf zeigen soll,
ebenfalls mit const deklariert werden:

1
  const char *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.
Gast #4731401
Lesenswert?

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.
#4731449
Lesenswert?

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
Moderator #4731629
Lesenswert?

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
void test1(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
void test2(void) {
17
  const char *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
int main(void) {
50

51
  test1();
52
  test2();
53

54
  return 0;
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:

1
$ gcc -Wall -O2 --static -Wl,-N -o stringtest stringtest.c 
2
$ stringtest
3
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.
Moderator #4731682
Lesenswert?

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 ;-)
#4731943
Lesenswert?

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.
Gast #4731971
Lesenswert?

Jobst Q. schrieb:
> Kann man damit ein Programm schreiben, das etwas sinnvolles tut?

https://de.wikipedia.org/wiki/Haskell_(Programmiersprache)#
1
Haskell ist eine rein funktionale Programmiersprache. Funktionen geben nur
2
Werte zurück, ändern aber nicht den Zustand eines Programms (d. h.
3
Funktionen haben keine Nebeneffekte). Das Ergebnis einer Funktion hängt
4
deshalb nur von den Eingangsparametern ab, und nicht davon, wann oder wie
5
oft die Funktion aufgerufen wird (siehe funktionale Programmierung).
6

7
...
8

9
Es gibt keine Operationen, die einen Variablenwert verändern. So gibt es
10
auch keine Unterscheidung zwischen Variablen und Konstanten und man braucht
11
keine const-Attribute oder Literal-Makros wie in C++ oder in C.

Haskell in industry
https://wiki.haskell.org/Haskell_in_industry
1
AT&T 
2

3
Haskell is being used in the Network Security division to automate
4
processing of internet abuse complaints. Haskell has allowed us to easily
5
meet very tight deadlines with reliable results.
6

7

8
BAE Systems 
9

10
As part of the SAFE project, BAE has built a collection of compilers,
11
interpreters, simulators, and EDSLs almost entirely in Haskell.


BAE Systems: Redesigning the Computer for Security
http://cufp.org/2013/tom-hawkins-bae-systems-redesigning-computer-secur.html

Reicht das?
Gast #4732082
Lesenswert?

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.
Moderator #4732104
Lesenswert?

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 :)
Gast #4732114
Lesenswert?

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;
Gast #4732148
Lesenswert?

Jobst Q. schrieb:
> Da ist ja eine großartige Effizienz zu erwarten.

Haskell as fast as C: working at a high altitude for low level 
performance
https://donsbot.wordpress.com/2008/06/04/haskell-as-fast-as-c-working-at-a-high-altitude-for-low-level-performance/
1
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...
#4732202
Lesenswert?

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.
Gast #4732438
Lesenswert?

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!
Gast #4732443
Lesenswert?

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.

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