Forum: PC-Programmierung Frage zu einer Codezeile in C


von fragend (Gast)


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

von Carl D. (jcw2)


Lesenswert?

Schreib einfach
1
  Personenname = "Huber";
2
  printf("Name: *s\n", Personenname);
und schau dir irgendwo C-Grundlagen an.
Hint:
char*      Zeiger auf Zeichen
"Huber".   Zeichenkonstante

: Bearbeitet durch User
von Peter II (Gast)


Lesenswert?

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.)

von Kaj (Gast)


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.

von fragend (Gast)


Lesenswert?

@Carl
Danke!
Ich bin nicht allein mit meiner Frage
http://stackoverflow.com/questions/23644586/problems-about-c-program-char-pointer

Es scheint also nicht ganz so einfach zu sein.
Dein Beispiel verstehe ich.
Nur diese Zeile nicht
1
printf("Name: *s\n", Personenname);
Was ist das für ein Format : *s ?

von Timmo H. (masterfx)


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.

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


Lesenswert?

Das Format "*s" kennt ein normales printf nicht.

Es muss lauten

printf("Name: %s\n", Personenname);

von fragend (Gast)


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!

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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".

von fragend (Gast)


Lesenswert?

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"

von Sebastian V. (sebi_s)


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";

von Peter II (Gast)


Lesenswert?

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.

von Kaj (Gast)


Lesenswert?

Sebastian V. schrieb:
> und compliert in C++
> auch gar nicht ohne const.
Das stimmt ja so schon mal nicht, es gibt nur ne lapidare Warnung.
1
Warnung: ISO C++ verbietet, eine Zeichenkettenkonstante in »char*« zu konvertieren [-Wwrite-strings]
Compiliert also sehr wohl.
Aber : Hier geht es nicht um C++

von Oldie but goldie (Gast)


Lesenswert?

fragend schrieb:
> Wie kann man das Wissen "nachlernen"

Ohne jegliche Polemik: Lies ein Grundlagenbuch zu C,
z.B. den Klassiker The C Programming Language:
https://www.amazon.de/dp/0131103628

von fragend (Gast)


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
}

von Timmo H. (masterfx)


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
}

: Bearbeitet durch User
von Peter II (Gast)


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.

von fragend (Gast)


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"?

von Kaj (Gast)


Lesenswert?

fragend schrieb:
> wird das Programm "abschmieren"?
Probier es einfach aus :)

von Paul B. (paul_baumann)


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

von Poster (Gast)


Lesenswert?

Deshalb steige ich auch nie in ein Flugzeug bei dem ein Meyer oder Huber 
auf der Pasagierliste steht.
Wer weis was dort der Bordcomputer macht.

von Timmo H. (masterfx)


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.

: Bearbeitet durch User
von fragend (Gast)


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.

von fragend (Gast)


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!

von Daniel A. (daniel-a)


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.

von Peter II (Gast)


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";

von Kaj (Gast)


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.

von Yalu X. (yalu) (Moderator)


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.

: Bearbeitet durch Moderator
von Carl D. (jcw2)


Lesenswert?

fragend schrieb:
> Dein Beispiel verstehe ich.
> Nur diese Zeile nicht
>
1
> printf("Name: *s\n", Personenname);
2
>
> Was ist das für ein Format : *s ?

Ein Tipp- bzw. Touch-Fehler ;-)
Es bleibt beim "%" für printf-Formate.

: Bearbeitet durch User
von Nase (Gast)


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.

von Paul B. (paul_baumann)


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

von Yalu X. (yalu) (Moderator)


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.

von Sebastian V. (sebi_s)


Lesenswert?

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...

von Yalu X. (yalu) (Moderator)


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 ;-)

von Dirk B. (dirkb2)


Lesenswert?

Yalu X. schrieb:
> den letzten Committee Draft mit der Bezeichnung N1570 kostenlos zum
> Herunterladen.
Den URL gibt es bei https://www.c-plusplus.net/forum/300567
(für N1570 leider nur als Text)

von Timmo H. (masterfx)


Lesenswert?

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.

von Jobst Q. (joquis)


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.

von Kaj (Gast)


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?

von Rolf M. (rmagnus)


Lesenswert?

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.

von Carl D. (jcw2)


Lesenswert?

Vielleicht wäre der Begriff Einmalvariable passender, denn ganz 
unveränderlich sind sie ja nicht. Aber eben nur einmal beschreibbar.

von Tim (Gast)


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.

von Yalu X. (yalu) (Moderator)


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 :)

: Bearbeitet durch Moderator
von Jobst Q. (joquis)


Lesenswert?

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.

von Tim (Gast)


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;

von Kaj (Gast)


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...

von Alexander S. (alesi)


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.

von Sheeva P. (sheevaplug)


Lesenswert?

Carl D. schrieb:
> Vielleicht wäre der Begriff Einmalvariable passender, denn ganz
> unveränderlich sind sie ja nicht. Aber eben nur einmal beschreibbar.
1
const char *const name = "Carl";

von fragend (Gast)


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!

von Tim (Gast)


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.

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