Forum: PC-Programmierung 0-pointer in c


von futz (Gast)


Lesenswert?

Hallo zusammen.
Ich übergebe einer Funktion einen Pointer auf eine Variable (in C). Wenn 
diese Variable noch keinen gültigen Wert erhalten hat (z.B. Variable 
enthält letzten Messwert, es wurde aber noch keine Messung 
durchgeführt), würde ich das gerne meiner Funktion mitteilen. Also, so 
dachte ich mir, übergebe ich einen NULL-Pointer, der ja in C auf die 
Adresse 0 zeigt.
Ich frage mich nun, ob (bzw. warum) es ausgeschlossen ist, dass sich 
eine Variable tatsächlich an der Adresse 0 befindet.
Für einen kleinen Hinweis wäre ich dankbar.

: Verschoben durch Admin
von Peter D. (peda)


Lesenswert?

futz schrieb:
> Ich frage mich nun, ob (bzw. warum) es ausgeschlossen ist, dass sich
> eine Variable tatsächlich an der Adresse 0 befindet.

Z.B. beim 8051 ist das möglich, da er getrennte Daten- und Codezugriffe 
hat.
Ich habe daher dem Linker gesagt, daß XDATA an der Adresse 0x0001 
beginnt. Das eine Byte ungenutzter SRAM kann man verschmerzen.

: Bearbeitet durch User
von PittyJ (Gast)


Lesenswert?

Man verwendet NULL oder nullptr.
Der Compiler und die Architektur legen dann fest, was sich wirklich 
hinter dem Wert verbirgt. Das muss nicht ungedingt 0 sein.

von futz (Gast)


Lesenswert?

Danke.

von mh (Gast)


Lesenswert?

PittyJ schrieb:
> Man verwendet NULL oder nullptr.
nullptr ist C++.

PittyJ schrieb:
> Der Compiler und die Architektur legen dann fest, was sich wirklich
> hinter dem Wert verbirgt. Das muss nicht ungedingt 0 sein.
Der Standard legt fest, dass NULL effektiv als (void*)0 definiert ist.

von Carl D. (jcw2)


Lesenswert?

Je nach Rechner-Architektur ist an Adresse 0
"nichts", genauer wenn es virtuelle Speicherverwaltung gibt wird die 
Page 0 (z.B. 4096Bytes) als "nicht existent" markiert,
oder Überraschendes, z.B. bei AVR (zumindest denen, die ich kenne) die 
CPU-Register. *(uint8_t*)0 -> R0;  *(uint8_t*)1 -> R1; ...

von mh (Gast)


Lesenswert?

Carl D. schrieb:
> Je nach Rechner-Architektur ist an Adresse 0
> "nichts", genauer wenn es virtuelle Speicherverwaltung gibt wird die
> Page 0 (z.B. 4096Bytes) als "nicht existent" markiert,
> oder Überraschendes, z.B. bei AVR (zumindest denen, die ich kenne) die
> CPU-Register. *(uint8_t*)0 -> R0;  *(uint8_t*)1 -> R1; ...

Ist das für deinen Compiler (AVR gcc?) dokumentiert? *(uint8_t*)0 sieht 
für mich ziemlich nach UB aus, da ein Null-Pointer dereferenziert wird 
(wenn auch (uint8_t*)0 == (void*)0 und damit ein Null-Pointer).

von Dirk B. (dirkb2)


Lesenswert?

An Adrese 0 kann durchaus etwas Sinnvolles stehen.
Z.B. Resetvektoren oder Register.

Aber für den normalen Nutzer eher nicht. Darum hat man diese Adresse 
genommen
NULL bedeutet lediglich "ungültige Adresse"
(einen Wert musste man halt nehmen)

Wenn man Zugriffsrechte auf die Adresse 0 hat, kann man auch einen 
NULL-Pointer dereferenzieren.

von mh (Gast)


Lesenswert?

Dirk B. schrieb:
> Wenn man Zugriffsrechte auf die Adresse 0 hat, kann man auch einen
> NULL-Pointer dereferenzieren.

Und hat damit undefined behaviour.

von Markus F. (mfro)


Lesenswert?

mh schrieb:
> Dirk B. schrieb:
>> Wenn man Zugriffsrechte auf die Adresse 0 hat, kann man auch einen
>> NULL-Pointer dereferenzieren.
>
> Und hat damit undefined behaviour.

Bei einigermassen aktuellem gcc wird das beispielsweise mit einem 
abort() bestraft, wenn man beim Compilieren nicht 
-no-delete-null-pointer-checks mitgibt.

von Oliver S. (oliverso)


Lesenswert?

Das ist gennauso wie der cast per Union im Standard UB, in der Praxis 
funktionierts halt trotzdem.

Oliver

von Carl D. (jcw2)


Lesenswert?

mh schrieb:
> Dirk B. schrieb:
>> Wenn man Zugriffsrechte auf die Adresse 0 hat, kann man auch einen
>> NULL-Pointer dereferenzieren.
>
> Und hat damit undefined behaviour.

Was soll an einem Zugriff auf das erste Byte/Wort/DWord/... UB sein.
Es kann sein daß da nichts sinnvolle gemapped ist, aber sonst ...
Bei STM32F103 (da hab ich gerade die Doku zur Hand) findet man dort den 
Anfang des Flashs.
Oder ist UB schon, daß man den Inhalt des Flashs (fast) beliebig 
gestalten kann.

Meine Intension war eigentlich, einem unerfahrenen TO mitzugeben, daß 
der Zugriffsfehler eines großen OS bei Benutzung des Nullptrs von eben 
diesem OS mit speziellen Tricks erzwungen wird und nichts mit mit der 
magischen Zahl Null zu tun hat.

Was glaubst du passiert, wenn man auf einem AVR mit malloc() Speicher 
holt, keine Prüfung auf NULL als Resultat macht und den neu beschafften 
Speicherbereich beschreibt. Da kommt keine allgemeine Schutzverletzung, 
da sind die CPU-Register Müll.

von mh (Gast)


Lesenswert?

Carl D. schrieb:
> Was soll an einem Zugriff auf das erste Byte/Wort/DWord/... UB sein.
Es ist UB einen Null-Pointer zu dereferenzieren.

Carl D. schrieb:
> Was glaubst du passiert, wenn man auf einem AVR mit malloc() Speicher
> holt, keine Prüfung auf NULL als Resultat macht und den neu beschafften
> Speicherbereich beschreibt. Da kommt keine allgemeine Schutzverletzung,
> da sind die CPU-Register Müll.
So manifestiert der Compiler auf einem AVR dieses spezielle UB.

von IUnknown (Gast)


Lesenswert?

mh schrieb:
> Es ist UB einen Null-Pointer zu dereferenzieren.

aus dem ISO-C Standard:

6.3.2.3 Pointers

3)
An integer constant expression with the value 0, or such an expression 
cast to type void *, is called a null pointer constant. 55) If a null 
pointer constant is converted to a pointer type, the resulting pointer, 
called a null pointer, is guaranteed to compare unequal to a pointer to 
any object or function.

J.3.12 Implementation defined behavior
The null pointer constant to which the macro NULL expands


-> Es ist nirgentwo definiert dass in einem NULL pointer auch "0" drin 
stehen muss als Addresse. Es ist also denkbar, dass ein Pointer an 
RAM-Addresse 0 zeigt und trotzdem nicht als NULL-Pointer zählt. damit 
wäre *(int *)0 erlaubt.

von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Hier wird auch mal kurz das Ding mit dem dereferenzieren von 
NULL-Pointern angesprochen:
https://youtu.be/yG1OZ69H_-o

Ziemlich zu Anfang, bei ca. 5:30

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Markus F. schrieb:
> Bei einigermassen aktuellem gcc wird das beispielsweise mit einem
> abort() bestraft, wenn man beim Compilieren nicht
> -no-delete-null-pointer-checks mitgibt.

avr-gcc verwendet -fno-delete-null-pointer-checks, und zwar hart:

http://gcc.gnu.org/viewcvs/gcc/trunk/gcc/config/avr/avr.c?view=markup&pathrev=254003#l725

IUnknown schrieb:
> -> Es ist nirgentwo definiert dass in einem NULL pointer auch "0" drin
> stehen muss als Addresse. Es ist also denkbar, dass ein Pointer an
> RAM-Addresse 0 zeigt und trotzdem nicht als NULL-Pointer zählt.

Soweit ACK.

> damit wäre *(int *)0 erlaubt.

Seh ich nicht so, denn (int*) 0 ist immer noch als Null-Pointer 
festgelegt (auch wenn die interne Darstellung ungleich 0x0 ist).

An eine gültige Adresse, die physisch 0x0 enthält, käme man dann nur 
dadurch, dass man die Adresse eines Objekts nimmt, dass an 0x0 beginnt.

Anm: Bei avr-gcc gibt es keine Objekte, sie sinnvoll an Adresse 0x0 
liegen, zumindest wenn man reale Hardware voraussetzt und nicht 
irgendeinen AVR-Simulator.  Von daher ist 0x0 als Wert für (void*)0 
naheliegend und auch so implementiert.

von MaWin O. (Gast)


Lesenswert?

Johann L. schrieb:
> Seh ich nicht so, denn (int*) 0 ist immer noch als Null-Pointer
> festgelegt (auch wenn die interne Darstellung ungleich 0x0 ist).

Richtig.
Eher so:

*((int *)4 - 1)

von mh (Gast)


Lesenswert?

Johann L. schrieb:
> Seh ich nicht so, denn (int*) 0 ist immer noch als Null-Pointer
> festgelegt (auch wenn die interne Darstellung ungleich 0x0 ist).
Sagt der Standard nicht nur, dass (void*)0 die "null pointer constant" 
ist und alle anderen "null pointer" beim Vergleich mit der "null pointer 
constant" true ergeben müssen?
In dem Fall wäre es möglich, dass (int*)0 kein null pointer ist, auch 
wenn es ziemlich schwachsinnig wäre es umzusetzen.

von Axel S. (a-za-z0-9)


Lesenswert?

futz schrieb:

> Ich übergebe einer Funktion einen Pointer auf eine Variable (in C). Wenn
> diese Variable noch keinen gültigen Wert erhalten hat (z.B. Variable
> enthält letzten Messwert, es wurde aber noch keine Messung
> durchgeführt), würde ich das gerne meiner Funktion mitteilen.

So weit, so gut.

> Also, so dachte ich mir, übergebe ich einen NULL-Pointer

Aber hier wird es Käse.

Warum rufst du die Funktion überhaupt auf, wenn du keine gültigen Daten 
zu übergeben hast? Warum übergibst du überhaupt einen Pointer und nicht 
den Wert? Einen Pointer übergibt man eigentlich nur in 2 Fällen:

1. die Variable ist in Wirklichkeit eine struct von einiger Größe und es 
wäre ineffizient, erst eine Kopie davon anzulegen. Dann übergibt man 
aber einen Pointer auf const.

2. man will den Wert der Variablen (das worauf der Pointer zeigt) 
innerhalb der Funktion ändern. Dann darf man aber keinen Nullpointer 
übergeben, denn die Funktion würde beim Versuch des Schreibens das 
Programm zu Absturz bringen.

Wie man es dreht und wendet, es ist unsinnig.

Sauber könntest du das auf verschiedene Arten lösen:

a) du initialisierst die Variable mit einem unmöglichen Wert. Das 
nennt sich inband signaling und ist in C weit verbreitet. Z.B. 
Funktionen, die normal nur positive Werte liefern können, im Fehlerfall 
aber z.B. -1.

b) du spendierst der Funktion einen weiteren Parameter, für die 
Gültigkeit des Meßwerts. Das heißt dann out-of-band signaling und gilt 
allgemein als sauberer.

von fop (Gast)


Lesenswert?

Ich schätze, den Wert 0 hat man benutzt, weil den etliche CPU's schnell 
erkennen können (Stichwort Zero-Flag). Und natürlich, weil diejenigen, 
die das festgelegt haben Architekturen gewöhnt waren, bei denen an 
Adresse 0 nichts vom C-Programm liegen konnte.

In der Tat kann es vorkommen, dass 0 eine gültige Adresse von Daten ist. 
Dann muss man halt ein wenig tricksen, damit nichts, auf was ein Zeiger 
zeigen soll, dort zu liegen kommt.

Und bei Architekturen mit mehreren Adressbereichen, sollte ein Pointer 
IMHO auch immer eine Info beinhalten, welcher Adressbereich gemeint ist. 
Wenn man versucht, sich das zu ersparen, kommt man auf ganz seltsame 
Konstrukte, wie z.B. Attribut Progmem.
Wobei ich sagen würde, dass der 8032 über 4 Adressbereiche 
(Verschiedenes unter der selben Nummer) verfügt.

von Detlev T. (detlevt)


Lesenswert?

Vorsicht, Falle! Bei den AVRs hatte ich mehr als einmal den Fehler 
gemacht, einen Nullpointer abzufangen ohne daran zu denken, dass an 
Adresse 0 im EEPROM gültige Daten liegen.

Bei den meisten Systemen liegt aber an Adresse 0 etwas, auf das eine 
Applikation nicht zugreifen muss. Daher diese Konvention, insbesondere 
weil alle (mir bekannten) CPUs das erkennen einer Null unterstützen.

: Bearbeitet durch User
von Carl D. (jcw2)


Lesenswert?

Detlev T. schrieb:
> Vorsicht, Falle! Bei den AVRs hatte ich mehr als einmal den Fehler
> gemacht, einen Nullpointer abzufangen ohne daran zu denken, dass an
> Adresse 0 im EEPROM gültige Daten liegen.

Aus dem Mega48/88/168/328 DB:
1
The lower 768/1280/1280/2303 data memory locations address both the Register File, the I/O memory, Extended I/O memory, and the internal data SRAM. The first 32 locations address the Register File, the next 64 location the standard I/O memory, then 160 locations of Extended I/O memory, and the next 512/1024/1024/2048 locations address the internal data SRAM

Mit *(uint8_t*)(0)=42; schreibt man 42 ins R0.
Das sind gültige Daten, aber nur selten ist sowas beabsichtigt und 
danach verhält sich das Programm meist undefined.
Anwendungen außer kryptifizierung eines Programms (unter Verschwendung 
der wertvollen Pointer-Register): keine.

>
> Bei den meisten Systemen liegt aber an Adresse 0 etwas, auf das eine
> Applikation nicht zugreifen muss. Daher diese Konvention, insbesondere
> weil alle (mir bekannten) CPUs das erkennen einer Null unterstützen.

"Nicht muß" ist deutlich schwächer als das "nicht kann" der Systeme mit 
MMU. Und vereinzelt (aVR) hat ein Schreibzugrif auf nullptr üble 
Nebenwirkungen.

von Detlev T. (detlevt)


Lesenswert?

@Carl Drexler

RAM != EEPROM

Beim AVR haben Flash, RAM und EEPROM getrennte Adressbereiche, alle 
fangen mit Adresse 0 an.

von Carl D. (jcw2)


Lesenswert?

Detlev T. schrieb:
> @Carl Drexler
>
> RAM != EEPROM
>
> Beim AVR haben Flash, RAM und EEPROM getrennte Adressbereiche, alle
> fangen mit Adresse 0 an.

Echt jetzt?

von Rolf M. (rmagnus)


Lesenswert?

Johann L. schrieb:
>> damit wäre *(int *)0 erlaubt.
>
> Seh ich nicht so, denn (int*) 0 ist immer noch als Null-Pointer
> festgelegt (auch wenn die interne Darstellung ungleich 0x0 ist).

Richtig.

> An eine gültige Adresse, die physisch 0x0 enthält, käme man dann nur
> dadurch, dass man die Adresse eines Objekts nimmt, dass an 0x0 beginnt.

Nein. Man käme dahin z.B. mit:
1
int x = 0;
2
void* p = (void*)x;

Denn nur ein konstanter Integer-Ausdruck mit dem Wert 0 führt zu einem 
Nullzeiger. x ist hier aber kein konstanter Ausdruck.

Ma W. schrieb:
> Johann L. schrieb:
>> Seh ich nicht so, denn (int*) 0 ist immer noch als Null-Pointer
>> festgelegt (auch wenn die interne Darstellung ungleich 0x0 ist).
>
> Richtig.
> Eher so:
>
> *((int *)4 - 1)

Ist allerdings formal auch undefiniertes Verhalten, da man 
Zeigerarithmetik nur innerhalb eines tatsächlich existierenden Arrays 
durchführen darf.
Aber wenn man direkt auf eine vorgegebene Adresse zugreifen will, landet 
man da früher oder später eh.

: Bearbeitet durch User
von Oliver S. (oliverso)


Lesenswert?

Johann L. schrieb:
> avr-gcc verwendet -fno-delete-null-pointer-checks, und zwar hart:
>
> 
http://gcc.gnu.org/viewcvs/gcc/trunk/gcc/config/avr/avr.c?view=markup&pathrev=254003#l725

Was aber nichts am Standard ändert.

NULL ist auf dem AVR trotzdem 0, und die Dereferenzierung funktioniert 
halt einfach. Das könnte  man zwar auch als eine Variante von UB 
ansehen, ist es aber nicht.

Oliver

Beitrag #5224894 wurde vom Autor gelöscht.
von Rolf M. (rmagnus)


Lesenswert?

Oliver S. schrieb:
> Das könnte  man zwar auch als eine Variante von UB ansehen, ist es aber
> nicht.

Doch, selbstverständlich ist es das. Oder willst du sagen, C verbietet 
dieses Verhalten?

: Bearbeitet durch User
von W.S. (Gast)


Lesenswert?

futz schrieb:
> Ich frage mich nun, ob (bzw. warum) es ausgeschlossen ist, dass sich
> eine Variable tatsächlich an der Adresse 0 befindet.
> Für einen kleinen Hinweis wäre ich dankbar.

kleiner Hinweis:
Es ist GARNICHTS ausgeschlossen. Einen Zeiger auf 0 als ungültig 
anzusehen, ist ganz einfach eine Konvention und sonst nichts. 
Selbstverständlich kann sich auf Adresse 0 auch irgend etwas befinden 
- und bei den allermeisten Systemen ist dort auch tatsächlich etwas. Bei 
einigen Fujitsu-Controllern befand sich dort der Schnellzugriffsbereich 
0..255 auf eine Reihe von Registern.

W.S.

von Axel S. (a-za-z0-9)


Lesenswert?

W.S. schrieb:
> futz schrieb:
>> Ich frage mich nun, ob (bzw. warum) es ausgeschlossen ist, dass sich
>> eine Variable tatsächlich an der Adresse 0 befindet.
>> Für einen kleinen Hinweis wäre ich dankbar.
>
> kleiner Hinweis:
> Es ist GARNICHTS ausgeschlossen. Einen Zeiger auf 0 als ungültig
> anzusehen, ist ganz einfach eine Konvention und sonst nichts.

Ist halt nur blöd, daß ein Nullpointer nicht zwangsläufig ein "Zeiger 
auf 0" sein muß. "Nullpointer" ist der Fachbegriff für "Pointer der auf 
nichts zeigt, der insbesondere nicht dereferenziert werden kann". Das 
ist ein abstraktes Konzept und hat mit dem Wert 0 nahezu nichts zu 
tun.

Außer, daß man halt einen Weg vorsehen mußte, einen Nullpointer zu 
konstruieren. Und dazu hat man sich entschieden, den Cast der 
Konstante 0 zu einem Pointer zu verwenden. Und das ist hier die 
Konvention. Weder muß der "Wert" eines Nullpointers 0 sein, noch muß es 
dazu unmöglich sein, ein Objekt an der Speicheradresse 0 anzulegen. 
Für den C-Compiler sind Adressen und Pointer eben nicht das gleiche, 
sondern nur fast.

von W.S. (Gast)


Lesenswert?

Axel S. schrieb:
> Ist halt nur blöd, daß ein Nullpointer nicht zwangsläufig ein "Zeiger
> auf 0" sein muß.

Nö.

Ein Nullpointer ist ein Pointer, der auf Aresse 0 zeigt und nix anderes. 
Deswegen heißt er ja auch so.
Und der TO hat sich ausdrücklich darauf bezogen.

Vielleicht verwechselst du das mit Pascal, dort heißt der ungültige 
Pointer "nil", abgekürzt von "nihil" was zu deutsch "nichts" heißt. Und 
nil ist dort ein Schlüsselwort, ganz im Gegensatz zu "NULL" bei C, was 
bloß eine Text-Ersetzung für 0 ist.

W.S.

von Axel S. (a-za-z0-9)


Lesenswert?

W.S. schrieb:
> Axel S. schrieb:
>> Ist halt nur blöd, daß ein Nullpointer nicht zwangsläufig ein "Zeiger
>> auf 0" sein muß.
>
> Nö.
>
> Ein Nullpointer ist ein Pointer, der auf Aresse 0 zeigt und nix anderes.

Das ist falsch

Siehe:

http://c-faq.com/null/null1.html
http://c-faq.com/null/machnon0.html

> ... "NULL" bei C, was bloß eine Text-Ersetzung für 0 ist.

Und auch das ist falsch

NULL ist ein C-Makro, das normalerweise zu ((void *)0) expandiert. Und 
das ist eben nicht der Wert 0.

von Dave C. (Gast)


Lesenswert?

W.S. schrieb:
> Selbstverständlich kann sich auf Adresse 0 auch irgend etwas befinden
> - und bei den allermeisten Systemen ist dort auch tatsächlich etwas.
> W.S.

Eine sehr gewagte Aussage. Auf Allen Windows NT basierten Systemen (also 
fapp allen Windows Maschinen) ist dort per Definition "nichts," weil das 
OS per-Prozess virtuelle Adressbereiche führt und die erste "Seite" 
jedes virtuellen Speichers (also genau dort wo Adresse 0 hereinfallen 
würde) niemals gültig gemappt ist. Ein Versuch, 0 innerhalb einer Task 
zu dereferenzieren führt also immer zu einem Page Fault.

von Axel S. (a-za-z0-9)


Lesenswert?

Dave C. schrieb:
> W.S. schrieb:
>> Selbstverständlich kann sich auf Adresse 0 auch irgend etwas befinden
>> - und bei den allermeisten Systemen ist dort auch tatsächlich etwas.
>> W.S.
>
> Eine sehr gewagte Aussage.

Vor allem komplett am Thema vorbei. Per Definition ist der Nullpointer 
ein Pointer, der verschieden von jedem Pointer auf irgendeine Variable 
oder Funktion ist. Das heißt, wenn auf der Zielarchitektur ein Objekt 
auf Adresse 0 liegen kann, dann kann der Nullpointer auf dieser 
Architektur nicht der Adresse 0 entsprechen.

Das Konzept Pointer in C ist eben nicht das gleiche wie eine Adresse 
in Assembler. Es ist nur fast das gleiche. Die Existenz des Nullpointers 
ist da nur ein Punkt. Andere Abweichungen haben Pointer bei der 
Arithmetik, wo der Typ des referenzierten Objekt berücksichtigt wird.

von Oliver S. (oliverso)


Lesenswert?

Axel S. schrieb:
> Das heißt, wenn auf der Zielarchitektur ein Objekt
> auf Adresse 0 liegen kann, dann kann der Nullpointer auf dieser
> Architektur nicht der Adresse 0 entsprechen.

Und da sind wir dann wieder beim Unterschied zwischen Theorie und 
Praxis. Denn es gibt halt doch Zielarchitekturen, z.B. AVR, bei denen 
das genauso ist.

Oliver

von Axel S. (a-za-z0-9)


Lesenswert?

Oliver S. schrieb:
> Axel S. schrieb:
>> Das heißt, wenn auf der Zielarchitektur ein Objekt
>> auf Adresse 0 liegen kann, dann kann der Nullpointer auf dieser
>> Architektur nicht der Adresse 0 entsprechen.
>
> Und da sind wir dann wieder beim Unterschied zwischen Theorie und
> Praxis. Denn es gibt halt doch Zielarchitekturen, z.B. AVR, bei denen
> das genauso ist.

Wenn das so ist, dann ist der Compiler potentiell nicht standardkonform. 
Potentiell deswegen, weil der Compiler das Problem immer noch dadurch 
entschärfen kann, daß er an Adresse 0 einfach kein Objekt anlegt, auch 
wenn er es im Prinzip könnte. Ich bin aber kein intimer Kenner des 
C-Standards bzw. dessen Auslegung.

von Oliver S. (oliverso)


Lesenswert?

Nee, das passt schon. Das SRam fängt beim AVR auch erst weiter oben an, 
auf Adresse 0 befindet sich das IO-gemappte Register R0. Damit kann der 
Compiler niemals eigene Objekte dorthin legen, allerdings ist trotzdem 
ein Zugriff auf die Adresse möglich und auch im Einzelfall erforderlich.

Oliver

von mh (Gast)


Lesenswert?

Oliver S. schrieb:
> ...
> allerdings ist trotzdem ein Zugriff auf die Adresse möglich und auch im
> Einzelfall erforderlich.
Kannst du auch zeigen wie man das in C macht, ohne dabei undefined 
behaviour auszunutzen?
1
uint8_t* ptr = 0;
2
*ptr = 42;
Ist UB in C.

von Daniel A. (daniel-a)


Lesenswert?

Axel S. schrieb:
> Das heißt, wenn auf der Zielarchitektur ein Objekt
> auf Adresse 0 liegen kann, dann kann der Nullpointer auf dieser
> Architektur nicht der Adresse 0 entsprechen.

Ein null pointer zeigt auf addresse 0, egal welche Architektur. Bei 
Linux gabs deswegen mal einen gefärlichen Bug:
https://lwn.net/Articles/342330/

von Oliver S. (oliverso)


Lesenswert?

mh schrieb:
> Ist UB in C

Funktioniert aber trotzdem. Theorie und Praxis...

Wie Johann weiter oben schrieb, ist der Compiler mit 
-fno-delete-null-pointer-checks konfiguriert. Damit vermeidet der 
zumindest alle Optimierungen, die darauf basieren, daß ein Zugriff auf 
die Addresse 0 nicht erlaubt ist.

Oliver

: Bearbeitet durch User
von Til S. (Firma: SEGGER) (til_s)


Lesenswert?

Vielleicht dazu noch ein Beispiel aus der Praxis. Im embOS werden alle 
Task Control Blöcke als verkettete Liste behandelt. Der letzte TCB zeigt 
auf NULL. Der Renesas RX Core hat RAM ab Adresse 0. Es wäre also blöd, 
wenn ein TCB am Anfang des RAMs liegen würde. In solchen Fällen 
definieren wir im Linkerfile die RAM Startadresse als 0x04. Damit 
verschenkt man zwar 4 Bytes RAM aber hat das Problem einfach umgangen. 
Das gleiche Prinzip wurde weiter oben auch schon beschrieben.

von Rolf M. (rmagnus)


Lesenswert?

W.S. schrieb:
> Ein Nullpointer ist ein Pointer, der auf Aresse 0 zeigt und nix anderes.
> Deswegen heißt er ja auch so.
> Und der TO hat sich ausdrücklich darauf bezogen.

Dadurch wird es aber nicht automatisch richtig.

> Vielleicht verwechselst du das mit Pascal, dort heißt der ungültige
> Pointer "nil", abgekürzt von "nihil" was zu deutsch "nichts" heißt.

Und du verwechselst offenbar das englische "null" mit der deutschen 
Null. "null" kann auch soviel wie "nichts" oder "ungültig" heißen.

Daniel A. schrieb:
> Axel S. schrieb:
>> Das heißt, wenn auf der Zielarchitektur ein Objekt
>> auf Adresse 0 liegen kann, dann kann der Nullpointer auf dieser
>> Architektur nicht der Adresse 0 entsprechen.
>
> Ein null pointer zeigt auf addresse 0, egal welche Architektur.

Das könnt ihr noch so oft behaupten, es bleibt falsch! Axel Schwenke 
hat das oben sehr gut erklärt. Es gibt in ISO C keine Stelle, die so 
etwas fordert. In der "rationale" zu C99 ist das aber nochmal explizit 
erklärt, da sich dieser Umstand auch auf implizite Initialisierungen 
auswirkt.
So heißt es dort in §6.7.8 "Initialization":

An implementation might conceivably have codes for floating zero and/or 
null pointer other than all bits zero.  In such a case, the 
implementation
must fill out an incomplete initializer with the various appropriate
representations of zero; it may not just fill the area with zero bytes. 
As
far as the committee knows, all machines treat all bits zero as a 
representation of floating-point zero.  But, all bits zero might not be
the canonical representation of zero.
( http://www.open-std.org/jtc1/sc22/wg14/www/C99RationaleV5.10.pdf )

: Bearbeitet durch User
von Daniel A. (daniel-a)


Lesenswert?

Rolf M. schrieb:
> Daniel A. schrieb:
>> Axel S. schrieb:
>>> Das heißt, wenn auf der Zielarchitektur ein Objekt
>>> auf Adresse 0 liegen kann, dann kann der Nullpointer auf dieser
>>> Architektur nicht der Adresse 0 entsprechen.
>>
>> Ein null pointer zeigt auf addresse 0, egal welche Architektur.
>
> Das könnt ihr noch so oft behaupten, es bleibt falsch! Axel Schwenke
> hat das oben sehr gut erklärt. Es gibt in ISO C keine Stelle, die so
> etwas fordert. In der "rationale" zu C99 ist das aber nochmal explizit
> erklärt, da sich dieser Umstand auch auf implizite Initialisierungen
> auswirkt.

Es existieren aber keine Implementierungen, die soetwas tun würden. Es 
wird auch niemals welche geben, weil dann sämtlicher Code, der davon 
ausgeht nichtmehr funktionieren würde. Das würde sämtliche Programme 
betreffen, die memset oder calloc verwenden, um Arrays oder Strukturen 
auszunullen, die Pointer beinhalten, also so ziemlich jedes Programm. 
Deshalb ist es auch vollkommen egal, ob der Standard das erlauben würde, 
es gibt einfach keinen Compiler bauer, der dumm genug wäre, das zu tun, 
und deshalb zeigt ein null pointer auch weiterhin immer auf 0, bei jeder 
Implementierung. Ernsthaft, was zum teufel hat das Standard komitee sich 
bei dem Blödsinn nur gedacht?

Axel S. schrieb:
> 2. man will den Wert der Variablen (das worauf der Pointer zeigt)
> innerhalb der Funktion ändern. Dann darf man aber keinen Nullpointer
> übergeben, denn die Funktion würde beim Versuch des Schreibens das
> Programm zu Absturz bringen.

Man kann auch einfach in der Funktion vorher prüfen, ob der Pointer 0 
ist.

von S. R. (svenska)


Lesenswert?

Daniel A. schrieb:
> Es existieren aber keine Implementierungen, die soetwas tun würden. Es
> wird auch niemals welche geben, ...

Ich bin ja erstaunt, dass du alle Implementierungen dieser Welt kennst, 
aber vollkommen beeindruckt, dass du auch alle Implementierungen kennst, 
die es jemals geben wird. Meine Hochachtung.

Daniel A. schrieb:
> Ernsthaft, was zum teufel hat das Standard komitee sich
> bei dem Blödsinn nur gedacht?

Sie haben auf eine Scheuklappe verzichtet und damit Implementationen 
ermöglicht, für die es möglicherweise vorteilhafter ist, das Problem 
anders anzugehen.

Es gibt ziemlich verkorkste Architekturen auf diesem Planeten, die 
trotzdem in Standard-C programmiert werden können und nicht zwingend auf 
esoterische Spezialsprachen angewiesen sind. Das ist ein massiver 
Vorteil von C gegenüber anderen Sprachen und Umgebungen, und dem trägt 
der Standard Rechnung.

von Rolf M. (rmagnus)


Lesenswert?

Daniel A. schrieb:
> Es existieren aber keine Implementierungen, die soetwas tun würden.

Falsch, siehe http://c-faq.com/null/machexamp.html
Und ja, ich habe gesehen, dass es bei einem der Systeme später geändert 
wurde, weil's zuviel kaputten Code gibt, der falsche Annahmen trifft.

von Steffen R. (steffen_rose)


Lesenswert?

Daniel A. schrieb:
> Es existieren aber keine Implementierungen, die soetwas tun würden.

Keil C51

von Axel S. (a-za-z0-9)


Lesenswert?

Daniel A. schrieb:
> Es existieren aber keine Implementierungen, die soetwas tun würden.

Das stimmt nicht.

> Es wird auch niemals welche geben

Das kannst du gar nicht wissen.

> weil dann sämtlicher Code, der davon
> ausgeht nichtmehr funktionieren würde.

Die Existenz von kaputtem Code belegt gar nichts. Außer natürlich die 
Existenz von unfähigen Programmierern.

Allerdings haben wir hier im Forum ja auch einen Bildungsauftrag. 
Angesichts dessen kann man gar nicht oft genug wiederholen, was einen 
Nullpointer auszeichnet und daß es nur einen korrekten Weg gibt, einen 
Nullpointer zu erzeugen, nämlich indem man das Literal 0 zu einem 
Pointer casted (was der Compiler aber bspw. bei Zuweisungen automatisch 
macht).

: Bearbeitet durch User
von Daniel A. (daniel-a)


Lesenswert?

Es ist immer möglich einnen null Pointer als 0 zu implementieren, es 
wäre höchstens manchmal etwas weniger effizient. Ich hätte nie gedacht, 
dass es tatsächlich c compiler implementationen gibt, die dies nicht 
tun. Ich verliere langsam jegliche Hoffnung für die Menschheit...

Gibt es wenigstens einen Weg um zur compile time zu prüfen, ob ein null 
Pointer 0 ist?

Ich denke an irgendwas in die richtung:
1
static_assert( ((union {void* p; uintptr_t i;}){0}).i != 0, "NULL isn't 0");
Aber das wäre UB, gibt es da irgend eine bessere Methode?

von Axel S. (a-za-z0-9)


Lesenswert?

Daniel A. schrieb:
> Gibt es wenigstens einen Weg um zur compile time zu prüfen, ob ein null
> Pointer 0 ist?

Sicher. Initialisiere einen Pointer mit memset() und vergleiche ihn dann 
mit einem richtigen Nullpointer.
1
char *x;
2
memset(&x, 0, sizeof(x));
3
if (x != (char *)0) {
4
    printf("Nullpointer != 0\n");
5
}

: Bearbeitet durch User
von Daniel A. (daniel-a)


Lesenswert?

Axel S. schrieb:
> Daniel A. schrieb:
>> Gibt es wenigstens einen Weg um zur compile time zu prüfen, ob ein null
>> Pointer 0 ist?
>
> Sicher. Initialisiere einen Pointer mit memset() und vergleiche ihn dann
> mit einem richtigen Nullpointer.

Das wäre aber zur Runtime, nicht zur compile time. Soetwas will ich 
merken, befor ich das Program starte.

von mh (Gast)


Lesenswert?

Die einzelnen Bits testen?

von S. R. (svenska)


Lesenswert?

Daniel A. schrieb:
> Es ist immer möglich einnen null Pointer als 0 zu implementieren, es
> wäre höchstens manchmal etwas weniger effizient.

Es gibt Architekturen, deren Adressraum signed ist und wo "Adresse 
0x0000" genau in der Mitte des Adressraums ist, da wäre die Festlegung 
ziemlich b'schissen.

Der C-Standard ist darauf ausgelegt, auf jeder noch so verkorksten 
Architektur einigermaßen gebrauchbar implementierbar zu sein. Die 
abstrakte Maschine ist deswegen wesentlich abstrakter als bei anderen 
Sprachen.

Daniel A. schrieb:
> Gibt es wenigstens einen Weg um zur compile time zu prüfen, ob ein null
> Pointer 0 ist?

Warum solltest du das tun wollen?

Du weißt, auf welchen Architekturen und mit welchen Compilern dein 
Programm läuft. Und wenn jemand einen Transputer-Compiler auspackt, dann 
ist der Port sein Problem, solange du nicht explizit "tut auch da" 
ranschreibst.

von Daniel A. (daniel-a)


Lesenswert?

S. R. schrieb:
> Daniel A. schrieb:
>> Es ist immer möglich einnen null Pointer als 0 zu implementieren, es
>> wäre höchstens manchmal etwas weniger effizient.
>
> Es gibt Architekturen, deren Adressraum signed ist und wo "Adresse
> 0x0000" genau in der Mitte des Adressraums ist, da wäre die Festlegung
> ziemlich b'schissen.

Ich sehe hier kein grosses Problem. Man muss nur darauf achten, dass 
dynamische Allocationen nicht exact an der Addresse anfangen, und bei 
der ABI des Compilers die Dereferenzierung eines 0 pointers erlauben, 
und dann sollte es eigentlich keine grösseren Probleme geben.

> Daniel A. schrieb:
>> Gibt es wenigstens einen Weg um zur compile time zu prüfen, ob ein null
>> Pointer 0 ist?
>
> Warum solltest du das tun wollen?
>
> Du weißt, auf welchen Architekturen und mit welchen Compilern dein
> Programm läuft. Und wenn jemand einen Transputer-Compiler auspackt, dann
> ist der Port sein Problem, solange du nicht explizit "tut auch da"
> ranschreibst.

Ich schreibe meine Programme immer so, dass diese auf möglichst vielen 
Platformen laufen. Wenn ich doch mal eine Annahme treffe kontrolliere 
ich diese mit einem static_assert. Es spielt für mich keine rolle, wer 
den Code womöglich später portiert oder anderweitig weiterverwendet. 
Aber debugging kann wirklich lästig sein, weshalb ich dem Programmierer 
lieber schon beim Compilieren wissen lassen will, wenn es ein Problem 
gibt und was das Problem ist.

von mh (Gast)


Lesenswert?

Daniel A. schrieb:
> Ich sehe hier kein grosses Problem. Man muss nur darauf achten, dass
> dynamische Allocationen nicht exact an der Addresse anfangen, und bei
> der ABI des Compilers die Dereferenzierung eines 0 pointers erlauben,
> und dann sollte es eigentlich keine grösseren Probleme geben.

Nur ungefähr die halbe c Standard Lib ist nicht mehr nutzbar, da sie 
NULL irgendwo in den Vor- oder Nachbedingungen nutzen.

von S. R. (svenska)


Lesenswert?

Daniel A. schrieb:
>> Es gibt Architekturen, deren Adressraum signed ist und wo "Adresse
>> 0x0000" genau in der Mitte des Adressraums ist, da wäre die Festlegung
>> ziemlich b'schissen.
>
> Ich sehe hier kein grosses Problem. [...]

Für den Assemblerprogrammierer kein Problem (und unnötig), für einen 
Compiler ist deine Forderung real äquivalent zu "die RAM-Zelle an 
Adresse 0 wird nicht benutzt". Damit teilst du den Adressraum mittig in 
zwei Teile, was deutlich unangenehmer ist als ein Nullpointer mit 
gesetzten Bits.

Daniel A. schrieb:
> Ich schreibe meine Programme immer so, dass diese auf möglichst vielen
> Platformen laufen.

Das heißt, du benutzt keinerlei Bibliotheken (außer der 
C-Standardbibliothek) und du benutzt keine Betriebssysteme o.ä.?

Wenn du esoterische Plattformen (seltsame DSPs, komische verteilte 
Systeme der 80er, Mikrocontroller mit getrennten Adressräumen) mit 
deinem Code genauso unterstützen willst wie PCs und Smartphones, dann 
machst du meiner Meinung nach irgendetwas grundsätzlich falsch.

Irgendwo gibt es sinnvolle Grenzen der Portabilität.

von Axel S. (a-za-z0-9)


Lesenswert?

Daniel A. schrieb:

>>> Gibt es wenigstens einen Weg um zur compile time zu prüfen, ob ein null
>>> Pointer 0 ist?
>>
>> Warum solltest du das tun wollen?
>
> Ich schreibe meine Programme immer so, dass diese auf möglichst vielen
> Platformen laufen.

Dann ist das doch ganz unnötig. Verwende einfach den standardkonformen 
Weg, einen Nullpointer zu initialisieren. Dann ist es 100%ig portabel.

von Daniel A. (daniel-a)


Lesenswert?

S. R. schrieb:
> Daniel A. schrieb:
>> Ich schreibe meine Programme immer so, dass diese auf möglichst vielen
>> Platformen laufen.
>
> Das heißt, du benutzt keinerlei Bibliotheken (außer der
> C-Standardbibliothek) und du benutzt keine Betriebssysteme o.ä.?

Es kommt darauf an, was ich Programmiere. Bei PC Programmen setze ich 
mindestens das vorhandensein eines OS und der meisten POSIX funktionen 
voraus. Fremde Libraries versuche ich möglichst selten zu verwenden, nur 
wenn es etwas aufwendigeres wie ein Parser oder etwas weit verbreitetes 
oder etwas dessen Code überschaubar ist ist verwende ich libraries.

Wenn ich ein uC Programm erstelle halte ich es modular genug, damit ich 
es auch auf dem PC nativ laufen und debuggen lassen kann. In dem fall 
verwende ich keine fremden libraries. Aber bei so exotischen dingen wie 
7bit Systemen ziehe ich eine grenze, das ist den Aufwand einfach nicht 
wert.

Axel S. schrieb:
>> Ich schreibe meine Programme immer so, dass diese auf möglichst vielen
>> Platformen laufen.
>
> Dann ist das doch ganz unnötig. Verwende einfach den standardkonformen
> Weg, einen Nullpointer zu initialisieren. Dann ist es 100%ig portabel.

Dann müsste ich bei sämtlichen meiner Programme nochmal die Sourcen 
durchgehen und alle Stellen finden, an denen ich diese Annahme getroffen 
habe, das wäre ziemlich aufwendig. Die Programme müssen ja nicht auf 
allen Platformen laufen, nur auf möglichst vielen. Bei meinen PC 
Programmen bin ich mir nicht sicher ob die üblichen Betriebssysteme 
überhaupt auf Systemen funktionieren würden dessen Kompiler solch 
exotische null pointer haben, deshalb erscheint mir dort ein 
static_assert sinvoller als alle sourcen anzupassen.

von Rolf M. (rmagnus)


Lesenswert?

Daniel A. schrieb:
>> Es gibt Architekturen, deren Adressraum signed ist und wo "Adresse
>> 0x0000" genau in der Mitte des Adressraums ist, da wäre die Festlegung
>> ziemlich b'schissen.
>
> Ich sehe hier kein grosses Problem. Man muss nur darauf achten, dass
> dynamische Allocationen nicht exact an der Addresse anfangen,

Sie dürfen nicht nur nicht exakt dort anfangen, sondern diese Adresse 
muss komplett außerhalb des allokierten Blocks liegen. Dieser muss also 
entweder vor dieser Adresse aufhören oder danach anfangen.
Es ist vor allem bei der Allokation von großen Objekten ziemlich blöd, 
wenn mitten im Adressraum ein Byte ist, das nicht genutzt werden darf.

> und bei der ABI des Compilers die Dereferenzierung eines 0 pointers
> erlauben, und dann sollte es eigentlich keine grösseren Probleme geben.

Dann müsste das ABI aber auch die C-Regel brechen, dass ein Vergleich 
der Adresse eines gültigen Objekts mit einem NULL-Zeiger niemals 
Gleichheit ergeben darf.

Daniel A. schrieb:
> Aber bei so exotischen dingen wie
> 7bit Systemen ziehe ich eine grenze, das ist den Aufwand einfach nicht
> wert.

C garantiert dir, dass char mindestens 8 Bit breit ist. Auf einem 
7-Bit-System würde ein Compiler-Hersteller char dann wohl 14 Bit breit 
machen. Wäre aber mal interessant, wieviel Code auf so einem System noch 
funktioniert.

von S. R. (svenska)


Lesenswert?

Daniel A. schrieb:
> Bei PC Programmen setze ich mindestens das vorhandensein
> eines OS und der meisten POSIX funktionen voraus.

Dort ist der Wert eines Nullpointers immer "kein Bit gesetzt" und 
CHAR_BITS ist 8. Kannst du dich drauf verlassen.

Daniel A. schrieb:
> Aber bei so exotischen dingen wie 7bit Systemen ziehe ich
> eine grenze, das ist den Aufwand einfach nicht wert.

Dann darfst du einfach annehmen, dass der Wert eines Nullpointers "kein 
Bit gesetzt" ist. Abweichende Systeme sind Exoten.

Daniel A. schrieb:
> Die Programme müssen ja nicht auf
> allen Platformen laufen, nur auf möglichst vielen.

Dann darfst du einfach annehmen, dass der Wert eines Nullpointers "kein 
Bit gesetzt" ist. Abweichende Systeme sind extrem selten und eher 
historisch.

Falls du wirklich einmal in die Verlegenheit kommen solltest, deinen 
Code auf einer extrem ungewöhnlichen Plattform einsetzen zu wollen, 
kannst du ihn ja gezielt darauf geradeziehen. Ungewöhnliche Nullpointer 
sind dann aber vermutlich dein geringstes Problem.

Daniel A. schrieb:
> Bei meinen PC Programmen bin ich mir nicht sicher ob die üblichen
> Betriebssysteme überhaupt auf Systemen funktionieren würden dessen
> Kompiler solch exotische null pointer haben,

Es existieren keine solchen Compiler und für heute übliche Architekturen 
wird es wahrscheinlich auch nie solche Compiler geben. Es existiert 
schlicht keine Notwendigkeit dafür (außerdem gibt es MMUs).

von Possetitjel (Gast)


Lesenswert?

Dirk B. schrieb:

> An Adrese 0 kann durchaus etwas Sinnvolles stehen.
> Z.B. Resetvektoren oder Register.
>
> Aber für den normalen Nutzer eher nicht. Darum hat man
> diese Adresse genommen
> NULL bedeutet lediglich "ungültige Adresse"
> (einen Wert musste man halt nehmen)

Muss man nicht.

Man hätte Referenzen auch als Paare [Startadr; (Endadr+1)]
definieren können; auf diese Art könnte man den gesamten
Speicher adressieren und hätte gleichzeitig die Möglichkeit
zur Bereichsprüfung.
NIL-Referenzen wären dann einfach solche mit
"Startadr >= Endadr".


Ich dachte in meiner Jugend, bei Programmiersprachen ginge
es primär um Klarheit im Ausdruck, Kompaktheit, Lesbarkeit,
Vermeidung von Fehlern.
Die völlig unsinnigen Diskussionen um Wahrheitswerte und
NULL-Pointer zeigen mir aber zweierlei:
1. S/M ist in der Mitte der Gesellschaft angekommen.
2. Das Erlernen der Programmiersprache "C"  ist ein
   Initiationsritual, das einen in die Gemeinschaft der
   "richtigen" Programmierer aufnimmt.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Daniel A. schrieb:
> Axel S. schrieb:
>> Daniel A. schrieb:
>>> Gibt es wenigstens einen Weg um zur compile time zu prüfen, ob ein null
>>> Pointer 0 ist?
>>
>> Sicher. Initialisiere einen Pointer mit memset() und vergleiche ihn dann
>> mit einem richtigen Nullpointer.
>
> Das wäre aber zur Runtime, nicht zur compile time. Soetwas will ich
> merken, befor ich das Program starte.

Das willst du am besten bereits wissen, bevor du die erste Zeile Code 
schreibst (wenn der Code denn so un-portierbar ist, dass er eine 
bestimmte Binärdarstellung von NULL erwartet).

--> ABI lesen.

von Dirk B. (dirkb2)


Lesenswert?

Rolf M. schrieb:
> C garantiert dir, dass char mindestens 8 Bit breit ist. Auf einem
> 7-Bit-System würde ein Compiler-Hersteller char dann wohl 14 Bit breit
> machen. Wäre aber mal interessant, wieviel Code auf so einem System noch
> funktioniert.

Seit C99 immer weniger.
Denn wenn viele meinen uint8_t wäre super, fallen sie dann auf die 
Schnauze.
uint_least8_t oder sogar uint_fast8_t wären öfter angebracht.

von Cyblord -. (cyblord)


Lesenswert?

Dirk B. schrieb:
> Seit C99 immer weniger.
> Denn wenn viele meinen uint8_t wäre super, fallen sie dann auf die
> Schnauze.

Wenn das System keinen 8 Bit Datentyp bietet, darf uint8_t gar nirgends 
definiert sein und damit kompiliert das gar nicht erst. Dann ist es 
zumindest direkt klar dass der Code nicht läuft ohne dass es zur 
Laufzeit komische Fehler gibt.

von Axel S. (a-za-z0-9)


Lesenswert?

Possetitjel schrieb:
[merkwürdiges]

Das Hauptproblem liegt IMHO nicht in den Eigenheiten von C, sondern 
darin, daß alle Welt C für eine geeignete Sprache zur Anwendungs- 
programmierung hält. Dafür war C aber nie gedacht und ist folgerichtig 
auch nicht sonderlich geeignet. Die Abwesenheit eines String-Datentyps 
genauso wie nackte Pointer oder fehlende Bereichsprüfung bei z.B. 
Array-Zugriffen belegen deutlich, daß bei C der Fokus auf Maschinennähe 
und Performanz liegt. Die beste Beschreibung von C ist IMNSHO "portabler 
Assembler".

von Cyblord -. (cyblord)


Lesenswert?

Axel S. schrieb:
> Das Hauptproblem liegt IMHO nicht in den Eigenheiten von C, sondern
> darin, daß alle Welt C für eine geeignete Sprache zur Anwendungs-
> programmierung hält.

Ist das so? Wo kommt denn heute noch C bei Anwendungsentwicklung vor?

> Dafür war C aber nie gedacht und ist folgerichtig
> auch nicht sonderlich geeignet. Die Abwesenheit eines String-Datentyps
> genauso wie nackte Pointer oder fehlende Bereichsprüfung bei z.B.
> Array-Zugriffen belegen deutlich, daß bei C der Fokus auf Maschinennähe
> und Performanz liegt. Die beste Beschreibung von C ist IMNSHO "portabler
> Assembler".

Darum ist es ja auch für Embedded Zeug so gut geeignet und wird nicht 
umsonst hier extrem oft verwendet.

von Axel S. (a-za-z0-9)


Lesenswert?

Daniel A. schrieb:
> Axel S. schrieb:

>> Dann ist das doch ganz unnötig. Verwende einfach den standardkonformen
>> Weg, einen Nullpointer zu initialisieren. Dann ist es 100%ig portabel.
>
> Dann müsste ich bei sämtlichen meiner Programme nochmal die Sourcen
> durchgehen und alle Stellen finden, an denen ich diese Annahme getroffen
> habe, das wäre ziemlich aufwendig.

Das mußt du nicht. Verbuche das einfach als Jugendsünde und schreib 
deine zukünftige Software so, daß sie sich an standardkonformem 
Verhalten des Compilers orientiert und nicht auf implementation 
defined behavior setzen muß. Es gibt gute Gründe, hier und da ein 
bestimmtes IDB auszunutzen. Aber man muß sich halt im klaren sein, daß 
der Code dann nicht mehr portabel ist.

Allerdings würde ich gerade Nullpointer nicht in diese Kategorie 
sortieren. Zumindest so lange man nicht mit inline Assembler hantiert. 
Ein portabler Nullpointer-Test in Assembler wird sicher unerfreulich.

von Nop (Gast)


Lesenswert?

Possetitjel schrieb:

> Man hätte Referenzen auch als Paare [Startadr; (Endadr+1)]
> definieren können

Hätte man, wenn man extra langsame Systeme als Ergebnis gewollt hätte. 
Was man Dir in Deiner Jugend nicht erzählt hat: Performance zählt auch.

von Possetitjel (Gast)


Lesenswert?

Axel S. schrieb:

> Possetitjel schrieb:
> [merkwürdiges]

Ich hätte davon nicht anfangen sollen; konnte mich halt
nur nicht beherrschen. Ich gelobe Besserung.

> Das Hauptproblem liegt IMHO nicht in den Eigenheiten
> von C, sondern darin, daß alle Welt C für eine geeignete
> Sprache zur Anwendungsprogrammierung hält.

Dieses "Missverständnis" wird aber dadurch aktiv gefördert,
dass im Vorwort der mir vorliegenden Ausgabe des K&R
drei Mal betont wird, C eigne sich "für einen breiten
Anwendungsbereich".

Genau betrachtet ist das gelogen -- C eignet sich NUR
für maschinennahes Zeug, das portabel sein soll.

Das sollte man -- wenigstens heutzutage -- auch so
verkaufen.
(Dass K&R vor 50 Jahren anderer Meinung waren, laste ich
ihnen gar nicht an. Wir wissen es heute aber besser.)

> Die beste Beschreibung von C ist IMNSHO "portabler
> Assembler".

Ja, sicher. - Das ist aber gar nicht mein Punkt: C ignoriert
den Unterschied zwischen der Bedeutung eines Datenelementes
einerseits und seiner Codierung andererseits.
Wie krank ist denn eine Sprache, die zwar boolesche Ausdrücke
auswerten kann, aber keinen eigenen Datentyp hat, um die
Ergebnisse zu speichern?!

von Axel S. (a-za-z0-9)


Lesenswert?

Possetitjel schrieb:
> Wie krank ist denn eine Sprache, die zwar boolesche Ausdrücke
> auswerten kann, aber keinen eigenen Datentyp hat, um die
> Ergebnisse zu speichern?!

Das folgt einfach der üblichen Implementierung des Z (Zero) Flags in der 
ALU. Z gesetzt = false, sonst true. Fertig. Wie gesagt: Effizienz zählt.

von Possetitjel (Gast)


Lesenswert?

Nop schrieb:

> Was man Dir in Deiner Jugend nicht erzählt hat:

Vielleicht beschränkst Du Deine Urteile zukünftig auf
Deine eigene Jugend; da liegst Du hoffentlich weniger
oft daneben.


> Performance zählt auch.

Das hat man mir leider ein paar Mal zu oft erzählt.
Überzeugendster Ausdruck dieser Strömung sind die
Toten in der Silvesternacht 1999 in Berlin.

Inzwischen weiss man: Verfrühte Mikrooptimierung ist
die Wurzel alles Bösen.

von Possetitjel (Gast)


Lesenswert?

Axel S. schrieb:

> Possetitjel schrieb:
>> Wie krank ist denn eine Sprache, die zwar boolesche
>> Ausdrücke auswerten kann, aber keinen eigenen Datentyp
>> hat, um die Ergebnisse zu speichern?!
>
> Das folgt einfach der üblichen Implementierung des Z (Zero)
> Flags in der ALU. Z gesetzt = false, sonst true. Fertig.


Nein!
Eben gerade nicht!

Das Zero-Flag ist KEINE Ganzzahl in irgend einem Register.

Vielen Dank für dieses wunderbare Argument; das ist mir noch
gar nicht aufgefallen: C fällt in diesem Punkt sogar hinter
den Assembler zurück!


> Wie gesagt: Effizienz zählt.

Das hat nichts mit Effizienz zu tun, das ist einfach
Stumpfsinn.

von Daniel A. (daniel-a)


Lesenswert?

Possetitjel schrieb:
> Wie krank ist denn eine Sprache, die zwar boolesche Ausdrücke
> auswerten kann, aber keinen eigenen Datentyp hat, um die
> Ergebnisse zu speichern?!

C hat einen Datentyp dafür: _Bool. Ein normaler arithmetischer Datentyp, 
mit der Eigenheit, dass beim Abfragen dessen wertes immer 0 oder 1 
herauskommt.

von Carl D. (jcw2)


Lesenswert?

Possetitjel schrieb:
> Man hätte Referenzen auch als Paare [Startadr; (Endadr+1)]
> definieren können; auf diese Art könnte man den gesamten
> Speicher adressieren und hätte gleichzeitig die Möglichkeit
> zur Bereichsprüfung.
> NIL-Referenzen wären dann einfach solche mit
> "Startadr >= Endadr".

Coole Idee. Und wie zeigt man auf eine einzelne Speicherzelle (ob nun 
7,8,12 oder 16 mal dahingestellt)?
Ein Glück daß Designer und Implemetierer von C mit einem Mindestvorat an 
Verstand zur Welt kamen.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Possetitjel schrieb:
> Inzwischen weiss man: Verfrühte Mikrooptimierung ist
> die Wurzel alles Bösen.

Klaro. Inzwischen weiß man sogar, dass sich der IS nur aufgrund 
verfrühter Mikrooptimierung gebildet hat.

von Possetitjel (Gast)


Lesenswert?

Carl D. schrieb:

> Possetitjel schrieb:
>> Man hätte Referenzen auch als Paare [Startadr; (Endadr+1)]
>> definieren können; auf diese Art könnte man den gesamten
>> Speicher adressieren und hätte gleichzeitig die Möglichkeit
>> zur Bereichsprüfung.
>> NIL-Referenzen wären dann einfach solche mit
>> "Startadr >= Endadr".
>
> Coole Idee. Und wie zeigt man auf eine einzelne Speicherzelle
> (ob nun 7,8,12 oder 16 mal dahingestellt)?

Hä?! Das meinst Du nicht ernst?!

Die Referenz (7,8) zeigt auf die einzelne Speicherzelle mit
der Adresse 7. Wo ist das Problem?

Die Referenz (0,1) würde auf Adresse 0 zeigen.
Die Referenz (7,7) wäre NIL (genauso wie (0,0) oder (14,12)).

Einziger Nachteil: Die "letzte" Adresse lässt sich nicht
einzeln adressieren, sondern nur als Bestandteil einer
größeren Datenstruktur, weil sich der Nachfolger nicht
bilden lässt.

von Possetitjel (Gast)


Lesenswert?

Johann L. schrieb:

> Possetitjel schrieb:
>> Inzwischen weiss man: Verfrühte Mikrooptimierung ist
>> die Wurzel alles Bösen.
>
> Klaro. Inzwischen weiß man sogar, dass sich der IS nur
> aufgrund verfrühter Mikrooptimierung gebildet hat.

Vermutlich stimmt das sogar -- "America first" ist ja
auch nur eine Spielart der Mikrooptimierung.

Aber das gehört nich hierher.

von Possetitjel (Gast)


Lesenswert?

Daniel A. schrieb:

> Possetitjel schrieb:
>> Wie krank ist denn eine Sprache, die zwar boolesche
>> Ausdrücke auswerten kann, aber keinen eigenen Datentyp
>> hat, um die Ergebnisse zu speichern?!
>
> C hat einen Datentyp dafür: _Bool.

Ja, okay. Danke für die Korrektur.

27 Jahre nach Erfindung der Sprache kommt doch langsam die
Idee auf, dass es außer Zahlen vielleicht DOCH noch andere
interessante Datentypen geben könnte.

> Ein normaler arithmetischer Datentyp,

Das bedeutet, ich kann mit einer booleschen Variablen x
problemlos x*x-x ausrechnen?

> mit der Eigenheit, dass beim Abfragen dessen wertes
> immer 0 oder 1 herauskommt.

Naja. Besser als nix, zugegeben.

von Daniel A. (daniel-a)


Lesenswert?

Possetitjel schrieb:
> Das bedeutet, ich kann mit einer booleschen Variablen x
> problemlos x*x-x ausrechnen?

Ja, aber du könntest auch gleich 0 hinschreiben, ist lesbarer.

von Axel S. (a-za-z0-9)


Lesenswert?

Possetitjel schrieb:
> Axel S. schrieb:
>> Possetitjel schrieb:
>>> Wie krank ist denn eine Sprache, die zwar boolesche
>>> Ausdrücke auswerten kann, aber keinen eigenen Datentyp
>>> hat, um die Ergebnisse zu speichern?!
>>
>> Das folgt einfach der üblichen Implementierung des Z (Zero)
>> Flags in der ALU. Z gesetzt = false, sonst true. Fertig.
>
> Nein!
> Eben gerade nicht!
>
> Das Zero-Flag ist KEINE Ganzzahl in irgend einem Register.

Das hst du auch nicht begriffen?

Das Zeroflag ist ein einzelnes Bit - genau das was man zum Speichern 
einer booleschen Variable minimal braucht. Und die Regeln nach denen die 
ALU das Zeroflag setzt, sind die gleichen nach denen C den booleschen 
Wert einer Variable bestimmt (bis auf die Negation). Deswegen ist das in 
C effizient, weil es direkt auf eine ALU-Operation gemapt werden kann.

von Nop (Gast)


Lesenswert?

Possetitjel schrieb:

> Vielleicht beschränkst Du Deine Urteile zukünftig auf
> Deine eigene Jugend; da liegst Du hoffentlich weniger
> oft daneben.

Bei dem Unsinn, den man Dir da in Deiner Jugend erzählt haben soll..

C hat übrigens in der Tat einen breiten Anwendungsbereich. Nicht nur 
Mikrocontroller, sondern Linux ist auch in C.

Oh, und Git als Anwendung. Das ist bewußt in C gemacht worden, weil man 
da wirklich auf Byte-Level programmiert hat. Der Performance wegen, die 
bei einem Projekt mit über 1000 Entwicklern weltweit durchaus wichtig 
ist.

Da man häufig Pointer auf Gültigkeit prüft, ergibt es Sinn, den Wert 0 
zu wählen, weil ein Test auf Gleichheit oder Ungleichheit mit 0 auf den 
meisten Architekturen schneller ist als gegen andere Werte.

Und erst recht als ein Test von zwei verschiedenen Werten nach Deinem 
Vorschlag, besonders auf Architekturen mit wenig Registern.

> Überzeugendster Ausdruck dieser Strömung sind die
> Toten in der Silvesternacht 1999 in Berlin.

Mangelhafte Doku und unklare Zuständigkeiten haben mit Performance 
nichts zu tun.

von Possetitjel (Gast)


Lesenswert?

Nop schrieb:

> C hat übrigens in der Tat einen breiten Anwendungsbereich.

Natürlich. Ein Faustkeil auch.


> Da man häufig Pointer auf Gültigkeit prüft, ergibt es Sinn,
> den Wert 0 zu wählen, weil ein Test auf Gleichheit oder
> Ungleichheit mit 0 auf den meisten Architekturen schneller
> ist als gegen andere Werte.

Es geht nicht darum, welchen Wert man wählt, um eine bestimmte
Bedingung zu codieren -- es geht darum, dass es offenbar eine
große Fraktion von Leuten gibt, denen nicht zu vermitteln ist,
dass ein NIL-Pointer (ungültige Speicherreferenz) von der
Bedeutung her etwas anderes ist als ein Pointer auf Speicher-
adresse 0.

Es ist eine Schwäche von C, dass hier das eine mit dem anderen
zusammenfällt -- und weder eine technischen Notwendigkeit, noch
eine tolle Errungenschaft.

> Und erst recht als ein Test von zwei verschiedenen Werten
> nach Deinem Vorschlag, besonders auf Architekturen mit wenig
> Registern.

Mein Vorschlag war sowieso schlecht: Man sollte [Startadr, Länge]
wählen.
Ein NIL-Pointer ist dann ganz einfach an Länge=0 zu erkennen.


>> Überzeugendster Ausdruck dieser Strömung sind die
>> Toten in der Silvesternacht 1999 in Berlin.
>
> Mangelhafte Doku und unklare Zuständigkeiten haben mit
> Performance nichts zu tun.

Du vergisst, dass nichts davon zum Tragen gekommen wäre,
wenn man beim Erstellen der Software nicht Performance über
Zuverlässigkeit gestellt hätte.

Insofern: Doch, ja, das hat damit zu tun.

von Possetitjel (Gast)


Lesenswert?

Axel S. schrieb:

> Das hst du auch nicht begriffen?

Axel, wenn Du meine Argumente schon nicht begreifst,
dann verzichte doch wenigens auf Deine Arroganz.

Du hast mir ein sehr gutes Argument geliefert, was eine
Schwäche von C als "portablem Assembler" aufzeigt. Wenn
Du Dein eigenes Argument nicht verstehst, dann kann ich
damit sehr gut leben. Ich danke Dir trotzdem.

von Possetitjel (Gast)


Lesenswert?

Daniel A. schrieb:

> Possetitjel schrieb:
>> Das bedeutet, ich kann mit einer booleschen Variablen x
>> problemlos x*x-x ausrechnen?
>
> Ja, aber du könntest auch gleich 0 hinschreiben, ist
> lesbarer.

Hihi... ja, gut, meine Frage war ungünstig formuliert.

Es ging mir darum, ob ich Deine Bemerkung "normaler
arithmetischer Datentyp" richtig verstanden habe,
denn darunter würde ich verstehen, dass ich alle
arithmetischen Grundoperationen anwenden und auch Werte
größer 1 zuweisen kann -- nur beim Lesen der Variablen
kommt halt nur 0 oder 1 raus.

von Nop (Gast)


Lesenswert?

Possetitjel schrieb:

> Es ist eine Schwäche von C, dass hier das eine mit dem anderen
> zusammenfällt -- und weder eine technischen Notwendigkeit, noch
> eine tolle Errungenschaft.

Klar kann man Nil-Pointer anders realisieren. Mit entsprechend höherem 
Registerdruck, wie ich schon sagte. Das scheint Dir egal zu sein - C ist 
aber von und für Leute, denen das nicht egal ist.

Nimm doch einfach Ada.

> Du vergisst, dass nichts davon zum Tragen gekommen wäre,
> wenn man beim Erstellen der Software nicht Performance über
> Zuverlässigkeit gestellt hätte.

Und? Das belegt immer noch gar nichts. Grundlagen der Aussagelogik 
beachten.

von Possetitjel (Gast)


Lesenswert?

Nop schrieb:

> Possetitjel schrieb:
>
>> Es ist eine Schwäche von C, dass hier das eine mit dem
>> anderen zusammenfällt -- und weder eine technischen
>> Notwendigkeit, noch eine tolle Errungenschaft.
>
> Klar kann man Nil-Pointer anders realisieren. Mit
> entsprechend höherem Registerdruck, wie ich schon sagte.

Mag sein, ja.
Man sollte aber vielleicht beachten, dass x86 hier nicht
das Maß aller Dinge ist.


> Das scheint Dir egal zu sein -

Nein. Dass ist Deine (etwas übertriebene) Interpretation
meiner Worte.

Dass mir Korrektheit und Zuverlässigkeit wichtiger sind
als die letzten 20% Performance, heißt nicht, dass mir
die Performance völlig egal wäre.

In Zeichen: Aus K > P folgt nicht P=0 :)

Die Performance ist bei der nächsten Rechnergeneration ohnehin
50% größer, ohne dass ich dafür etwas tun muss -- aber die
Fehler im Programm bleiben, wie sie sind.

> C ist aber von und für Leute, denen das nicht egal ist.

Leider nicht nur -- denn ganz ehrlich: Welche Alternative
hat man? (Beispielsweise für µC-Programmierung)


> Nimm doch einfach Ada.

Oberon wäre wohl naheliegender.


>> Du vergisst, dass nichts davon zum Tragen gekommen wäre,
>> wenn man beim Erstellen der Software nicht Performance
>> über Zuverlässigkeit gestellt hätte.
>
> Und? Das belegt immer noch gar nichts.

Doch.

Die Grundlage für jegliche Bemühung um technische Zuverlässig-
keit liegt in der Erkenntnis, dass es genügt, EINE EINZIGE
notwendige Bedingung für die Katastrophe nicht zu erfüllen.

> Grundlagen der Aussagelogik beachten.

Done.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Nop schrieb:
> Nimm doch einfach Ada.

Geht nicht, denn darauf lässt sich nicht so einfach schimpfen.

Dass C eine uralt-Sprache mit Schwächen ist steht außer Frage, denk ich. 
Bei allen "Argumenten" gegen C, sollte man aber immer folgenden im Blick 
haben.

Erstens: In jeder Sprache lassen sich ungültige Programme formulieren. 
In manchen einfacher und intuitiver, in anderen hat man andere 
Möglichkeiten.

Zweitens: Wenn man eine Sprache nicht berherrscht, hat man schlechte 
Karten.  Egal wie die Sprache heißt.  Wenn es — mal wieder — um C und 
Undefined Behaviour geht — oder Implementation Defined wie oben —, dann 
ist das ein treffliches Vehikel, um auf C zu schimpfen. Man kann aber 
auch den Standpunkt einnehmen, dass derjenige, der sowas nicht weiß, die 
Sprache einfach nicht beherrscht und sein Handwerkszeug nicht kennt.

Das soll jetzte kein Rant auf Software-Entwickler sein, denn oft genug 
haben diese garnicht die Zeit, sich in die Grundlagen einzuarbeiten und 
kommen wie die Jungfrau zum Kind:  Jemand macht ne Harware, und Software 
gehört "einfach dazu", sind ja nur ein paar Zeilen die da zu tippsen 
sind, kann jedes Script-Kiddie.  Abgabe ist am besten gestern.

In der Not wird das fehlende Fundament dann gerne durch das 
Design-Pattern "Trial & Error" ausgebügelt:  Code getippt, durch den 
Simulator / Debugger / Testboard genudelt oder gar generierten Binärcode 
studiert Binary wie erwartet => Code ist valide.

Fehler sind so vorprogrammiert, einhergehend entsprechender Frust und 
Schaden bis hin zu "Allergie gegen C" wie in einem Thread neulich.

Und der Rat "Nimm C++ statt C" ist in einer solchen Situation einfach 
irrsinnig, denn: C++ ist i.w. eine Obermenge von C, und wenn man C nicht 
solide kann und deshalb den Hals auf C hat, wird man C++ nimmermehr 
lernen. C++ ist um Größenordnungen komplexer, es ist ein Monstrum.  Wer 
das Konzept von UB in C nicht blickt, soll das Zusammenspiel von 
Templates mit Namespaces mit Mehrfachvererbung mit Lookup mit Bindings 
mit Overloads mit Resolutions mit XXX sicher beherrschen, sämtliche 
Spielarten von "ill formed, no warning required" umschiffen, dabei 
effizienten und wartbaren Code in endlicher Zeit produzieren können?

Jo klar.

von Nop (Gast)


Lesenswert?

Possetitjel schrieb:

> Man sollte aber vielleicht beachten, dass x86 hier nicht
> das Maß aller Dinge ist.

Ich schrieb nichts von x86.

> Dass mir Korrektheit und Zuverlässigkeit wichtiger sind
> als die letzten 20% Performance

Na dann mach doch einfach in C ein Typedef und ein paar Defines, schon 
kannste Deine Vorstellungen auch in C umsetzen. Wenn Du hingegen an 
einer Sprache interessiert bist, die Dich zu so etwas zwingt, bist Du 
bei C grundverkehrt.

> Die Performance ist bei der nächsten Rechnergeneration ohnehin
> 50% größer, ohne dass ich dafür etwas tun muss

Du bist hier in einem Forum für Mikrocontroller. Paar hunderttausend mal 
einen M0 statt eines M4, das ist merkbar. Von batteriebetriebenen 
Anwendungen mal ganz zu schweigen. x86 ist nicht das Maß aller Dinge. 
;-)

> Leider nicht nur -- denn ganz ehrlich: Welche Alternative
> hat man? (Beispielsweise für µC-Programmierung)

Du mußt Dich schon entscheiden, ob Du hardwarenah oder auf sehr 
abstraktem Niveau arbeiten willst. Sonst nimm halt statt eines M0 
einfach einen Raspi und laß Ada drauf los. Rechenkraft fällt schließlich 
vom Himmel.

Wer in C programmiert, denkt in portablem Assembler. Das war übrigens 
bei Git einer der Hauptgründe für C, denn Git als Anwendung ist ja keine 
Systemprogrammierung mehr. Wer nicht vorhat, in portablem Assembler zu 
denken, kann andere Sprachen wählen.

>> Nimm doch einfach Ada.
>
> Oberon wäre wohl naheliegender.

Warum, Ada ist eigens für genau Deine Bedürfnisse entworfen worden.

>> Und? Das belegt immer noch gar nichts.
>
> Doch.

Nein. Daß es Fälle gibt, in denen man zuviel Speicher gespart hat, heißt 
nicht, daß Speichersparen verkehrt oder nicht erstrebenswert wäre.

von Possetitjel (Gast)


Lesenswert?

Johann L. schrieb:

> Geht nicht, denn darauf lässt sich nicht so einfach
> schimpfen.

Das hat einen Unterton, der mir dann doch nicht gefällt.
Ich muss C gottseidank nicht benutzen; ich bin, je nach
Einzelfall, mit FreePascal oder Tcl/Tk gut bedient.

Ich habe also kein Motiv, auf C zu schimpfen.

> Dass C eine uralt-Sprache mit Schwächen ist steht außer
> Frage, denk ich.

Das sehe ich deutlich anders.

Nach meinem Eindruck sind die Leute, die gleichzeitig gut in
C programmieren können UND nüchtern über die Schwächen von
C reden können, SEHR selten.

(Zugestanden: Auf µC.net ist die Dichte etwas höher.)

> Erstens: In jeder Sprache lassen sich ungültige Programme
> formulieren.

Klar. Steht außer Frage.


> Zweitens: Wenn man eine Sprache nicht berherrscht, hat man
> schlechte Karten.

Du überspringst die vorgelagerte Hauptfrage: Warum sollte ich
mir die Quälerei antun, diese Sprache zu lernen (außer aus
nackter Not, weil es nix anderes gibt)?

Auf diese Frage bekommt man i.d.R. keine sachliche Antwort.

> Egal wie die Sprache heißt.  Wenn es — mal wieder — um C
> und Undefined Behaviour geht — oder Implementation Defined
> wie oben —, dann ist das ein treffliches Vehikel, um auf C
> zu schimpfen.

Das will ich eigentlich gar nicht. Ich bin lediglich nicht
bereit, die (aus meiner Sicht zahlreichen, kruden) Eigenarten
von C als gottgegeben hinzunehmen -- oder mir gar als die
Erlösung verkaufen zu lassen.
Ich möchte - unabhängig davon, ob ich die Festlegung gut
oder schlecht finde - verstehen, WARUM dieses oder jenes
genau so festgelegt wurde.
(Der Verweis auf die Originalliteratur hilft nicht weiter; ich
habe eine deutsche Ausgabe des K&R zur Verfügung. Informativ,
aber ziemlich zähe Lektüre.)

Negatives Beispiel:
Wenn mir weiter oben erzählt werden sollte, dass ich blöd
bin, weil ich nicht begreife, dass boolesche Datentypen
sowieso überflüssig sind, weil die CPU ja auch keinen
booleschen Datentyp hat, dann hat sich der Gesprächspartner
disqualifiziert. Ich weiss, was die CPU macht; ich habe schon
Assembler programmiert.

Positives Beispiel:
Eine der vielen Krankheiten bei C finde ich die Syntax der
for-Schleife. Vor einer Weile ist dieser Punkt mal diskutiert
worden, und A.K. hat erklärt, dass das eine 1:1-Umsetzung
bestimmter Assemblerkonstrukte ist.
Ich finde die Syntax immer noch furchtbar, aber ich rege
mich nicht mehr darüber auf, weil ich jetzt weiss, dass ich
dieses Thema in 99% aller Fälle getrost ignorieren kann. Da
steckt überhaupt keine grandiose Geheimwissenschaft dahinter,
sondern das ist lediglich (wieder mal) eine Kurzschreibweise
für ein Konstrukt, auf das ich gern verzichten kann.

(Kann ich wirklich. tcl kennt "foreach" und "while", mehr
brauche ich nicht.)

> Man kann aber auch den Standpunkt einnehmen, dass derjenige,
> der sowas nicht weiß, die Sprache einfach nicht beherrscht
> und sein Handwerkszeug nicht kennt.

Ja! Sicher! Das ist doch gar nicht das Thema! Ich weiss ,
dass ich C nicht beherrsche.

Das Thema ist: Warum sollte es sich lohnen, die Qual auf sich
zu nehmen, diese Sprache zu erlernen?

Die übliche patzige Antwort "Dann lass es halt bleiben!" ist
eben nur das: Eine patzige Antwort ohne Nutzwert.

Ich habe in den vergangenen 30 Jahre ungefähr 3 Mal Anlauf
genommen, C zu lernen, aber das ist jedes Mal steckengeblieben,
weil ich mich nicht überwinden konnte, mir diesen (aus meiner
laienhaften Sicht) kruden Scheiss einzutrichtern.

Ich will also überhaupt nicht schimpfen; ich suche gute (!)
Argumente dafür, die Sprache zu lernen. Was also ist an der
Sprache so toll?

Die guten Argumente bekommt man nur üblicherweise nicht --
sondern Pöbeleien, dass man halt einfach zu blöd sei, die tiefe
Weisheit und Erleuchtung zu verstehen.
Liebe Leute, dass ich die C-Syntax furchtbar finde, ist KEIN
zwingender Beweis dafür, dass ich geistig minderbemittelt bin!

von Nop (Gast)


Lesenswert?

Possetitjel schrieb:

> Das Thema ist: Warum sollte es sich lohnen, die Qual auf sich
> zu nehmen, diese Sprache zu erlernen?

Weil man damit sehr hardwarenah programmieren kann, ohne deswegen 
Assembler nehmen zu müssen - der wäre nicht portabel und auf jeder CPU 
wieder sehr deutlich anders.

C wurde für diesen Anwendungsfall entworfen, und das ist auch der Grund, 
wieso es einiges an Unschärfe da gibt. Nehmen wir mal einen Shift eines 
32-bit Integers um 32 Bit, der ist in C nicht definiert.

Das kommt daher, daß unter x86-32 der Opcode für den Shiftbefehl nur 5 
Bit für die Shift-Distanz hat, das reicht ja. Wenn man nun eine 1 um 32 
Bit shiften will, dann sind die unteren 5 Bit 0, folglich wird gar nicht 
geshiftet, und es kommt 1 heraus. Auf Power-PC ist das hingegen anders, 
und das Ergebnis ist 0.

Wollte man das nun plattformübergreifend definieren, dann müßte man je 
nach Plattform mit Checks arbeiten, die wiederum die Performance in den 
Keller ziehen würden.

Es gibt so manches, was in C erstmal absurd aussieht, was aber durchaus 
seinen Grund hat, und zwar oftmals eben aus Hardware-Gründen. Das ist 
bei einer Sprache, die so dicht an die Hardware geht und gehen soll, 
nicht zu vermeiden.

Wer da natürlich herangeht und eine ideale abstrakte Maschine erwartet, 
die sich überall gleich verhält die dafür dann auch langsamer sein darf, 
der ist mit C verkehrt und sollte lieber eine andere Sprache nehmen. Ad 
hoc fiele mir Java ein, was genau mit dieser Zielsetzung entworfen 
wurde, da hat man die Abhängigkeit von der Hardware in die VM 
verfrachtet.

von temp (Gast)


Lesenswert?

Possetitjel schrieb:
> Die guten Argumente bekommt man nur üblicherweise nicht --
> sondern Pöbeleien, dass man halt einfach zu blöd sei, die tiefe
> Weisheit und Erleuchtung zu verstehen.
> Liebe Leute, dass ich die C-Syntax furchtbar finde, ist KEIN
> zwingender Beweis dafür, dass ich geistig minderbemittelt bin!

Du bist hier im Forum "Mikrocontroller und Digitale Elektronik". Wenn 
wir das jetzt mal nach oben abgrenzen und nur Systeme ohne BS 
betrachten, dann geht an C oder/und Assembler kein Weg vorbei. Du kannst 
die Syntax finden wie du willst, das ist uns egal. Aber wer sich über 
die Syntax einer simplen for-Schleife aufregt... Da möchte ich dich gern 
wieder zitieren...

Klar kannst du jetzt trotzig sein und dich nach Alternativen umsehen. 
Allerdings bist du dann auf wenige Controllertypen und in der Regel 
einen Hersteller der Sprache angewiesen. Das will sich keiner antun. 
Schon gar nicht im professionellem Bereich. Das wird dann ziemlich 
einsam.

Die ersten Gehversuche im Controllerbereich mit C sind ja nun auch nicht 
so schwer. Fang von mir aus mit Arduino an. Das können sogar Kinder und 
Künstler. Aber wenn man mit voreingestellter Haßkappe loslegt braucht 
man sich nicht zu wundern.

Beitrag #5228746 wurde von einem Moderator gelöscht.
von Possetitjel (Gast)


Lesenswert?

Nop schrieb:

>> Dass mir Korrektheit und Zuverlässigkeit wichtiger sind
>> als die letzten 20% Performance
>
> Na dann mach doch einfach in C ein Typedef und ein paar
> Defines, schon kannste Deine Vorstellungen auch in C
> umsetzen.

Ich weiss nicht, was ich Dir getan habe, um diesen Zynismus
zu verdienen.
Wenn ich "einfach ein paar Defines" machen könnte, würden wir
diese Diskussion nicht führen -- und das weisst Du natürlich
auch.

> Wenn Du hingegen an einer Sprache interessiert bist, die
> Dich zu so etwas zwingt, bist Du bei C grundverkehrt.

Da ist er wieder, der Horror des C-Programmierers, von der
Sprache "gezwungen" zu werden, den einen oder anderen Fehler
nicht zu machen.
Du verwendest auch für alle Elektrogeräte Blankdraht, weil Du
ja keinesfalls "gezwungen" werden willst, nicht versehentlich
an die Phase zu packen?

>> Die Performance ist bei der nächsten Rechnergeneration
>> ohnehin 50% größer, ohne dass ich dafür etwas tun muss
>
> Du bist hier in einem Forum für Mikrocontroller.

Ja. Das Argument gilt im Grundsatz auch dort.

> Paar hunderttausend mal einen M0 statt eines M4, das ist
> merkbar. Von batteriebetriebenen Anwendungen mal ganz zu
> schweigen. x86 ist nicht das Maß aller Dinge.
> ;-)

Nein, natürlich nicht. -- Moore's Law gilt aber auch für µC.

>> Leider nicht nur -- denn ganz ehrlich: Welche Alternative
>> hat man? (Beispielsweise für µC-Programmierung)
>
> Du mußt Dich schon entscheiden, ob Du hardwarenah oder auf
> sehr abstraktem Niveau arbeiten willst.

Nein -- warum denn?

Wieso ist ein boolescher Datentyp, der kein verkappter
Integer ist, oder eine Speicherreferenz, die Längenprüfung
zulässt, plötzlich "sehr abstrakt"?

Hast Du mal in AWL programmiert? DAS ist hardwarenahe UND
abstrakt.

> Sonst nimm halt statt eines M0 einfach einen Raspi und laß
> Ada drauf los. Rechenkraft fällt schließlich vom Himmel.

Nein, das tut sie nicht.

Ich weiss nicht, woher dieser Drang zur Diffamierung rührt.
Natürlich soll man ökonomisch mit der Rechenleistung umgehen,
aber "ökonomisch" ist eben nicht "geizig".
Der sarkastische Spruch "Wir müssen sparen -- koste es, was
es wolle!" trifft das ganz gut.

> Wer in C programmiert, denkt in portablem Assembler.

Das mag sein -- aber das hilft mir nicht weiter.

Welche (reale!) Wahl habe ich, wenn ich portable System-
programmierung machen will?

(Forth mal bitte außen vor lassen.)

>>> Nimm doch einfach Ada.
>>
>> Oberon wäre wohl naheliegender.
>
> Warum, Ada ist eigens für genau Deine Bedürfnisse entworfen
> worden.

Nun ja, wenn es einen Crosscompiler für MSP430 gibt, kommt
es vielleicht tatsächlich in die engere Wahl...

>>> Und? Das belegt immer noch gar nichts.
>>
>> Doch.
>
> Nein. Daß es Fälle gibt, in denen man zuviel Speicher
> gespart hat, heißt nicht, daß Speichersparen verkehrt
> oder nicht erstrebenswert wäre.

Das habe ich doch auch nirgendwo behauptet. Es geht, wie
schon mehrfach wiederholt, einfach um die Prioritäten.
Und die liegen, meiner unmaßgeblichen Meinung nach, bei
C deutlich suboptimal.

von Carl D. (jcw2)


Lesenswert?

Kann mal jemand denn nullptr abstellen, oder ist das der neue Kurt?

von W.S. (Gast)


Lesenswert?

Possetitjel schrieb:
> Ich will also überhaupt nicht schimpfen; ich suche gute (!)
> Argumente dafür, die Sprache zu lernen. Was also ist an der
> Sprache so toll?

Toll? C etwa? Nee...

Wohlgemerkt, wenn man C vernünftig benutzt und mit dem nötigen mentalen 
Abstand betrachtet, ist C eine durchaus nützliche Programmiersprache.

Aber eigentlich weiß jeder ernstzunehmende C-Programmierer, daß C eine 
miserabel konstruierte Programmiersprache ist. Ebenso weiß auch jeder, 
daß alle bisherigen Versuche, sowohl von C wegzukommen als auch zugleich 
dabei zu bleiben, ziemliche Krücken geworden sind.

Der Katzenjammer darüber ist schon seit einigen Jahren zu beobachten, 
auch dieses Forum ist vollgestopft mit Hilfeschreien von Eleven - aber 
das Ganze kommt ein bissel zu spät.

Vor 20 oder besser noch vor 30 Jahren hätte man es sich eben besser 
überlegen sollen und es nicht zulassen dürfen, daß sich die 
Programmier-Szene zu einer C-Monokultur entwickelt. Eine anständige 
Konkurrenz hätte auch C recht gut getan.

Aber das ist alles vorbei. Wer jetzt nen µC programmieren will, muß halt 
C oder Assembler nehmen, weil eine ernstzunehmende Alternative nicht 
existiert.

Genau DAS ist das "tolle" an C und zugleich das einzige Argument. Denkt 
an Gorbatschow. Wer zu spät kommt, den bestraft das Leben.

W.S.

Vielleicht sollte man hier jetzt

   AMEN

sagen. Oder?

von Nop (Gast)


Lesenswert?

Possetitjel schrieb:

> Wenn ich "einfach ein paar Defines" machen könnte, würden wir
> diese Diskussion nicht führen -- und das weisst Du natürlich
> auch.

Wieso, was soll denn daran das Problem sein? Das Coole an so einer 
Lösung wäre obendrein, daß Du diese Defines dann auch noch mit ifdefs 
klammern kannst und damit einen Debug-Build mit Checks und einen 
Release-Build ohne Checks bauen kannst. Das geht dann im Buildscript 
einfach als Compiler-Option rein.

> Da ist er wieder, der Horror des C-Programmierers, von der
> Sprache "gezwungen" zu werden, den einen oder anderen Fehler
> nicht zu machen.

Nein, es ist der Horror davor, zu ineffizienten Konstrukten gezwungen zu 
sein und die nicht nur nach Bedarf zu machen. Man macht in C schließlich 
auch ISRs und dergleichen. Da möchte ich schon, daß da nicht erst noch 
ein paar Checks im Hintergrund ablaufen, solange ich das nicht so 
eingerichtet habe.

C veranstaltet ganz bewußt im Hintergrund keine weitere Funktionalität. 
Das, was Du hinschreibst, ist auch das, was Du bekommst.

Wenn Du das nicht willst, kannst Du auch C++ nehmen, da gilt es heute 
als schlechter Stil, überhaupt noch mit raw pointers herumzumachen. Um 
das dann freilich noch schnell zu kriegen, mußt Du immer im Hinterkopf 
haben, was bei welchen Konstrukten dann alles an Rattenschwanz 
nachkommt.

Bei C++ bezahlst Du nicht für etwas, das Du nicht bestellt hast - aber 
Du mußt die riesige Speisekarte exakt kennen. Ansonsten ist das mit C++ 
wie in einem Puff, wo zwar das Mineralwasser 2 Euro kostet, aber für das 
Gläschen Sekt mit der Bedienung biste 200 Euro los.

Dafür haste die Garantie, daß Du mit C++ alles "safer" hast, solange Du 
nicht in den Keller gehst.

> Nein, natürlich nicht. -- Moore's Law gilt aber auch für µC.

Was nichts dran ändert, daß bei gleichem technischen Fortschritt immer 
noch der µC mit weniger RAM, ROM und MHz weniger Energie verbraucht und 
somit andere Anwendungen ermöglicht.

> Wieso ist ein boolescher Datentyp, der kein verkappter
> Integer ist,

JEDER bool'sche Datentyp ist ein verkappter Integer, weil dabei nämlich 
letztlich Maschinencode rausfällt, der ein Register auf 0 testet.

> oder eine Speicherreferenz, die Längenprüfung zulässt

... und den Overhead dafür auch gleich erzwingt. Ich will aber keine 
Längenprüfung haben, wenn ich bei memory mapped IO direkt mit einem 
volatile-Pointer arbeite.

> Hast Du mal in AWL programmiert?

Nein, bislang nichtmal davon gehört. Aber warum tust Du es dann nicht 
einfach?

> Welche (reale!) Wahl habe ich, wenn ich portable System-
> programmierung machen will?

Dreh die Frage mal um: wieso meinst Du, daß eine andere Sprache, die das 
so ermöglicht, Dir dann besser gefiele?

> Nun ja, wenn es einen Crosscompiler für MSP430 gibt, kommt
> es vielleicht tatsächlich in die engere Wahl...

Warte doch einfach auf Moore's Law, das gilt auch für µCs, sagtest Du 
gerade.

> Und die liegen, meiner unmaßgeblichen Meinung nach, bei
> C deutlich suboptimal.

Sie liegen deutlich mehr in Richtung Performance und direkter Arbeit mit 
Hardware als bei vergleichbaren imperativen Sprachen. Was man durchaus 
kritisieren kann, das ist das Ausmaß an undefined behaviour, von dem man 
einen guten Teil genausogut auch als implementation defined hätte machen 
können. Da ist man damals zu großzügig gewesen.

von Nop (Gast)


Lesenswert?

W.S. schrieb:

> Toll? C etwa? Nee...

Naja, ich hab mit Pascal angefangen, und nach einem kleinen Demoprojekt 
zur Einarbeitung in C habe ich nie zurückgesehen. Das war wie Radfahren 
jetzt ohne Stützräder.

Dennoch wäre es besser gewesen, hätte man damals nicht eine völlig 
nutzlose Spielzeugsprache als Pascal standardisiert, sondern etwas 
Richtiges - und zwar portabel und herstellerunabhängig.

von Possetitjel (Gast)


Lesenswert?

Nop schrieb:

> Possetitjel schrieb:
>
>> Das Thema ist: Warum sollte es sich lohnen, die Qual
>> auf sich zu nehmen, diese Sprache zu erlernen?
>
> Weil man damit sehr hardwarenah programmieren kann, ohne
> deswegen Assembler nehmen zu müssen - der wäre nicht
> portabel und auf jeder CPU wieder sehr deutlich anders.

Gut. Vorläufig akzeptiert.

> [...] Nehmen wir mal einen Shift eines 32-bit Integers
> um 32 Bit, der ist in C nicht definiert.
>
> Das kommt daher, daß unter x86-32 der Opcode für den
> Shiftbefehl nur 5 Bit für die Shift-Distanz hat, das
> reicht ja. Wenn man nun eine 1 um 32 Bit shiften will,
> dann sind die unteren 5 Bit 0, folglich wird gar nicht
> geshiftet, und es kommt 1 heraus. Auf Power-PC ist das
> hingegen anders, und das Ergebnis ist 0.

Okay. Soweit verstanden.

> Wollte man das nun plattformübergreifend definieren,
> dann müßte man je nach Plattform mit Checks arbeiten,
> [...]

Nicht unbedingt.

Es besteht ja überhaupt kein Zwang, die Shift-Anweisung
unbedingt in einen einzigen Maschinenbefehl abbilden zu
müssen -- auf manchen Plattformen geht das gar nicht,
weil es keinen Befehl gibt, der um mehr als 1 Bit schiebt.

Zwei 16-bit-Shifts liefern auch auf x86 das Gewünschte.

Ich würde ja nix sagen, wenn man mit einer Compilerdirektive
zwischen einem "portablen" und einem "nativen" Modus hinundher-
schalten könnte - dazu müsste es aber erstmal einen portablen
Modus geben.

> Es gibt so manches, was in C erstmal absurd aussieht,
> was aber durchaus seinen Grund hat, und zwar oftmals
> eben aus Hardware-Gründen. Das ist bei einer Sprache,
> die so dicht an die Hardware geht und gehen soll, nicht
> zu vermeiden.

Hmm.
Mag sein, dass das primär ein Problem der Lehre, der
Vermittlung ist -- aber mir sind die Erklärungen, WARUM
die Syntax eben genau SO aussieht, wie sie aussieht,
i.d.R. deutlich zu dünn gesät.

> Wer da natürlich herangeht und eine ideale abstrakte
> Maschine erwartet, die sich überall gleich verhält die dafür
> dann auch langsamer sein darf,

Das ist zu krass formuliert, aber die Richtung stimmt schon.

> der ist mit C verkehrt und sollte lieber eine andere Sprache
> nehmen.

Das würde ich gern -- wenn ich denn eine kennen würde.

> Ad hoc fiele mir Java ein, was genau mit dieser Zielsetzung
> entworfen wurde, da hat man die Abhängigkeit von der Hardware
> in die VM verfrachtet.

Hmm. Ja. Java ist wieder in die andere Richtung übertrieben.

von Nop (Gast)


Lesenswert?

Possetitjel schrieb:

> Zwei 16-bit-Shifts liefern auch auf x86 das Gewünschte.

Das war jetzt die einfache Variante. Beim Shift einer Konstanten um eine 
Konstante fällt gar kein Maschinencode heraus, das macht der Compiler 
schon zur Compilezeit.

Interessant wird es, wenn man eine Konstante oder Variable um eine 
andere Variable shiftet, also etwas wie "x << y" macht. Das ginge ohne 
Checks nicht. Und man möchte auch nicht auf jeder Plattform den Shift in 
mehrere Shifts aufspalten, nur weil es Plattformen geben kann, die nur 
um 1 Bit shiften können.

> Ich würde ja nix sagen, wenn man mit einer Compilerdirektive
> zwischen einem "portablen" und einem "nativen" Modus hinundher-
> schalten könnte - dazu müsste es aber erstmal einen portablen
> Modus geben.

Die C-Lösung ist es, den portablen und den nativen Modus zu vereinen. 
Portabel innerhalb dessen, was der C-Standard erlaubt.

> Mag sein, dass das primär ein Problem der Lehre, der
> Vermittlung ist -- aber mir sind die Erklärungen, WARUM
> die Syntax eben genau SO aussieht, wie sie aussieht,
> i.d.R. deutlich zu dünn gesät.

Naja ob man nun geschweifte Klammern oder begin/end sagt, ist letztlich 
Geschmackssache. Ich bevorzuge die geschweiften Klammern, weil ich nicht 
die Syntax-Elemente im Weg haben will beim Lesen.

> Das würde ich gern -- wenn ich denn eine kennen würde.

Wenn Du Dich auf einen einzigen Compiler festlegen kannst, der aber auch 
für etliche Plattformen verfügbar ist, käme Free Pascal in Frage.

von Possetitjel (Gast)


Lesenswert?

temp schrieb:

> Du bist hier im Forum "Mikrocontroller und Digitale Elektronik".

Das ist mir bewusst :)


> Wenn wir das jetzt mal nach oben abgrenzen und nur Systeme ohne
> BS betrachten, dann geht an C oder/und Assembler kein Weg vorbei.

Ja - und meine Frage ist: Warum?


> Du kannst die Syntax finden wie du willst, das ist uns egal.

Das ist keine besonders gute Ausgangsposition, am Anfang der
Unterhaltung zu erklären, dass einem die Beweggründe des
Gesprächspartners egal sind, meinst Du nicht?


> Aber wer sich über die Syntax einer simplen for-Schleife
> aufregt... Da möchte ich dich gern wieder zitieren...

Das ändert aber nichts am Fakt: Ich finde die Syntax grausig.

> Klar kannst du jetzt trotzig sein und dich nach Alternativen
> umsehen.

Ja. Jeder reale Fortschritt beginnt mit Unzufriedenheit :)

> Allerdings bist du dann auf wenige Controllertypen und in
> der Regel einen Hersteller der Sprache angewiesen. Das will
> sich keiner antun.

Naja, das läuft dann auf eine ganz simple Antwort hinaus:
Das Tollste an der Programmiersprache C ist, dass sie
standardisiert ist.
Das wäre zwar zum Kotzen -- aber immerhin eine nachvollziehbare
Antwort.

> Aber wenn man mit voreingestellter Haßkappe loslegt braucht
> man sich nicht zu wundern.

Über das Stadium bin ich hinaus.
Ich habe nur keine Lust zu einer Quälerei, "weil das nun mal
eben so ist" und "das nun mal eben dazugehört".

von W.S. (Gast)


Lesenswert?

Nop schrieb:
> hätte man damals nicht eine völlig
> nutzlose Spielzeugsprache als Pascal standardisiert

Sind wir jetzt beim C<--->Pascal Streit?

Aber mal abgesehen davon, klingt mir dein Beitrag sehr nach 
Unwissenheit. Pascal - jedenfalls das heutige Pascal - ist weder nutzlos 
noch Spielzeug. Würdest du es auch nur ein klein wenig kennen, dann 
hättest du nicht sowas geschrieben.

Also töne nicht gar so laut darüber.

W.S.

von Possetitjel (Gast)


Lesenswert?

W.S. schrieb:

> Toll? C etwa? Nee...

:)

> Wohlgemerkt, wenn man C vernünftig benutzt und mit dem
> nötigen mentalen Abstand betrachtet, ist C eine durchaus
> nützliche Programmiersprache.

Das steht außer Frage.

> Aber eigentlich weiß jeder ernstzunehmende C-Programmierer,
> daß C eine miserabel konstruierte Programmiersprache ist.

Gut... der Deutung kann ich sogar etwas abgewinnen: Je
vehementer jemand C verteidigt, desto weniger ernst ist er
als Programmierer zu nehmen :)

> Vor 20 oder besser noch vor 30 Jahren hätte man es sich eben
> besser überlegen sollen und es nicht zulassen dürfen, daß sich
> die Programmier-Szene zu einer C-Monokultur entwickelt. Eine
> anständige Konkurrenz hätte auch C recht gut getan.

Das ist leicht gesagt -- aber wer ist "man"?

Nach meinem Gefühl standen die 90er zu sehr im Zeichen der
Objektorientierung, als dass sich noch irgendjemand ernsthaft
um eine prozedurale Sprache gekümmert hätte.

> Aber das ist alles vorbei. Wer jetzt nen µC programmieren will,
> muß halt C oder Assembler nehmen, weil eine ernstzunehmende
> Alternative nicht existiert.

Hmm...

> Genau DAS ist das "tolle" an C und zugleich das einzige
> Argument.

Naja, Du bist nicht der einzige, der das so sieht. Vielleicht
ist das ja tatsächlich die Antwort auf meine Frage. Wäre zwar
nicht schön, aber immerhin eine verstehbare Antwort.

von Nop (Gast)


Lesenswert?

W.S. schrieb:

> Sind wir jetzt beim C<--->Pascal Streit?

Nein.

> Pascal - jedenfalls das heutige Pascal - ist weder nutzlos
> noch Spielzeug.

Aber standardisiert ist es nicht! Es ging darum, daß Pascal als nutzlose 
Spielzeugsprache standardisiert wurde. Das hatte zur Folge, daß jede 
reale Implementation, egal ob auf Mac, ST oder DOS mit haufenweise 
proprietären Erweiterungen arbeitete. Nichts war zu gar nichts 
kompatibel, die Balkanisierung war immer ein Problem.

Deswegen ist Pascal auch so abgestürzt, als Borland die Kurve nicht so 
richtig gekriegt hat, weil Turbo-Pascal nicht nur an Borland, sondern 
auch an DOS gebunden war. Unter DOS war es freilich cool, besonders ab 
Version 5 oder so.

Kleines Schmankerl, wie sich das auch heute noch auswirkt: In Free 
Pascal sind globale Variablen immer implizit volatile, weil es das ganze 
Konzept von nicht-volatile für globale Variablen nicht gibt. Bei MicroE 
hingegen sind globale Variablen nicht volatile, wenn man sie nicht dazu 
quzalifiziert.

Die Konsequenz ist ein typischer Pascal-F*ck*p: Nimm einen Quelltext für 
Free Pascal, der globals zwischen ISR und Applikation tauscht, nutz das 
mit MicroE weiter und wundere Dich, wieso das nicht geht.

Das wäre nicht passiert, wenn man eine der brauchbaren Pascal-Version 
standardisiert hätte, völlig egal welche. Das Blöde an der ganzen Sache 
ist nun, daß Pascal vom ganzen Konzept her deutlich weniger maschinennah 
als C ausgerichtet ist und daher besser anstatt schlechter portabel sein 
sollte.

von Possetitjel (Gast)


Lesenswert?

Nop schrieb:

> Interessant wird es, wenn man eine Konstante oder Variable
> um eine andere Variable shiftet, also etwas wie "x << y"
> macht. Das ginge ohne Checks nicht.

Da bin ich eben nicht ganz überzeugt.

> Und man möchte auch nicht auf jeder Plattform den Shift in
> mehrere Shifts aufspalten, nur weil es Plattformen geben
> kann, die nur um 1 Bit shiften können.

Muss man ja nicht. Der Compiler weiss ja, was die Plattform
kann, für die er gerade kompiliert.
Die Plattformen, die sich "nativ" so verhalten wie gewünscht,
kommen mit einem Befehl aus; auf den anderen sind halt mehrere
notwendig.

>> Ich würde ja nix sagen, wenn man mit einer Compilerdirektive
>> zwischen einem "portablen" und einem "nativen" Modus hinundher-
>> schalten könnte - dazu müsste es aber erstmal einen portablen
>> Modus geben.
>
> Die C-Lösung ist es, den portablen und den nativen Modus zu
> vereinen. Portabel innerhalb dessen, was der C-Standard erlaubt.

Hmm. Ja. -- Ich verstehe, dass das damals sinnvoll erschien,
weil es viele (aus heutiger Sicht) exotische Plattformen gab
und man keinesfalls unnötige Einschränkungen machen wollte.

Aber die Entwicklung ist inzwischen 30 Jahre weiter.

>> Mag sein, dass das primär ein Problem der Lehre, der
>> Vermittlung ist -- aber mir sind die Erklärungen, WARUM
>> die Syntax eben genau SO aussieht, wie sie aussieht,
>> i.d.R. deutlich zu dünn gesät.
>
> Naja ob man nun geschweifte Klammern oder begin/end sagt,

Nein -- DAS meine ich nun wirklich nicht. Ich dachte an
Konstruktionen wie "(1<<5)|(1<<2)", die für den Unkundigen
völlig krank aussehen, aber dennoch ihren Sinn haben.

>> Das würde ich gern -- wenn ich denn eine kennen würde.
>
> Wenn Du Dich auf einen einzigen Compiler festlegen kannst,
> der aber auch für etliche Plattformen verfügbar ist, käme
> Free Pascal in Frage.

Ja -- für Quasi-PC-Zeug ist das auch meine Wahl. Aber das
löst das Problem mit den Mikrocontrollern nicht.

von Nop (Gast)


Lesenswert?

Possetitjel schrieb:

> Nach meinem Gefühl standen die 90er zu sehr im Zeichen der
> Objektorientierung, als dass sich noch irgendjemand ernsthaft
> um eine prozedurale Sprache gekümmert hätte.

Die Arbeit für C99 war doch in den 90ern. Für Pascal war die große 
Gelegenheit in den 80ern, bevor Unix (und damit C) sich überhaupt 
verbreitet hatte. Borland war damals in der Position, denn sie haben das 
Augenmerk auf ein praxistaugliches Pascal gelegt und hatten auch eine 
ziemliche Marktmacht.

Aber Borland hat es vorgezogen, keinen Standard zu machen, weil sie 
dadurch mit jeder neuen Version von TP nach Belieben neue Features 
einbauen konnten und der Konkurrenz immer voraus waren. Hätte man erst 
standardisiert und dann implementiert, wäre der Vorsprung geringer 
gewesen.

Im Prinzip hat Borland für ein paar Jahre Profit auf einer dann ohnehin 
wegbrechenden Plattform (DOS) Pascal geopfert.

Was dann 1990 als ISO-Pascal aktualisiert wurde, war nach wie vor ein 
Witz.

von Nop (Gast)


Lesenswert?

Possetitjel schrieb:

> Die Plattformen, die sich "nativ" so verhalten wie gewünscht,
> kommen mit einem Befehl aus; auf den anderen sind halt mehrere
> notwendig.

Tja, und damit hätte man für x86 mehrere Befehle gebraucht. Nicht, daß 
das der spezifische Grund war, denn C gab's schon Jahre früher, aber die 
Entscheidung war, daß Performance wichtiger ist als ein Shift um mehr 
als die Variablenbreite, der auf vielen CPUs sowieso nicht existiert.

> Aber die Entwicklung ist inzwischen 30 Jahre weiter.

Mal den Befehlssatz für ARM Cortex-M4 angesehen? Dort findet man für die 
Shift-Befehle um einen fixen Betrag immer noch genau diese Begrenzung.

> Nein -- DAS meine ich nun wirklich nicht. Ich dachte an
> Konstruktionen wie "(1<<5)|(1<<2)", die für den Unkundigen
> völlig krank aussehen, aber dennoch ihren Sinn haben.

Also das ist aber auch schlechter Stil, die 5 und die 2 sollte man durch 
vernünftige defines ersetzen, sonst weiß man zwei Wochen danach selber 
nicht mehr, was das sollte.

> Ja -- für Quasi-PC-Zeug ist das auch meine Wahl. Aber das
> löst das Problem mit den Mikrocontrollern nicht.

Free Pascal gibt's doch auch für Microcontroller. Jetzt nicht für alle 
und jeden, aber für etliche, und das wird besser.

von Possetitjel (Gast)


Lesenswert?

Nop schrieb:

> Possetitjel schrieb:
>
>> Wenn ich "einfach ein paar Defines" machen könnte, würden
>> wir diese Diskussion nicht führen -- und das weisst Du
>> natürlich auch.
>
> Wieso, was soll denn daran das Problem sein?

Eventuell die Tatsache, dass ich die Programmiersprache C
nicht beherrsche?

>> Da ist er wieder, der Horror des C-Programmierers, von der
>> Sprache "gezwungen" zu werden, den einen oder anderen Fehler
>> nicht zu machen.
>
> Nein, es ist der Horror davor, zu ineffizienten Konstrukten
> gezwungen zu sein und die nicht nur nach Bedarf zu machen.

Nein, warum denn. Ich sage ja nicht, dass es NUR überwachte
Referenzen geben soll -- meine Aussage war nur, dass es die
AUCH geben sollte.

Und ein echt boolescher Datentyp würde gar nix zur Laufzeit
kosten, denn die Zuweisungen prüft der Compiler zur beim
Übersetzen.

> Wenn Du das nicht willst, kannst Du auch C++ nehmen, da
> gilt es heute als schlechter Stil, überhaupt noch mit
> raw pointers herumzumachen.

Habe ich ernsthaft erwogen. Aber ehrlich: Mit C++ anfangen,
wenn man nicht mal C kann?!

>> Nein, natürlich nicht. -- Moore's Law gilt aber auch
>> für µC.
>
> Was nichts dran ändert, daß bei gleichem technischen
> Fortschritt immer noch der µC mit weniger RAM, ROM
> und MHz weniger Energie verbraucht und somit andere
> Anwendungen ermöglicht.

Jein. Mehr RAM und ROM geht primär auf den Preis; mehr
MHz geht tatsächlich auf die Energie.
Es spielt also eine Rolle, ob die zusätzlichen Features
der Sprache eher zusätzliche Zeit oder zusätzlichen
Speicher erfordern.

>> Wieso ist ein boolescher Datentyp, der kein verkappter
>> Integer ist,
>
> JEDER bool'sche Datentyp ist ein verkappter Integer, weil
> dabei nämlich letztlich Maschinencode rausfällt, der ein
> Register auf 0 testet.

Nee.
Test auf Gleichheit kann ich über beliebigen Elementen
definieren, dass ist kein Vorrecht der Arithmetik.

Ein Byte im Speicher wird erst dadurch zur Zahl, dass
zahlentypische Operationen damit erlaubt sind. Wenn der
Compiler das per Typprüfung verbietet, ist es keine Zahl.

>> Hast Du mal in AWL programmiert?
>
> Nein, bislang nichtmal davon gehört. Aber warum tust Du
> es dann nicht einfach?

Das ist SPS-spezifisch (und das will man auch nicht
wirklich.)
Ich habe das nur als Beispiel angeführt, weil das einerseits
wildes Bitgefrickel ist (soll heißen: einzelne Bits einlesen,
AND/OR/NOT, Einzelbits schreiben, "Timer" laufen lassen usw.)
und andererseits vollkommen von der realen Steuerungshardware
entkoppelt ist.

>> Welche (reale!) Wahl habe ich, wenn ich portable System-
>> programmierung machen will?
>
> Dreh die Frage mal um: wieso meinst Du, daß eine andere
> Sprache, die das so ermöglicht, Dir dann besser gefiele?

Weil ich keinen logischen Zusammenhang sehe zwischen den
Eigenschaften von C, die mich stören, und der Eignung zur
Systemprogrammierung:
- Vernünftiges Typsystem wäre wünschenswert. Schließt ja
  nicht aus, dass man das per Schlüsselwort außer Kraft setzen
  kann, wenn das (z.B. für Hardwarezugriff) notwendig ist.
  Boolescher Datentyp, Pascal-Typecasts (x:=word(y);) inclusive.
- Nur arithmetische Ausdrücke haben einen Wert, Anweisungen nicht.
- Verzicht auf die irrsinnigen Vorrangregeln; als Ausgleich
  logisch nachvollziehbare Behandlung von Klammern.
- überwachte Speicherreferenzen zusätzlich zu rohen Zeigern.
Nur mal als Anfang.

>> Nun ja, wenn es einen Crosscompiler für MSP430 gibt, kommt
>> es vielleicht tatsächlich in die engere Wahl...
>
> Warte doch einfach auf Moore's Law, das gilt auch für µCs,
> sagtest Du gerade.

???

Moore's Law macht die Hardware schneller -- aber die Software
nicht portabler.

>> Und die liegen, meiner unmaßgeblichen Meinung nach, bei
>> C deutlich suboptimal.
>
> Sie liegen deutlich mehr in Richtung Performance und direkter
> Arbeit mit Hardware als bei vergleichbaren imperativen Sprachen.

Gut, darauf können wir uns sogar einigen.
Die Schwächen von C wären mir völlig egal, wenn es eine Alternative
gäbe, die genauso portabel ist wie C, aber einige seiner Schwächen
nicht hat. Der Preis dürfte gern ein (moderater!) Performance-
nachteil sein.

von S. R. (svenska)


Lesenswert?

Possetitjel schrieb:
> Ich würde ja nix sagen, wenn man mit einer Compilerdirektive
> zwischen einem "portablen" und einem "nativen" Modus hinundher-
> schalten könnte - dazu müsste es aber erstmal einen portablen
> Modus geben.

Genau das hast du mit C doch.

Der Standard unterscheidet zwischen
- specified ("portabel")
- implementation-defined ("native")
- undefined ("verboten")

> Mag sein, dass das primär ein Problem der Lehre, der
> Vermittlung ist -- aber mir sind die Erklärungen, WARUM
> die Syntax eben genau SO aussieht, wie sie aussieht,
> i.d.R. deutlich zu dünn gesät.

Syntax ist in aller Regel persönlichen Vorlieben geschuldet.

Der eine mag geschweifte Klammern (auf einer US-Tastatur kein Thema), 
der andere bevorzugt begin/end (auf einer deutschen Tastatur weniger 
Fingerverrenkungen).

Manche Sprachen deklarieren Variablen mit "int abc", andere mit "abc : 
int". Letzteres vereinfacht das Parsing, ist ansonsten aber äquivalent.

Jede Schleife kann als while-Schleife dargestellt werden, und die 
for-Schleife in C ist nur syntaktischer Zucker, um die häufigste Form 
davon (die Zählschleife) kompakt hinzuschreiben. Gleichzeitig ist die 
Schreibweise mächtig genug, um auch durch Listen/Bäume zu iterieren.

In Perl gibt es fast nur syntaktischen Zucker, was die Sprache gut zu 
schreiben und schlecht lesbar macht. In Python ist das umgekehrt. Wähle 
deine Präferenz.

Ich wüsste gerne, was genau dein Problem mit C ist, außer dass du es 
hasst. Nicht alles, was man nicht versteht, ist idiotisch und nicht 
alles, was idiotisch ist, war es immer. Und Unwissenheit verführt zu 
vielem.

von Nop (Gast)


Lesenswert?

Possetitjel schrieb:

> Eventuell die Tatsache, dass ich die Programmiersprache C
> nicht beherrsche?

Hm ja, das leuchtet ein.

> Weil ich keinen logischen Zusammenhang sehe zwischen den
> Eigenschaften von C, die mich stören, und der Eignung zur
> Systemprogrammierung:
> - Vernünftiges Typsystem wäre wünschenswert. Schließt ja
>   nicht aus, dass man das per Schlüsselwort außer Kraft setzen
>   kann, wenn das (z.B. für Hardwarezugriff) notwendig ist.
>   Boolescher Datentyp, Pascal-Typecasts (x:=word(y);) inclusive.
> - Nur arithmetische Ausdrücke haben einen Wert, Anweisungen nicht.
> - Verzicht auf die irrsinnigen Vorrangregeln; als Ausgleich
>   logisch nachvollziehbare Behandlung von Klammern.
> - überwachte Speicherreferenzen zusätzlich zu rohen Zeigern.
> Nur mal als Anfang.

Mit der Wunschliste würde ich definitiv Pascal empfehlen, und hier Free 
Pascal. Erstens kostet das nichts, zweitens gibt es das auch auf dem PC, 
und drittens gibt's ne aktive Community. Die ersten Gehversuche in einer 
Sprache würde ich nicht auf einem Controller machen.

Besondere Performance-Einbußen würde ich im Normalfall nicht erwarten. 
Die Kernfrage ist, wie sehr Du auf Deinen Controller abonniert bist. Für 
ARMs ginge das.

Was Du mit nicht-C verlierst, ist der Zugang zur lingua franca, zu der 
Pascal es wegen der Standardisierungs-Problematik nicht gebracht hat.

von S. R. (svenska)


Lesenswert?

Possetitjel schrieb:
> Eventuell die Tatsache, dass ich die Programmiersprache C
> nicht beherrsche?

Das macht es natürlich schwieriger, die positiven Seiten zu sehen. ;-)

> Nein, warum denn. Ich sage ja nicht, dass es NUR überwachte
> Referenzen geben soll -- meine Aussage war nur, dass es die
> AUCH geben sollte.

Die gibt es mit C++ und sie kommen mit einem Preisschild.

> Und ein echt boolescher Datentyp würde gar nix zur Laufzeit
> kosten, denn die Zuweisungen prüft der Compiler zur beim
> Übersetzen.

Ein echt boolescher Datentyp ist seit C99 vorhanden.

Wenn du in C einen integer boolsch benutzt, nutzt der Compiler die bei 
der ALU ohnehin anfallenden Flags, um sie zu implementieren. Nutzt du 
einen echten Boolean, wird zusätzlicher Code generiert, um 
sicherzustellen, dass der Registerwert exakt 0 oder 1 ist. Das steht auf 
dem Preisschild.

> Habe ich ernsthaft erwogen. Aber ehrlich: Mit C++ anfangen,
> wenn man nicht mal C kann?!

Modernes C++ (das heißt C++11 und neuer!) hat mit klassischem C 
eigentlich nichts gemeinsam. Schau es dir einfach mal an.

>> JEDER bool'sche Datentyp ist ein verkappter Integer, weil
>> dabei nämlich letztlich Maschinencode rausfällt, der ein
>> Register auf 0 testet.
>
> Nee.
> Test auf Gleichheit kann ich über beliebigen Elementen
> definieren, dass ist kein Vorrecht der Arithmetik.

Ja, das kannst du, und je nachdem, wie allgemein du deine Elemente 
definierst, sind deine Tests beliebig kompliziert. Aber am Ende kommt 
ein "ist gleich" oder ein "ist nicht gleich" heraus. Die Darstellung 
dessen innerhalb der CPU ist in der Regel ein Registerwert, den man 
effizient auf 0 testen kann und daher auch auf 0 testen möchte.

> Ein Byte im Speicher wird erst dadurch zur Zahl, dass
> zahlentypische Operationen damit erlaubt sind. Wenn der
> Compiler das per Typprüfung verbietet, ist es keine Zahl.

Eine CPU kennt grundsätzlich nur einen Datentyp, nämlich "Register der 
Wortbreite", selten ein wenig mehr. Dessen direkte Bedeutung ist durch 
die Maschinenbefehle festgelegt und lässt wenig Raum für 
Interpretationen.

Alles, was darüber hinausgeht, kommt mit einem Preisschild.

>>> Welche (reale!) Wahl habe ich, wenn ich portable System-
>>> programmierung machen will?
>>
>> Dreh die Frage mal um: wieso meinst Du, daß eine andere
>> Sprache, die das so ermöglicht, Dir dann besser gefiele?
>
> Weil ich keinen logischen Zusammenhang sehe zwischen den
> Eigenschaften von C, die mich stören, und der Eignung zur
> Systemprogrammierung:
> - Vernünftiges Typsystem wäre wünschenswert. Schließt ja
>   nicht aus, dass man das per Schlüsselwort außer Kraft setzen
>   kann, wenn das (z.B. für Hardwarezugriff) notwendig ist.
>   Boolescher Datentyp, Pascal-Typecasts (x:=word(y);) inclusive.

Das Typsystem von C orientiert sich am Typsystem der CPU. Schau dir C++ 
an, das ist da wesentlich ergiebiger. Typecasts in C erzwingen nur eine 
bestimmte Interpretation eines Datentypen.

> - Nur arithmetische Ausdrücke haben einen Wert, Anweisungen nicht.

Was meinst du damit? Gib mal ein Beispiel für eine Anweisung, die in C 
einen Wert hat, aber sinnvollerweise keinen haben sollte.

> - Verzicht auf die irrsinnigen Vorrangregeln; als Ausgleich
>   logisch nachvollziehbare Behandlung von Klammern.

Irgendwelche Vorrangregeln muss es geben. Mag sein, dass die von C 
gewählten suboptimal sind, aber das ist historisch so gewachsen.

Alternativ kann man das auch wie in Perl machen, wo sowohl "||" als auch 
"or" existieren, so dass man sich als Programmierer das aussuchen kann, 
was man will. Das macht den Code allerdings schlechter lesbar, kommt 
also ebenfalls mit Preisschild.

> - überwachte Speicherreferenzen zusätzlich zu rohen Zeigern.

Wirf einen Blick auf C++.

> Moore's Law macht die Hardware schneller -- aber die Software
> nicht portabler.

Sie erlaubt dir aber eine wesentlich abstraktere Sicht auf die Hardware, 
wodurch sie portabler wird. Siehe Java.

> Die Schwächen von C wären mir völlig egal, wenn es eine Alternative
> gäbe, die genauso portabel ist wie C, aber einige seiner Schwächen
> nicht hat. Der Preis dürfte gern ein (moderater!) Performance-
> nachteil sein.

Du kommst mit C++ recht nah an die Portabilität von C heran, und die 
Performance-Nachteile kannst du - wenn du weißt, was du tust - 
weitestgehend vermeiden. Allerdings kommen die Features, die du 
möchtest, mit ihrem Preisschild und die Abwägung ist allein deine.

Ein Hauptgrund für die Portabilität von C ist übrigens gerade die 
Freiheit der Sprachdefinition. Sie macht den Einsatz auf vielen 
exotischen Architekturen erst möglich, und jede darüber hinausgehende 
Spezifikation würde das verhindern (oder so verteuern, dass es nicht 
lohnt). Daher gibt es keine "genauso portable" Sprache wie C, außer C 
selbst.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Possetitjel schrieb:
> Nop schrieb:
>
>> Interessant wird es, wenn man eine Konstante oder Variable
>> um eine andere Variable shiftet, also etwas wie "x << y"
>> macht. Das ginge ohne Checks nicht.
>
> Da bin ich eben nicht ganz überzeugt.

Bleiben wir mal bei x86 und 5 Bits für den Shift-Offset, und nehmen an, 
dass dessen Barrel-Shifter eine 5-Bit Eingabe hat, egal ob die Eingabe 
aus einem Register oder einem Immediate stammt.  Ein x << var müsste 
dann vom Compiler umgesetzt werden wie
1
IF var > 31
2
    result :=  0
3
ELSE
4
    result := x << var
mit einer naheliegenden Semantik für Shifts > 31 Bit.  Und der nächste 
fände es unpraktisch, dass Shifts mit negativem Offset nicht 
funktionieren, und warum ein Links-Shift mit negatitem Offset nicht zu 
einem Recht-Shift führt, warum ein Rechts-Shift mit negativem Offset 
nicht zu einem entsprechenden Links-Shift etc.

Hier ist es wie im "normalen" Leben auch:  An bestimmten Stellen muss 
man eine Entscheidung treffen, und egel wie man sich entscheidet, hätte 
die andere Alternative auch ihre positiven Seiten. Aber sich garnicht zu 
entscheiden ist noch viel übler.

Und dann sieh's mal so: Wenn sich K&R damals dafür entschieden hätten, 
die Semantik so zu gestalten, dann würden wir vielleicht hier und heute 
darüber diskutieren, warum eine Sprache, die angetreten ist effizienten 
Code zu generieren, so ein Unsinn verlangt:  "In meiner Anwendung sind 
die Offsets per Konstruktion immer < 32, warum erzeugt der bescheuerte 
C-Compiler dennoch so'n Schrott-ineffizienten Code?" würde dann 
lamentiert.

Oder diese andere Semantik hätte dazu geführt haben können, dass C heute 
keine Rolle mehr spielte.  Weiß man's?

Schließlich ist zu bedenken, dass die Sprache um 1970 entstand und 
entworfen wurde.  Das sind run 45 Jahre.  Durch 18 Monate (1 
Moore-Einheit) sind das 30 Moore-Einheiten, d.h. damals war die Hardware 
2^30 mal langsamer. Muss man sich mal auf der Zunge zergehen lassen: 
2^30 (ich hab's extra in 'nen Tschenrechenr getippst: 45 : 1.5 ist 
wirklich 30).  Und mit 24 Monaten für 1 Moore ist der Faktor immer noch 
6.000.000.

Jetzt takte deinen AVR oder MSP430 oder x86_64 Boliden mal um den Faktor 
6.000.000 bis 1.000.000.000 langsamer und gib ihm nur 1/1.000.000.000 
bis 1/6.000.000 des Speichers und dann reden wir nochmal :-)


>>> Ich würde ja nix sagen, wenn man mit einer Compilerdirektive
>>> zwischen einem "portablen" und einem "nativen" Modus hinundher-
>>> schalten könnte - dazu müsste es aber erstmal einen portablen
>>> Modus geben.
>>
>> Die C-Lösung ist es, den portablen und den nativen Modus zu
>> vereinen. Portabel innerhalb dessen, was der C-Standard erlaubt.
>
> Hmm. Ja. -- Ich verstehe, dass das damals sinnvoll erschien,
> weil es viele (aus heutiger Sicht) exotische Plattformen gab
> und man keinesfalls unnötige Einschränkungen machen wollte.
>
> Aber die Entwicklung ist inzwischen 30 Jahre weiter.

S einfach ist das mit einem Schalter auch nicht, wenn dann hätte man für 
jeden Schalter 2 Ausprägungen der (Standard)-Bibliotheken:  Eine 
"sichere" und eine "schnelle", und das für jeden möglichen Schalter der 
das ABI oder die Semantik beeinflusst.

Ein Faktor, warum sich C so lange hat halten können, ist bestimmt auch, 
dass es eben nicht in zig Varianten und Dialekte zersplitterte.

> Nein -- DAS meine ich nun wirklich nicht. Ich dachte an
> Konstruktionen wie "(1<<5)|(1<<2)", die für den Unkundigen
> völlig krank aussehen, aber dennoch ihren Sinn haben.

hihi, ich mal mir grad aus, wie C aussähe wenn es im Zeitalter von 
Touchscreen, Autokorrektur und Emojis entstanden wäre.

Possetitjel schrieb:
> Ich bin lediglich nicht bereit, die (aus meiner Sicht zahlreichen,
> kruden) Eigenarten von C als gottgegeben hinzunehmen [...]
> Ich möchte - unabhängig davon, ob ich die Festlegung gut
> oder schlecht finde - verstehen, WARUM dieses oder jenes
> genau so festgelegt wurde.

puh, da musst du Ritchie fragen, wenn dir der Weg zu ihm (und zurück) 
nicht zu weit ist.

Ein Aspekt war jedenfalls auch die wesentlich beschränktere Hardware — 
nicht nur die der Targets, sondern auch die der Hosts und der 
Build-Umgebung, auf der ein C-Compiler zu laufen hatte.

Stell dir vor, du sollst einen C-Compiler schreiben in einer Sprache, 
die noch weniger kann als C, auf einer Maschine, die um o.g. Faktoren 
weniger leistungsfähig ist wie heutige Boliden was RAM, ROM und Speed 
angeht. Ritch ist bestimmt nicht für 9 Monate in Klausur in ein Kloster 
gegangen und ist dann mit dem heiligen Gral "C" zurückgekehrt, sondern 
er hat bestimmt auch eine Implementation gemacht und Erfahrungen daraus 
in Syntax und Semantik von C einfließen lassen.

Um C zu verstehen genügt es m.E. nicht, nur die Entwicklung nach C 
sich anzuschauen und die Schwächen und Lücken, sondern man muss zum 
Verständnis auch einen Blick zurück auf die C-Vorgänger wie B machen, 
und inwieweit C in Bezug auf diese einen Fortschritt und eine 
Entwicklung darstellte.

> Das Thema ist: Warum sollte es sich lohnen, die Qual auf sich
> zu nehmen, diese Sprache zu erlernen?

Wenn du bereits mit "Qual" reingehst, kann das m.E. nix werden.  Damit 
wirst du bestenfalls zum c-hater No. 2

> Ich habe in den vergangenen 30 Jahre ungefähr 3 Mal Anlauf
> genommen, C zu lernen, aber das ist jedes Mal steckengeblieben,
> weil ich mich nicht überwinden konnte, mir diesen (aus meiner
> laienhaften Sicht) kruden Scheiss einzutrichtern.

Die erste Frage ist, ob du es lernen musst wegen Job oder warum.  Wenn 
es "Nur" für Hobby oder Bastel ist, kann man sich auch eine Hardware 
suchen, in der es mehr Auswahl und Alternativen gibt wie Basic oder so.

Und dann: Etwas neues ist erst mal das: neu.  Es ist ungewohnt und nicht 
so gewohnt wie die alten, bequemen ausgelatschten Schalppen.

Txet und Cdoe nmimt man nciht als einlnzee Bcuhsebatn whar, snredon als 
Geanzs, wie ein Blid.  Nur dshealb knan man dseien Txet leesn — wnen 
acuh mit etaws Mhüe.

Bereits ein anderer Coding-Style (Einrückungen, Groß-Kleinschreibung, 
CanmelCase, (nicht-)Verwendung von Spaces) kann die Lesbarkeit massiv 
verändern wenn man dies nicht gewohnt ist — und zwar bei einer Sprache, 
die man bereits kann. Bei einer neuen Sprache, die visuell komplett 
anders rüberkommt wie "{" anstatt "begin" ist das erst mal der Hammer.

> ich suche gute (!) Argumente dafür, die Sprache zu lernen. Was
> also ist an der Sprache so toll?

Gegenfrage: Welches Problem willst du damit lösen?

Mein Hassgegner ist C++, wegen der abartigen Komplexität und der 
Intransparenz.  Aber neulich hab ich noch was mit C++ gemacht. 
Hilfreich ist auf jeden Fall, keinen Druck zu haben, ein Aufgabe, an der 
man Interesse hat und wo man den Grad der Drohnung selbst bestimmen 
kann.  Und ich bin echt heilfroh, auf Arbeit nicht mit irgendwelchen C++ 
Aposteln (oder sonstigen) zusammenarbeiten zu müssen. Ich verwende C, 
Assembler, C++, Java, Python, JavaScript je nach Gusto, für Hobby, um 
mir selber zuzuarbeiten oder für irgendwelche Nerd-Hacks. Aber ohne 
Motivation und zu lösendes "Problem" käm mir glcub nie in den Sinn, ne 
Sprache lernen zu wollen.

> Liebe Leute, dass ich die C-Syntax furchtbar finde, ist KEIN
> zwingender Beweis dafür, dass ich geistig minderbemittelt bin!

Wie gesagt, sie Syntax ist gar nicht sooo anders: Ausdrücke, Blöcke, 
Schleifen, If-Else, Funktionen, Variablen.  Ob ich eine Variable als 
"int x" oder "x : integer" angebe ist jetzt nicht wirklich das 
Killerkriterium.  Ja, es nervt wenn man nicht weiß "muss ich jetzt : 
oder ; oder ," aber das ist Übung.  Und wie gesagt, es ist einfach auch 
ein visuelles Ding.

Und manches ist einfach komisch. Isso.  Ich mach mir auch keine Gedanken 
darüber, warum es DER Löffel heißt, aber DIE Gabel und DAS Messer. 
Jemand, der Deutsch lernt, begreift das schlichtweg nicht und rauft sich 
die Haare drüber aus.  Ein Muttersprachler nimmt es noch nichtmal 
wahr...

von Wilhelm M. (wimalopaan)


Lesenswert?

Nop schrieb:

> Bei C++ bezahlst Du nicht für etwas, das Du nicht bestellt hast - aber
> Du mußt die riesige Speisekarte exakt kennen. Ansonsten ist das mit C++
> wie in einem Puff, wo zwar das Mineralwasser 2 Euro kostet, aber für das
> Gläschen Sekt mit der Bedienung biste 200 Euro los.
>
> Dafür haste die Garantie, daß Du mit C++ alles "safer" hast, solange Du
> nicht in den Keller gehst.

Das ist der Grund dafür, dass es eine ganze Menge Stimmen gibt, die 
sagen: "stop teaching C". Leider wird dieser Spruch von Kate Gregory oft 
falsch verstanden. Er bezieht sich nämlich nicht auf C, sondern auf C++, 
und meint, man solle aufhören, in C++ so wie in C zu programmieren. Und 
das wäre in der Tat gut so ... Allerdings sind die meisten aktuellen 
Bücher, Kurse, etc. nicht dazu geeignet, einen anderen Stil zu 
propagieren. Und vielen Leuten ist es viel zu anstrengend, lange über 
Typ-Systeme  bzw. Algebren nachzudenken. Stattdessen: alles ist ein int 
oder ein String, das passt schon ... Dabei ist die 
Abstraktionsmöglichkeit, die man in C++ hat, tatsächlich zero-overhead. 
Nur viele trauen dem Compiler irgendwie nicht: der echte Mann denkt halt 
immer noch, dass ein shift in C/C++ schneller sei als eine 
Multiplikation. Archaisch ...

Das Ergebnis des Compilers kann halt immer nur so gut sein wie die 
Abstraktion des Programmierers. Und da sich die Abstraktion eines 
Problems in den ADTs (der algebraischen Struktur) manifestiert, ist das 
der entscheidende Faktor. Und gerade in diesem Aspekt (Schaffung eines 
domänenspezifischen Typsystems und Typ-Manipulation) ist C++ m.E. 
derzeit unschlagbar ...

von Nop (Gast)


Lesenswert?

Wilhelm M. schrieb:

> Und gerade in diesem Aspekt (Schaffung eines
> domänenspezifischen Typsystems und Typ-Manipulation)

Vorsicht mit dem Hang zu DSLs. Die sind für das Problem zwar effizient, 
schaffen aber effektiv Inseln. Daran ist schon LISP gescheitert. Wenn 
ich mir so manche Betrachtungen zu C++ hier durchlese, erinnert mich das 
schon an "The Bipolar Lisp Programmer": 
http://www.marktarver.com/bipolar.html

C++ bekommt langsam auch noch ein anderes Problem von Lisp: Was in 
anderen Sprachen technische Probleme sind, wird in C++ langsam zu einem 
sozialen Problem. Naturgemäß sind insbesondere die Überflieger sich 
einer solchen Problematik weder bewußt, noch können sie sie handhaben.

von Possetitjel (Gast)


Lesenswert?

S. R. schrieb:

> Possetitjel schrieb:
>> Ich würde ja nix sagen, wenn man mit einer Compilerdirektive
>> zwischen einem "portablen" und einem "nativen" Modus hinundher-
>> schalten könnte - dazu müsste es aber erstmal einen portablen
>> Modus geben.
>
> Genau das hast du mit C doch.

Jein.

> Der Standard unterscheidet zwischen
> - specified ("portabel")
> - implementation-defined ("native")
> - undefined ("verboten")

Eine bestimmte Sprachkonstruktion fällt aber immer in genau
eine (und immer nur diese eine) Klasse, und ich muss für
jedes Konstrukt WISSEN, welche Klasse das ist.

Ich hatte aber im Sinn, dass man per Direktive im Quelltext
wählen kann, welche genaue Bedeutung dasselbe Sprachkonstrukt
haben soll. Das hätte den Vorteil, dass ich nicht den gesamten
Standard im Kopf haben muss, weil nämlich im Quelltext explizit
geschrieben steht, welche Variante jetzt zum Tragen kommt.

>> Mag sein, dass das primär ein Problem der Lehre, der
>> Vermittlung ist -- aber mir sind die Erklärungen, WARUM
>> die Syntax eben genau SO aussieht, wie sie aussieht,
>> i.d.R. deutlich zu dünn gesät.
>
> Syntax ist in aller Regel persönlichen Vorlieben geschuldet.

Ja -- aber nicht nur.
Natürliche Sprachen enthalten alle eine erhebliche Redundanz.

Wo ist diese Redundanz bei "*(++(x*--))"?
Und wo bei "x=(y==z))"?

> In Perl gibt es fast nur syntaktischen Zucker, was die
> Sprache gut zu schreiben und schlecht lesbar macht. In
> Python ist das umgekehrt. Wähle deine Präferenz.

Tcl. -- Nettes Beispiel :)

Zur Erläuterung: Ich bin zu Tcl gekommen wie die Jungfrau zum
Kind, nämlich durch eine Kette von Zufällen.
Bei Tcl geblieben bin ich
a) weil es Tk gibt und
b) weil Tcl Listen, "foreach" und reguläre Ausdrücke kennt.

Perl und Python waren damals (vor 20 Jahren) in der engeren
Wahl; Perl fiel wegen Unlesbarkeit durch, und an Python hat
mich die Einrückerei gestört. Tcl hat nur durch reinen Zufall
gewonnen.

Den Python/Tk-Quelltext, den ich neulich gesehen habe, fand
ich lesbar, aber ziemlich unschön.


> Ich wüsste gerne, was genau dein Problem mit C ist,

Hmm. Ich möchte die Portabilität von C ohne die Macken von
C. Gewisse Einbußen in der Performance wären akzeptabel.
Die ganze Esoterik der L-Values, die dreizehn Vorrangebenen
für Operatoren, die kranke Syntax sind nicht akzeptabel.

> außer dass du es hasst.

Nein, das stimmt nicht.
Ich bin nur allergisch auf C-Apologeten und C-Missionare.

Ich habe überhaupt nix gegen C -- solange ich nicht gezwungen
bin, es zu verwenden.

(Soll heißen: Auch wenn ich nicht wirklich verstehe, WOFÜR
es gut ist, gestehe ich doch zu, DASS es für irgend etwas
gut ist.)

> Nicht alles, was man nicht versteht, ist idiotisch und
> nicht alles, was idiotisch ist, war es immer.

Na logisch -- das ist ja gar nicht meine Aussage.

Ich kann durchaus nachvollziehen, warum Brian Kernighan und
Dennis Ritchie die eine oder andere Entscheidung genau SO und
nicht anders getroffen haben.
Ich verstehe nur nicht, warum wir das heute -- 45 Jahre später --
immer noch genauso machen müssen.

Schließlich kommt auch niemand auf die Idee, seinen Ruhmkorffschen
Apparat in Funktion zu setzen, wenn er abends sein Zimmer erhellen
möchte :)

von Wilhelm M. (wimalopaan)


Lesenswert?

Nop schrieb:
> Wilhelm M. schrieb:
>
>> Und gerade in diesem Aspekt (Schaffung eines
>> domänenspezifischen Typsystems und Typ-Manipulation)
>
> Vorsicht mit dem Hang zu DSLs.

Ein paar domänenspezifische DT sind noch keine DSEL!
Aber sie können verhindern, das Raumsonden verloren gehen ...

> C++ bekommt langsam auch noch ein anderes Problem von Lisp: Was in
> anderen Sprachen technische Probleme sind, wird in C++ langsam zu einem
> sozialen Problem. Naturgemäß sind insbesondere die Überflieger sich
> einer solchen Problematik weder bewußt, noch können sie sie handhaben.

Ich sehe eher, dass C++ aus Kompatibilitätsgründen viele problematische 
old-school Konstrukte ermöglicht, die auch gerade einem Anfänger das 
Leben schwer machen können (deswegen: stop teaching C (if you teach 
C++)). Und zum anderen ist es eine Sprache, die sehr lange existiert mit 
demzufolge einer teilweise mitgealterten User-Gemeinde mit eingeübten 
Idiomen, die sich neuen Stilen/Idiomen/Pattern nur schwer öffnet. Auf 
den aktuellen Konferenzen ist das zum Glück anders: viele junge Leute 
mit extrem spannenden, weil unkonvetionellen (im Wortsinn) Ansätzen.

von Nop (Gast)


Lesenswert?

Wilhelm M. schrieb:

> den aktuellen Konferenzen ist das zum Glück anders: viele junge Leute
> mit extrem spannenden, weil unkonvetionellen (im Wortsinn) Ansätzen.

Auf Konferenzen. oO Paralleluniversum.

Die Realität: obwohl C weitaus kompakter ist, hat auch das schon 
Probleme, und die Antwort darauf war Misra-C zum Subsetting. Nein, die 
Antwort war nicht, nur noch Language-Lawyer einzustellen, weil denen das 
Domänenwissen fehlt. Und wenn sie es doch haben, dann haste 
Rockstar-Programmierer, die erstens teuer sind und zweitens zuwenige.

Bei C++ wird auch Subsetting betrieben, aber überall anders und nicht 
standardisiert.

Die Folge ist eine Erosion des Subsettings. Sei es, weil man tatsächlich 
etwas außer dem Subset sinnvoll einsetzen kann, oder ganz profan zum 
Angeben, weil jemand ein cooles Feature aufgeschnappt hat. Die Folge 
ist, daß die Maintenance-Programmierer zusehends fluchen, weil die am 
Ende die Obermenge kennen müssen, und zwar soweit, daß sie den Kram noch 
debuggen können.

Zudem hast Du bei C++ außer den Experten, die tatsächlich wissen, was 
für Maschinencode rausfällt, die viel zahlreicheren, die das nicht 
wissen, sondern die effektiv guesstimate-programming betreiben. Sie 
wissen ungefähr, was wie funktioniert, mehr auch nicht. Ein Phänomen, 
was man ansonsten gerne Java-Programmierern vorwirft. Diese Art von 
C++-Programmierern hat Torvalds übrigens mit seinem bekannten Rant 
gemeint.

Und nein, diese Leute werden nicht ihre komplette Freizeit damit 
verbingen, mit dem neusten Standard mitzuhalten. Es gibt nämlich auch 
Menschen, die sowas wie Freunde oder gar Familie haben, oder Interessen 
außerhalb vom Programmieren. Kurzum, normale Leute, deren Teamfähigkeit 
eben deswegen auch schon ein wichtiger Faktor ist.

Du hast übrigens gerade sehr schön vorgeführt, was ich meinte. Du 
scheinst Dich mit C++ sehr gut auszukennen und hast sichtlich überhaupt 
nichts damit anfangen können, was ich mit den sozialen Problemen meinte. 
Auf Konferenzen. Ja nee, ist klar.

von Carl D. (jcw2)


Lesenswert?

Wilhelm lass es einfach. Dieser Thread ist nur noch dazu da, alt 
eingesessene "Wahrheiten" vorzutragen. Nutz deine Zeit für spannende 
neue Dinge.

von S. R. (svenska)


Lesenswert?

Possetitjel schrieb:
>> Der Standard unterscheidet zwischen
>> - specified ("portabel")
>> - implementation-defined ("native")
>> - undefined ("verboten")
>
> Eine bestimmte Sprachkonstruktion fällt aber immer in genau
> eine (und immer nur diese eine) Klasse, und ich muss für
> jedes Konstrukt WISSEN, welche Klasse das ist.

Ja. Das fällt aber unter "kenne deine Werkzeuge", und in C gibt es nicht 
besonders viele Konstrukte (im Gegensatz zu z.B. C++ oder Java). Du 
kannst auch nicht Auto fahren, ohne vorher gelernt zu haben, wie man ein 
Auto bedient.

> Ich hatte aber im Sinn, dass man per Direktive im Quelltext
> wählen kann, welche genaue Bedeutung dasselbe Sprachkonstrukt
> haben soll.

Du möchtest also effektiv mehrere Programmiersprachen mit leichten 
Bedeutungsunterschieden innerhalb einer Datei, zwischen denen du ständig 
wechseln kannst. Ich sehe darin keinen besonderen Vorteil (und mit 
Pragmen bekommst du das übrigens auch heute schon - sie sind nur nicht 
zwischen Compilern portabel).

> Das hätte den Vorteil, dass ich nicht den gesamten
> Standard im Kopf haben muss, weil nämlich im Quelltext explizit
> geschrieben steht, welche Variante jetzt zum Tragen kommt.

Und es hat den Nachteil, dass ich mir eine Codezeile "c = a + b" 
anschaue und es vom Kontext abhängig ist, was sie bedeutet. Ich kann mir 
also keine isolierten Patches anschauen und sie bewerten, sondern muss 
im Zweifelsfall die gesamte restliche Codebasis auch im Kopf haben.

>> Syntax ist in aller Regel persönlichen Vorlieben geschuldet.
>
> Ja -- aber nicht nur.
> Natürliche Sprachen enthalten alle eine erhebliche Redundanz.

Und als Seiteneffekt kannst du Widersprüche ausdrücken: "Dunkel war's, 
der Mond schien helle, als ein Auto blitzeschnelle langsam um die Ecke 
fuhr". Wie interpretierst du solche Aussagen eindeutig?

Redundanz ist in der Programmierung nicht unbedingt sinnvoll, denn wenn 
an sich äquivalente Aussagen sich unterscheiden, hast du ein Problem. 
Das gilt für Copy/Paste ebenso wie für Datenstrukturen mit redundantem 
Inhalt.

> Wo ist diese Redundanz bei "*(++(x*--))"?
> Und wo bei "x=(y==z))"?

Schlechte Ausdrucksweisen kannst du ebenfalls in jeder Sprache 
konstruieren: "Ey Alda komma her und krich auffe Fresse isch mach disch 
Messa" ist grammatisch auch nicht besser als ein Antipattern in der 
Programmiersprache deiner Wahl.

>> In Perl gibt es fast nur syntaktischen Zucker, was die
>> Sprache gut zu schreiben und schlecht lesbar macht. In
>> Python ist das umgekehrt. Wähle deine Präferenz.
>
> Tcl. -- Nettes Beispiel :)

Mit Tcl werde ich nicht unbedingt warm, da fehlt mir irgendwie Syntax. 
Ich habe mich damit aber auch nicht weiter befasst, als ich unbedingt 
musste (= Xilinx).

>> Ich wüsste gerne, was genau dein Problem mit C ist,
>
> Hmm. Ich möchte die Portabilität von C ohne die Macken von
> C. Gewisse Einbußen in der Performance wären akzeptabel.

Du willst also die Vorteile von C, ohne den Preis zu bezahlen.
Ich hätte auch gern einiges, ohne dafür bezahlen zu müssen.

> Die ganze Esoterik der L-Values, die dreizehn Vorrangebenen
> für Operatoren, die kranke Syntax sind nicht akzeptabel.

Für dich nicht akzeptabel. Ich bin z.B. im Gegensatz zu dir bereit, 
diesen Preis für die Vorteile von C zu zahlen.

Du bezahlst halt woanders dafür. Sei es, dass die Programmiersprache 
deiner Wahl deine Zielarchitektur nicht (oder nur schlecht) unterstützt, 
du dich in Abhängigkeit zu bestimmten Herstellern begibst, oder oder 
oder.

Es gibt halt nicht alles. C ist vorhanden, mit seinen Stärken und 
Schwächen, und Alternativen sind auch vorhanden, mit ihren Stärken und 
Schwächen. Die Lösung deiner Wahl musst du selbst finden oder 
entwickeln.

> Ich bin nur allergisch auf C-Apologeten und C-Missionare.

Es gibt offensichtlich genug Programmierer auf diesem Planeten, bei 
denen die Stärken von C die Schwächen von C hinreichend ausgleichen.

> Ich kann durchaus nachvollziehen, warum Brian Kernighan und
> Dennis Ritchie die eine oder andere Entscheidung genau SO und
> nicht anders getroffen haben.
> Ich verstehe nur nicht, warum wir das heute -- 45 Jahre später --
> immer noch genauso machen müssen.

Weil sich bestimmte Entscheidungen als gut bewährt haben, und viele 
schlechte Entscheidungen nicht so schlecht waren, als dass man sie nicht 
hätte reparieren können oder die guten Entscheidungen gleich mit 
wegwerfen.

Vergleiche mal antiken C-Code mit halbwegs modernem C-Code. Da liegen 
Welten zwischen. Wir machen das bei weitem nicht mehr genauso wie K&R in 
den 70ern. Zum Glück.

> Schließlich kommt auch niemand auf die Idee, seinen Ruhmkorffschen
> Apparat in Funktion zu setzen, wenn er abends sein Zimmer erhellen
> möchte :)

Wir schicken immernoch Strom durch einen Draht, um ihn zum Glühen zu 
bringen, um das Zimmer zu erhellen. Wir produzieren immernoch 
Explosionen in einem Stahlkasten, um uns zügig fortzubewegen. Wir 
benutzen immernoch "make", um Software zu bauen.

von Einer K. (Gast)


Lesenswert?

S. R. schrieb:
> Wir
> benutzen immernoch "make", um Software zu bauen.
Du!

von Wilhelm M. (wimalopaan)


Lesenswert?

Carl D. schrieb:
> Wilhelm lass es einfach. Dieser Thread ist nur noch dazu da, alt
> eingesessene "Wahrheiten" vorzutragen. Nutz deine Zeit für spannende
> neue Dinge.

Vermutlich hast Du Recht :-(

In diesem Forum ist es leider doch sehr verbreitet, wenn einem die 
Argumente ausgehen, sich auf die persönliche Ebene zu begeben, und dann 
dort irgendwelche abstrusen Vermutungen über die Persönlichkeit der 
anderen Teilnehmer anzustellen.

von Carl D. (jcw2)


Lesenswert?

Wilhelm M. schrieb:
> Carl D. schrieb:
>> Wilhelm lass es einfach. Dieser Thread ist nur noch dazu da, alt
>> eingesessene "Wahrheiten" vorzutragen. Nutz deine Zeit für spannende
>> neue Dinge.
>
> Vermutlich hast Du Recht :-(
>
> In diesem Forum ist es leider doch sehr verbreitet, wenn einem die
> Argumente ausgehen, sich auf die persönliche Ebene zu begeben, und dann
> dort irgendwelche abstrusen Vermutungen über die Persönlichkeit der
> anderen Teilnehmer anzustellen.

Besonder lustig:
"ich verstehe die Sprache nicht, aber hab schon viel (von 
meinesgleichen) gehört, daß sie besch...en ist".

Dann steht da was von "C-Missionaren".
Niemand zwingt jemandem, der lieber Pascal schreibt, C auf. Aber der 
Pascal-Fan muß dann sein Problem "Pascal430" sebst lösen.

Ursprungsthema "0-Pointer in C",
Nun nur noch "deshalb ist alles, was ich versteh, das Beste".
Aber man kennt ja im Lauf der Zeit die Protagonisten.

von Nop (Gast)


Lesenswert?

Wilhelm M. schrieb:

> In diesem Forum ist es leider doch sehr verbreitet, wenn einem die
> Argumente ausgehen

So wie Dir. Ich nannte Dir einige Punkte, und Du gehst lieber auf nichts 
ein. Wenn Du Dir Dein C++-Getrolle künftig mal sparst, werde ich es 
nicht vermissen.

von Einer K. (Gast)


Lesenswert?

Theoretisch könntet ihr euch in Grabenkämpfen zerfleischen, bis nur 
einer übrig bleibt, und der behält dann automatisch und widerspruchslos 
recht.

Aber in der Realität, wird es immer abweichende Meinungen, Anforderungen 
und Vorlieben geben.

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.