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.
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.
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.
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.
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; ...
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).
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.
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.
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.
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.
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.
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#l725IUnknown 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.
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)
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.
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.
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.
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.
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.
Detlev T. schrieb:> @Carl Drexler>> RAM != EEPROM>> Beim AVR haben Flash, RAM und EEPROM getrennte Adressbereiche, alle> fangen mit Adresse 0 an.
Echt jetzt?
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
intx=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.
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?
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.
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.
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.
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.htmlhttp://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.
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.
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.
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
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.
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
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?
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/
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
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.
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 )
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.
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.
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.
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).
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:
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.
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.
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.
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.
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.
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.
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.
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.
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.
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).
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.
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.
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.
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.
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".
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.
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.
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.
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?!
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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!
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.
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.
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.
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?
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.
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.
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.
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.
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".
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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
IFvar>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...
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 ...
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.
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 :)
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.
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.
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.
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.
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.
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.
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.