Forum: Mikrocontroller und Digitale Elektronik Art der if-Auswahl nur Geschmackssache?


von __Son´s B. (bersison)


Lesenswert?

Hallo!
Habe hier 2 Lösungen für die selbe Aufgabe.
Ist es "reine Geschmackssache", oder gibt es bei der einen oder anderen 
Lösung ernsthafte Einwände?
1
//_1________________________________________
2
void EnergieRestAmpel(uint16_t RestZeit)
3
{
4
if(RestZeit >= ENERGIE_SCHWELLE_OBEN)
5
{
6
 LED_ENERGIE_GN_ON;              
7
 return;
8
}
9
if(RestZeit <= ENERGIE_SCHWELL_UNTEN)
10
{
11
 LED_ENERGIE_RT_ON;              
12
 return;
13
}
14
 LED_ENERGIE_GE_ON;              
15
}
16
17
//_2_________________________________________
18
void EnergieRestAmpel(uint16_t RestZeit)
19
{  
20
uint8_t Prio=3;
21
if(RestZeit >= ENERGIE_SCHWELLE_OBEN)  Prio=2;
22
if(RestZeit <= ENERGIE_SCHWELL_UNTEN)  Prio=1;
23
  
24
switch(Prio)
25
{
26
case 1:
27
 LED_ENERGIE_RT_ON;  
28
 break;
29
case 2:
30
 LED_ENERGIE_GN_ON;    
31
 break;
32
default:
33
 LED_ENERGIE_GE_ON;    
34
}
35
}
___________________________________________
Ich persönlich favorisiere die switch-Lösung aufgrund der tabellarischen 
Übersicht. Vor allem wenn mehr als 2 Vergleiche erfolgen. Nur muss 
einmalig die Priorität (von unten nach oben) festgelegt werden.

: Bearbeitet durch Moderator
von Walter T. (nicolas)


Lesenswert?

Such es Dir aus. Implementiere es. Du wirst im Laufe Deine Projekts 
ohnehin noch das eine oder andere mehrmals ändern, weil Dir am Anfang 
jede Woche die Vorteile der einen oder der anderen Lösung mehr liegen.

von ... (Gast)


Lesenswert?

void EnergieRestAmpel(const uint16_t RestZeit)
{
   if(RestZeit >= ENERGIE_SCHWELLE_OBEN)
      LED_ENERGIE_GN_ON;
   else if(RestZeit <= ENERGIE_SCHWELL_UNTEN)
      LED_ENERGIE_RT_ON;
   else
      LED_ENERGIE_GE_ON;
}

Natürlich müssen diese sinnlosen #defines ordentlich aussehen.
Sinnlos, weil man dafür auch einfach Funktionen nutzen kann.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

__Son´s B. schrieb:
1
void EnergieRestAmpel(uint16_t RestZeit)
2
{
3
if(RestZeit >= ENERGIE_SCHWELLE_OBEN)
4
{
5
 LED_ENERGIE_GN_ON;
6
 return;
7
}
8
if(RestZeit <= ENERGIE_SCHWELL_UNTEN)
9
{
10
 LED_ENERGIE_RT_ON;
11
 return;
12
}
13
LED_ENERGIE_GE_ON; // <= here
14
}
Lies das nochmal. Überlege welchen Sinn die Kombination >= und <= macht. 
Und wie oft die markierte zeile ausgeführt wird.

Hint: findige Software-Ingenöre haben "else" erfunden

von Possetitjel (Gast)


Lesenswert?

Michael R. schrieb:

> Lies das nochmal. Überlege welchen Sinn die Kombination
> >= und <= macht. Und wie oft die markierte zeile ausgeführt
> wird.

???

Zuviel Blut im Kaffee?

Die markierte Zeile wird ausgeführt, wenn RestZeit (echt)
kleiner als ENERGIE_SCHWELLE_OBEN, aber noch (echt) größer
als ENERGIE_SCHWELLE_UNTEN ist.

von Thomas H. (beon)


Lesenswert?

Im Endeffekt machen beide Codes das Gleiche. Meiner Meinung nach ist es 
Geschmackssache. Zitat von einem unserer Informatiker: "Code soll für 
einen selbst übersichtlich und lesbar sein. Optimierung macht der 
Compiler".

von Matthias M. (Firma: privat) (quadraturencoder)


Lesenswert?

switch() case statements sind normalerweise für Auflistungen gedacht. 
Mit enums kann man so z.B. eine Warnung generieren, wenn das 'case' 
Statement für einen später eingefügten Fall fehlt. Sehr hilfreich.

Dein Codebeispiel ist ok, aber das 'return' mitten im Code is Spaghetti 
und sollte vermieden werden. Wenn Du Jahre später mal am Ende der 
Funktion code hinzufügst könntest Du das eventuell übersehen.

IMHO ist es so besser:

void EnergieRestAmpel(uint16_t RestZeit)
{
  if (RestZeit >= ENERGIE_SCHWELLE_OBEN)
  {
    LED_ENERGIE_GN_ON;
  }
  else if(RestZeit <= ENERGIE_SCHWELL_UNTEN)
  {
    LED_ENERGIE_RT_ON;
  }
  else
  {
    LED_ENERGIE_GE_ON;
  }
}

Die geschwungenen Klammern kannst DU dann auch weglassen, und die Idee, 
die hinter der Formel steckt wird offensichtlicher. So würdest Du das 
Problem umgangssprachlich wohl auch ausdrücken: Wenn die Restzeit 
grösser ist, dann mach das, ansonsten wenn die Zeit kleiner ist, mach 
jenes, und wenn keines der beiden zutrifft, mach sonstwas.

von Peter D. (peda)


Lesenswert?

Der GCC erlaubt auch Switch mit Bereichen:
1
switch(RestZeit)
2
{
3
  case 0 ... ENERGIE_SCHWELL_UNTEN:
4
    LED_ENERGIE_RT_ON;
5
    break;
6
  case ENERGIE_SCHWELL_UNTEN + 1 ... ENERGIE_SCHWELLE_OBEN:
7
    LED_ENERGIE_GN_ON;
8
    break;
9
  default:
10
    LED_ENERGIE_GE_ON;
11
}

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Michael R. schrieb:
> Lies das nochmal.

Dito. Man könnte kritisieren, das die obere Schwelle 
'ENERGIE_SCHWELLE_OBEN' heisst, während die untere 
'ENERGIE_SCHWELL_UNTEN' heisst, da hat der Autor eben SCHWELL statt 
SCHWELLE geschrieben. Das ist aber auch schon alles. Zwischen den 
Schwellen passiert nun mal nichts.

von Possetitjel (Gast)


Lesenswert?

__Son´s B. schrieb:

> Habe hier 2 Lösungen für die selbe Aufgabe.
> Ist es "reine Geschmackssache",

Fast, aber nicht ganz, würde ich sagen.

> Ich persönlich favorisiere die switch-Lösung aufgrund
> der tabellarischen Übersicht. Vor allem wenn mehr als
> 2 Vergleiche erfolgen.

Ja.
Die zweite Variante ist ein ziemlich universelles Muster,
wie man M Variablen auf N Fallklassen abbilden kann. Die
if-Abfragen oben ermitteln erstmal die Fallnummer; der
switch ruft dann den Handler für den jeweiligen Fall auf.
Ich verwende das immer dann, wenn ich eine einigermaßen
feststehende, a priori bekannte Anzahl von Fällen habe,
aus denen zu jedem Zeitpunkt genau einer zutrifft.

Die erste Konstruktion benutze ich für Abläufe (endliche
Automanten, "state machines"). In der Steuerungstechnik
kennt (kannte) man das als Schrittkette (Ablaufkette).
Hier kann man ziemlich viele Zustände haben, deren Anzahl
sich auch mal ändern kann; ich will dann kein switch
haben, was über mehrere Seiten geht.

Geschachtelte if-then-else verwende ich nie, dazu ist
mein IQ zu niedrig.

von Tom (Gast)


Lesenswert?

Was Du tust, ist 1) aus der RestZeit ein "Warnlevel" zu generieren und 
2) das WarnLevel anzuzeigen.

Wenn man das übertrieben sauber trennen will, könnte man zuerst die 
RestZeit in irgendeinen abstrakten Zustand übersetzen und dann diesen 
irgendwie anzeigen. Dein zweiter Ansatz geht ein wenig in diese 
Richtung, wobei mir die "Priorität" nicht ganz klar ist.
1
// energy_analysis.h + .c
2
typedef enum {ENERGY_OK, ENERGY_WARNING, ENERGY_CRITICAL} eEnergyState;
3
4
eEnergyState classify_energy_level(uint16_t RestZeit)
5
{
6
    if(RestZeit >= ENERGIE_SCHWELLE_OBEN)
7
        return ENERGY_OK;
8
    if(RestZeit <= ENERGIE_SCHWELL_UNTEN)
9
        return ENERGY_CRITICAL;
10
    return ENERGY_WARNING;
11
}
12
13
// hmi.h + .c
14
void displayEnergyLevel(eEnergyState e)
15
{
16
    switch(e)
17
    {
18
    case ENERGY_OK: LED_ENERGIE_GN_ON; break;
19
    case ENERGY_WARNING: LED_ENERGIE_GE_ON; break;
20
    case ENERGY_CRITICAL: LED_ENERGIE_RT_ON; break;
21
    }
22
}
23
24
// main.c
25
void DoTheAmpel(uint16_t RestZeit)
26
{
27
    eEnergyState s = classify_energy_level(RestZeit);
28
    displayEnergyLevel(s);
29
}
In diesem einfachen Fall ist das wahrscheinlich in dieser Form Quatsch. 
Wenn es groß und kompliziert wird, macht man sich aber das Leben auf 
Dauer einfacher, wenn man Aufgaben strikt auf Module aufteilt und 
Querabhängigkeiten zwischen diesen vermeidet.

von Peter D. (peda)


Lesenswert?

Possetitjel schrieb:
> Geschachtelte if-then-else verwende ich nie, dazu ist
> mein IQ zu niedrig.

Geht mir auch so.
Ich finde die extrem unlesbar.

von Karl (Gast)


Lesenswert?

Peter D. schrieb:
> Ich finde die extrem unlesbar.

Mit einer ordentlichen Programmiersprache sind die auch gut lesbar. Darf 
man halt nicht diesen Klammermist nehmen.

Thomas H. schrieb:
> Im Endeffekt machen beide Codes das Gleiche.

Die Varianten kompilieren durchaus unterschiedlich. Bei if ... else 
stehen die auszuführenden Anweisungen direkt hinter dem Vergleich, dann 
folgt der nächste Vergleich. Bei case stehen alle Vergleiche zusammen, 
von dort wird zu den auszuführnenden Anweisungen gesprungen.

Ich hab auch schon ein Kompilat gesehen, wo ein case mit aufsteigender 
Nummerierung (also a = 1, 2, 3) derart aufgelöst wurde, dass anhand a 
eine Adresse einer Lookup-Table berechnet wurde, in der dann die 
Sprungadresse zum auszuführenden Code stand. Das geht aber nur mit 
Aufzählungen, nicht mit Bereichen oder <> Vergleichen.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Possetitjel schrieb:
> Zuviel Blut im Kaffee?
>
> Die markierte Zeile wird ausgeführt, wenn RestZeit (echt)
> kleiner als ENERGIE_SCHWELLE_OBEN, aber noch (echt) größer
> als ENERGIE_SCHWELLE_UNTEN ist.

Ja. zuviel Blut im Kaffee ;-) sorry, überlesen

von W.A. (Gast)


Lesenswert?

Michael R. schrieb:
> Lies das nochmal. Überlege welchen Sinn die Kombination >= und <= macht.

Lies das nochmal.
ENERGIE_SCHWELLE_OBEN und ENERGIE_SCHWELL_UNTEN sind vermutlich 
verschieden.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

W.A. schrieb:
> Lies das nochmal.
> ENERGIE_SCHWELLE_OBEN und ENERGIE_SCHWELL_UNTEN sind vermutlich
> verschieden.

Ja, schon gut :-) hab das analysiert. Mein Brain 1.9.68 hat Erkennung 
von Wörtern in GROSSBUCHSTABEN auf char[15] definiert.

Und wo krieg ich jetzt ein Update her? Cheffe mein, 2.0 wäre nicht, zu 
viele Subsysteme die das nicht mitmachen...

von Egon N. (egon2321)


Lesenswert?

Peter D. schrieb:
> Possetitjel schrieb:
>> Geschachtelte if-then-else verwende ich nie, dazu ist
>> mein IQ zu niedrig.
>
> Geht mir auch so.
> Ich finde die extrem unlesbar.

Else If hat aber den Vorteil, dass man sofort sieht, dass sich beide 
Segmente ausschließen.

Das ist mit zwei If hintereinander nicht sauber definiert.

Die Switch Case mit der vorherigen If Abfrage ist meiner Meinung nach 
unschön, fehleranfällig und unübersichtlich. Es sollte zu jeder Zeit 
klar sein was passiert, sprich man sollte grob die Funktion erkennen 
können obwohl man maximal Zeile+-1 sieht.

BTW: Ich bin kein Fan von deutschen Bezeichnungen. ein UPPER_LIMIT bzw. 
LOWER_LIMIT ist da schöner, kann man auch noch abkürzen usw. Energie 
z.B. mit E reinnehmen. 40 Zeichen lange Defines will man ehrlich gesagt 
nicht.

: Bearbeitet durch User
von Jobst Q. (joquis)


Lesenswert?

__Son´s B. schrieb:
> Ist es "reine Geschmackssache", oder gibt es bei der einen oder anderen
> Lösung ernsthafte Einwände?

Nicht nach meinem Geschmack ist auf jeden Fall das

> LED_ENERGIE_RT_ON;

Was soll das sein? Eine Konstante wie die meisten #defines ist es 
jedenfalls nicht. Damit sich überhaupt etwas tut, muss es wohl eine 
Funktion sein.
Dann würde ich das aber zumindest kenntlich machen:

LED_ENERGIE_RT_ON ();

Noch besser fände ich es allerdings, Funktionen nicht hinter #defines zu 
verstecken.

von Hunsbuckel (Gast)


Lesenswert?

Mit den ganzen -wo auch immer- deklarierten und initialisierten 
Variablen und Funktionen ( wie JoQuis schon zutreffend anmerkte ) kann 
das sowieso keine Sau richtig lesen!

Ich nenne sowas blah - blah - Code !!!

von Possetitjel (Gast)


Lesenswert?

Egon N. schrieb:

> Peter D. schrieb:
>> Possetitjel schrieb:
>>> Geschachtelte if-then-else verwende ich nie, dazu ist
>>> mein IQ zu niedrig.
>>
>> Geht mir auch so.
>> Ich finde die extrem unlesbar.
>
> Else If hat aber den Vorteil, dass man sofort sieht,
> dass sich beide Segmente ausschließen.
>
> Das ist mit zwei If hintereinander nicht sauber definiert.

Da ich durch Tcl geprägt bin, würde ich sowieso in jedem
Fall schreiben:

if(bedingung1) {
  aktion;
}

if(bedingung2) {
  andere_aktion;
}

Nach meinem laienhaften Verständnis der C-Syntax müsste das
eindeutig sein.
Das sind halt zwei isolierte Statements und gut.

Das Problem bei if/elseif ist, dass es zwar syntaktisch
eine Anweisung bleibt, man aber mehrere unabhängige
Bedingungen abprüfen kann, und das ist Teufelswerk.

Ganz spaßig wird es, wenn man in den then-Zweigen weitere
if-Abfragen unterbringt -- das ist dann genau der Fall,
den Peter als unlesbar bezeichnet.

> Die Switch Case mit der vorherigen If Abfrage ist meiner
> Meinung nach unschön, fehleranfällig und unübersichtlich.

Da bin ich eben komplett anderer Meinung.

Ein Problem, bei dem M Variablen zu N unterschiedlichen
Fällen führen, ist schon rein inhaltlich immer ein großer
Matschklumpen.
Ich finde es bei dieser Konstellation gut, erstmal zu
ermitteln "Welcher Fall liegt eigentlich vor?" (das passiert
in dem Block mit den if-Abfragen), und wenn man das weiss,
kann man den Fall anhand der Fallnummer behandeln, das macht
die switch-Anweisung.

Die Schnittstelle zwischen beiden besteht nur in der Fallnummer.

> Es sollte zu jeder Zeit klar sein was passiert, sprich man
> sollte grob die Funktion erkennen können obwohl man maximal
> Zeile+-1 sieht.

Das ist zwar wünschenswert, aber nicht realistisch.

von A. S. (Gast)


Lesenswert?

Hier ist die erste Lösung deutlich kürzer.

Die zweite benötigt ein weiteres Token (Prio).

Prinzipiell ist die zweite okay, hier bläht sie unnötig auf.

von S. R. (svenska)


Lesenswert?

Possetitjel schrieb:
> Da ich durch Tcl geprägt bin, würde ich sowieso in jedem
> Fall schreiben:
>
> if(bedingung1) {
>   aktion;
> }
>
> if(bedingung2) {
>   andere_aktion;
> }

Wenn "bedingung1" und "bedingung2" sich nicht gegenseitig ausschließen, 
dann werden beide Aktionen ausgeführt. Das ist nicht überall gewünscht, 
insbesondere hier nicht.

Außerdem muss in deinem Code "bedingung2" auch dann ausgewertet werden, 
wenn "bedingung1" schon zutraf.

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


Lesenswert?

__Son´s B. schrieb:
> Ich persönlich favorisiere die switch-Lösung aufgrund der tabellarischen
> Übersicht.

Ich nicht. Ich finde es im Gegenteil vollkommen bescheuert, erst eine 
Hilfsvariable per if-Kaskade zu setzen und dann ein switch() Statement 
zur Auswertung der Hilfsvariable zu verwenden. Das führt garantiert zu 
längerem Code und ist kein bißchen übersichtlich, weil man jetzt an 
zwei Stellen nachsehen muß - wo die Hilfsvariable gesetzt wird und wo 
sie ausgewertet wird.

Persönlich würde ich das als if .. else if ... else if ... else Kaskade 
schreiben. Vernünfig eingerückt und geklammert ist das sehr gut lesbar - 
zumindest so lange alles auf einen Bildschirmseite paßt. Das 
standardmäßige switch() Statement paßt halt nicht, weil das nur gegen 
Konstanten vergleichen kann. Auf eine gcc-Erweiterung sollte man im 
Sinne der Portabilität verzichten.

Wenn es nicht auf eine Seite paßt; etwa weil doch mehr Code nach jedem 
if kommt, und wenn das tatsächlich so verkapselt ist, daß es keinen 
gemeinsamen Code nach der Verzweigung gibt, dann ist die Variante mit 
abgeschlossenen if () { ...; return; } Blöcken auch ok. Das Argument 
"Spaghetti" greift hier nicht, denn es geht ja nur darum, einen 
Codeblock (den Funktionsrumpf) geordnet zu verlassen.

von Possetitjel (Gast)


Lesenswert?

S. R. schrieb:

> Possetitjel schrieb:
>> Da ich durch Tcl geprägt bin, würde ich sowieso in jedem
>> Fall schreiben:
>>
>> if(bedingung1) {
>>   aktion;
>> }
>>
>> if(bedingung2) {
>>   andere_aktion;
>> }
>
> Wenn "bedingung1" und "bedingung2" sich nicht gegenseitig
> ausschließen, dann werden beide Aktionen ausgeführt. Das
> ist nicht überall gewünscht, insbesondere hier nicht.

???

(Achtung -- kein C, sondern Tcl:)
1
 
2
set RestLaufzeit 0 
3
4
if { $RestEnergie >= ENERGIE_SCHWELLE_UNTEN } then { 
5
  set RestLaufzeit 1 
6
} 
7
8
if { $RestEnergie >= ENERGIE_SCHWELLE_OBEN } then { 
9
  incr RestLaufzeit 
10
}

Ich sehe das Problem nicht.


> Außerdem muss in deinem Code "bedingung2" auch dann
> ausgewertet werden, wenn "bedingung1" schon zutraf.

Selbstverständlich. Soll ja.

von Possetitjel (Gast)


Lesenswert?

Axel S. schrieb:

> Ich finde es im Gegenteil vollkommen bescheuert, erst
> eine Hilfsvariable per if-Kaskade zu setzen und dann
> ein switch() Statement zur Auswertung der Hilfsvariable
> zu verwenden.

Steht Dir frei. Alles kann, nichts muss ;)


> Das führt garantiert zu längerem Code

Mag sein -- aber auch zu intakteren Zähnen und Tischkanten.
(Zumindest bei mir.)


> und ist kein bißchen übersichtlich,

Sehr mutig, dass Du so genau weisst, was ich übersichtlich
finde.


> weil man jetzt an zwei Stellen nachsehen muß - wo die
> Hilfsvariable gesetzt wird und wo sie ausgewertet wird.

Das ist für mich keine echte Komplikation.

In wirklich richtig komplizierten Fällen -- und nur über
die lohnt es sich zu diskutieren -- muss ich sowieso über
zwei getrennte Fragen nachdenken:

1. Woran erkenne ich den jeweiligen Fall?
2. Welche Aktion ist im jeweiligen Fall notwendig?

Der Quelltext widerspiegelt somit nur, wie mein Gehirn
funktioniert.


> Persönlich würde ich das als if .. else if ... else if ... else
> Kaskade schreiben. Vernünfig eingerückt und geklammert ist
> das sehr gut lesbar - zumindest so lange alles auf einen
> Bildschirmseite paßt.

Im konkreten Beispiel stimmt das -- weil sich nämlich beide
Bedingungen auf dieselbe Variable beziehen. Es ist also aus
inhaltlichen Gründen sowieso sichergestellt, dass sich die
Fälle gegenseitig ausschließen.

Wenn mehrere Variablen mit voneinander abhängigen Bedingungen
eine Rolle spielen, ist das aber keineswegs mehr so.

von Karl (Gast)


Lesenswert?

Possetitjel schrieb:
> Nach meinem laienhaften Verständnis der C-Syntax müsste das
> eindeutig sein.
> Das sind halt zwei isolierte Statements und gut.

Nach meinem Verständnis ist das Schrott. Die Statements gehören im 
obigen Beispiel zusammen und dann kann man sie auch zusammen behandeln. 
Das in mehrere ifs aufzuteilen ist nur fehleranfällig.

> Das Problem bei if/elseif ist, dass es zwar syntaktisch
> eine Anweisung bleibt, man aber mehrere unabhängige
> Bedingungen abprüfen kann, und das ist Teufelswerk.

Isses schon so schlimm wie goto, oder nur so schlimm wie upn?

Der GROSSE Vorteil von if ... else ist, dass man damit sehr effizient 
Abfragen zusammenfassen kann, die der Compiler dann sehr effizient in 
Maschinencode umsetzt. Wer diesen Vorteil gerade auf µCs nicht nutzt, 
weil er davon Kopfschmerzen bekommt, sollte vielleicht besser Bäcker 
werden.

> Ganz spaßig wird es, wenn man in den then-Zweigen weitere
> if-Abfragen unterbringt -- das ist dann genau der Fall,
> den Peter als unlesbar bezeichnet.

Aus aktuellem Anlass:
1
procedure rtc_checksummer();
2
begin
3
  if rtc.summ and Rsauto <> 0 then begin  // prüfen auf Flag Auto
4
    if rtc.wday = 7 then begin  // wenn Sonntag
5
      if rtc.day >= $25 then begin  // wenn letzter Sonntag im Monat
6
        if rtc.month = $03 then begin  // wenn März, Umschalten auf Sommerzeit
7
          if (rtc.summ and Rsumm) = 0 then begin  //wenn Flag nicht gesetzt
8
            if rtc.hh = $02 then begin
9
              rtc.hh := $03;
10
              rtc.summ := Rsauto or Rsumm;
11
              rtc_setsummer();  // Sommerzeit schreiben
12
            end;
13
          end;
14
        end else if rtc.month = $10 then begin  // wenn Oktober, Umschalten auf Winterzeit
15
          if (rtc.summ and Rsumm) <> 0 then begin  //wenn Flag gesetzt
16
            if rtc.hh = $03 then begin
17
              rtc.hh := $02;
18
              rtc.summ := Rsauto;
19
              rtc_setsummer();  // Winterzeit schreiben
20
            end;
21
          end;
22
        end;
23
      end;
24
    end;
25
  end;
26
end;


Was bitte ist daran unlesbar? Und, schon Kopfschmerzen?

: Bearbeitet durch Moderator
von Possetitjel (Gast)


Lesenswert?

Karl schrieb:

> Possetitjel schrieb:
>> Nach meinem laienhaften Verständnis der C-Syntax müsste
>> das eindeutig sein.
>> Das sind halt zwei isolierte Statements und gut.
>
> Nach meinem Verständnis ist das Schrott.

Das ist keine sachlich begründete Aussage und daher nicht
hilfreich.


> Die Statements gehören im obigen Beispiel zusammen und
> dann kann man sie auch zusammen behandeln.

Das ist zwar richtig, aber eine Besonderheit des konkreten
Beispiels .

Es gibt jedoch Fälle, in denen das nicht so ist, und dann
ist die Formulierung als unabhängige Bedingungen nützlich.
Nichts anderes wollte ich ausdrücken.


>> Das Problem bei if/elseif ist, dass es zwar syntaktisch
>> eine Anweisung bleibt, man aber mehrere unabhängige
>> Bedingungen abprüfen kann, und das ist Teufelswerk.
>
> Isses schon so schlimm wie goto, oder nur so schlimm
> wie upn?

Was ist upn?

Davon abgesehen: Dein gutes Gedächtnis beeindruckt -- aber
ich bin nicht sicher, inwieweit Du verstehst, was Du liest.
Es gibt einen sachlichen Zusammenhang zwischen der goto-
Diskussion und dieser hier.
Das war mir gar nicht aufgefallen; ich danke für den
Hinweis.

> Der GROSSE Vorteil von if ... else ist, dass man damit
> sehr effizient Abfragen zusammenfassen kann, die der
> Compiler dann sehr effizient in Maschinencode umsetzt.

Irrelevant. Optimierende Compiler existieren. Du bist auf
dem Stand von vor 50 Jahren stehengeblieben.


> Aus aktuellem Anlass:procedure rtc_checksummer();
> begin
>   if rtc.summ and Rsauto <> 0 then begin  // prüfen auf Flag Auto
>     if rtc.wday = 7 then begin  // wenn Sonntag
>       if rtc.day >= $25 then begin  // wenn letzter Sonntag im Monat
>         if rtc.month = $03 then begin  // wenn März, Umschalten auf
> Sommerzeit
>           if (rtc.summ and Rsumm) = 0 then begin  //wenn Flag nicht
> gesetzt
>             if rtc.hh = $02 then begin
>               rtc.hh := $03;
>               rtc.summ := Rsauto or Rsumm;
>               rtc_setsummer();  // Sommerzeit schreiben
>             end;
>           end;
>         end else if rtc.month = $10 then begin  // wenn Oktober,
> Umschalten auf Winterzeit
>           if (rtc.summ and Rsumm) <> 0 then begin  //wenn Flag gesetzt
>             if rtc.hh = $03 then begin
>               rtc.hh := $02;
>               rtc.summ := Rsauto;
>               rtc_setsummer();  // Winterzeit schreiben
>             end;
>           end;
>         end;
>       end;
>     end;
>   end;
> end;
>
> Was bitte ist daran unlesbar? Und, schon Kopfschmerzen?

Das ist nicht nur unlesbar, das ist hässlich. Dabei wäre es
so einfach, es besser zu machen.
1
 
2
set SwitchToSummertime true 
3
... 
4
if { rtc.wday != 7 }  then { set SwitchToSummertime false } 
5
if { rtc.day < 25 }   then { set SwitchToSummertime false } 
6
if { rtc.month != 3 } then { set SwitchToSummertime false } 
7
...

Man kann ein wired-AND auf die unterschiedlichsten Arten
ausdrücken.

von Harlekin (Gast)


Lesenswert?

Possetitjel schrieb:
> set SwitchToSummertime true
> ...
> if { rtc.wday != 7 }  then { set SwitchToSummertime false }
> if { rtc.day < 25 }   then { set SwitchToSummertime false }
> if { rtc.month != 3 } then { set SwitchToSummertime false }
> ...

Danke. Wieder was gelernt.

Gibt es hierzu empfehlenswerte Bücher?

von Rolf M. (rmagnus)


Lesenswert?

Possetitjel schrieb:
> Das Problem bei if/elseif ist, dass es zwar syntaktisch
> eine Anweisung bleibt, man aber mehrere unabhängige
> Bedingungen abprüfen kann, und das ist Teufelswerk.

Warum?

>> Die Switch Case mit der vorherigen If Abfrage ist meiner
>> Meinung nach unschön, fehleranfällig und unübersichtlich.
>
> Da bin ich eben komplett anderer Meinung.

Ich finde sie nur unnötig aufgebläht. Es steht quasi doppelt da.

> Ich finde es bei dieser Konstellation gut, erstmal zu
> ermitteln "Welcher Fall liegt eigentlich vor?" (das passiert
> in dem Block mit den if-Abfragen), und wenn man das weiss,
> kann man den Fall anhand der Fallnummer behandeln, das macht
> die switch-Anweisung.

"Fallnummer"? Also wenn man das so macht, dann sollte man (ähnlich wie 
Tom es gezeigt hat) den Fällen auch Namen geben. Irgendwelche 
Magic-Numbers machen es alles andere als übersichtlicher. Dann muss ich 
beim Lesen des switch/case erst wieder oben nachschauen, welches 
Ergebnis der if-Abfrage denn nun zu Fall Nummer 3 gehört, was den 
Lesefluss meiner Meinung nach stark stört.

Possetitjel schrieb:
> In wirklich richtig komplizierten Fällen -- und nur über
> die lohnt es sich zu diskutieren -- muss ich sowieso über
> zwei getrennte Fragen nachdenken:
>
> 1. Woran erkenne ich den jeweiligen Fall?
> 2. Welche Aktion ist im jeweiligen Fall notwendig?
>
> Der Quelltext widerspiegelt somit nur, wie mein Gehirn
> funktioniert.

Hmm, dann funktionieren unsere Gehirne verschieden ;-)
Wenn ich das im Kopf durchgehen würde, würde ich sagen:

Wenn der Wert kleiner ist als die Untergrenze, dann soll die rote LED 
angehen. Wenn er stattdessen größer ist als die Obergrenze, soll die 
grüne LED angehen, und sonst die gelbe. Das würde der if/else-Kaskade 
entsprechen.
Die switch/case-Variante, die dann deiner Gehirnfunktion entsprechen 
müsste, würde ich so lesen:

Wenn nichts anderes gilt, haben wir Fall 3. Wenn der Wert kleiner ist 
als die Untegrenze ist, ist das Fall 1. Wenn er größer ist als die 
Obergrenze, ist das Fall 2.
In Fall 1 mache die rote LED an, in Fall 2 die grüne und in Fall 3 die 
gelbe.

Da hab ich ja im zweiten Satz schon vergessen, welcher Fall im ersten 
Satz welcher Bedingung zugeordnet war.

> Im konkreten Beispiel stimmt das -- weil sich nämlich beide
> Bedingungen auf dieselbe Variable beziehen. Es ist also aus
> inhaltlichen Gründen sowieso sichergestellt, dass sich die
> Fälle gegenseitig ausschließen.
>
> Wenn mehrere Variablen mit voneinander abhängigen Bedingungen
> eine Rolle spielen, ist das aber keineswegs mehr so.

Ja, ab einer gewissen Komplexität ist die Trennung durchaus sinnvoll. 
Aber zwingend ist für mich dann auch wieder, dass keine magischen 
Fallnummern verwendet werden, sondern aussagekräftige Bezeichnungen.

von __Son´s B. (bersison)


Lesenswert?

FAZIT:
Vielen Dank für die vielen konstruktiven Gedanken, Geschmäcker, 
Bemerkungen und Anregungen! Eure Ideen werden mich noch lange begleiten.
So macht Forum Spaß :-)

1| Rechtschreibfehler "SCHWELL vs. SCHWELLE" ist mir gar nicht 
aufgefallen...
2| Da es sich um nur 2(+1) Abfragen handelt, werde ich if/else ohne 
return verwenden.

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


Lesenswert?

Possetitjel schrieb:
>> Persönlich würde ich das als if .. else if ... else if ... else
>> Kaskade schreiben. Vernünfig eingerückt und geklammert ist
>> das sehr gut lesbar - zumindest so lange alles auf einen
>> Bildschirmseite paßt.
>
> Im konkreten Beispiel stimmt das -- weil sich nämlich beide
> Bedingungen auf dieselbe Variable beziehen. Es ist also aus
> inhaltlichen Gründen sowieso sichergestellt, dass sich die
> Fälle gegenseitig ausschließen.
>
> Wenn mehrere Variablen mit voneinander abhängigen Bedingungen
> eine Rolle spielen, ist das aber keineswegs mehr so.

Doch. Bei einer if ... else if ... else if ... Kaskade ist 
sichergestellt daß maximal einer der Blöcke ausgeführt wird. Und wenn 
es ein abschließendes else gibt, dann sogar genau einer. Vollkommen 
egal, wieviele Variablen in die Test-Ausdrücke der if()'s einfließen.

Im Prinzip ist das genauso [1] wie eine switch() Anweisung, wo auch 
immer nur einer der Blöcke ausgeführt wird. Nur daß man mit switch() nur 
sehr begrenzte Tests hat - man kann nur eine Variable auf bestimmte 
konstante Werte testen. if() hingegen kann beliebig komplexe Tests 
ausführen. Nicht daß man Tests kompliziert machen sollte, aber bereits 
die simple Unterteilung eines Bereichs in 3 Teile ist zu kompliziert für 
switch(), aber trivial für if().

[1] genau genommen kann switch() mehr als das; wenn man am Ende eines 
case: Blocks kein break hat, passiert ein fall through in den nächsten 
Block. Das braucht man zwar selten, aber es sorgt regelmäßig für 
Verwirrung.


Possetitjel schrieb:
> Sehr mutig, dass Du so genau weisst, was ich übersichtlich
> finde.

Stell dir einfach mal vor, der Code wäre länger. Es wären 10 Fälle und 
das switch() enthielte so viel Code, daß der ganze Krempel 3 
Bildschirmseiten braucht. Und jetzt suchst du einen Bug, der darin 
besteht, daß case 42: ausgeführt wird, obwohl er eigentlich nicht 
sollte.

Dann ist eine Kaskade von if()'s deutlich besser lesbar, weil 
ausgeführter Code und die zugehörige Bedingung beieinander stehen. Und 
nicht durch 2½ Bildschirmseiten Codewüste voneinander getrennt sind.

von __Son´s B. (bersison)


Lesenswert?

void EnergieRestAmpel(uint16_t RestZeit)
{
 if(RestZeit >= ENERGIE_SCHWELLE_OBEN) LED_ENERGIE_GN_ON;
 else if(RestZeit <= ENERGIE_SCHWELLE_UNTEN) LED_ENERGIE_RT_ON;
 else LED_ENERGIE_GE_ON;
}

In diesem Zusammenhang verstehe ich folgende Fehlermeldung in beiden 
else-Zeilen nicht;
'else' without a previous 'if'
Mit geschweifter Klammer läuft der Compiler sauber durch!
Code ist doch OK? Ist doch nur ein Anweisung hinter if(RestZeit...).

von Dergute W. (derguteweka)


Lesenswert?

Moin,

__Son´s B. schrieb:
> Ist doch nur ein Anweisung hinter if(RestZeit...

Die "Anweisung" scheint mir ein Macro zu sein (das aus mehreren 
Statements bestehen kann und das wohl auch tut).

Gruss
WK

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Dergute W. schrieb:
> Moin,
>
> __Son´s B. schrieb:
>> Ist doch nur ein Anweisung hinter if(RestZeit...
>
> Die "Anweisung" scheint mir ein Macro zu sein (das aus mehreren
> Statements bestehen kann und das wohl auch tut).

ist mit sicherheit so. Phöse ;-)

Wenn man jetzt den "do { x; y; z; } while (0)" Trick empfiehlt, gibts 
sicher wieder Geschrei ;-)

von Jobst Q. (joquis)


Lesenswert?

__Son´s B. schrieb:
> void EnergieRestAmpel(uint16_t RestZeit)
> {
>  if(RestZeit >= ENERGIE_SCHWELLE_OBEN) LED_ENERGIE_GN_ON;
>  else if(RestZeit <= ENERGIE_SCHWELLE_UNTEN) LED_ENERGIE_RT_ON;
>  else LED_ENERGIE_GE_ON;
> }
>
> In diesem Zusammenhang verstehe ich folgende Fehlermeldung in beiden
> else-Zeilen nicht;
> 'else' without a previous 'if'
> Mit geschweifter Klammer läuft der Compiler sauber durch!
> Code ist doch OK? Ist doch nur ein Anweisung hinter if(RestZeit...).

Das kommt eben durch den Makromurks LED_ENERGIE_GN_ON usw.

Mit

void EnergieRestAmpel(uint16_t RestZeit)
 {
 if(RestZeit >= ENERGIE_SCHWELLE_OBEN) SchalteEnergieAmpel(GRUEN);
 else if(RestZeit <= ENERGIE_SCHWELLE_UNTEN) SchalteEnergieAmpel(ROT);
 else SchalteEnergieAmpel(GELB);
 }

wär das kein Problem.

von Jobst Q. (joquis)


Lesenswert?

Der Vollständigkeit halber gäbe es auch noch die kompakte Lösung:

SchalteEnergieAmpel((RestZeit >= ENERGIE_SCHWELLE_OBEN)?GRUEN:
                    (RestZeit <= ENERGIE_SCHWELLE_UNTEN)?ROT:GELB);

von Dergute W. (derguteweka)


Lesenswert?

Moin,

Geschachtelter, bedingter Zuweisungsoperator rulez...!

Gruss
WK

von __Son´s B. (bersison)


Lesenswert?

Dergute W. schrieb:
> Die "Anweisung" scheint mir ein Macro zu sein (das aus mehreren
> Statements bestehen kann und das wohl auch tut).

Stimmt - übersehen oder verdrängt...

von Cyblord -. (cyblord)


Lesenswert?

Jobst Q. schrieb:
> Der Vollständigkeit halber gäbe es auch noch die kompakte Lösung:
>
> SchalteEnergieAmpel((RestZeit >= ENERGIE_SCHWELLE_OBEN)?GRUEN:
>                     (RestZeit <= ENERGIE_SCHWELLE_UNTEN)?ROT:GELB);

Kompakt ist super, wenn man Speicherplatz für den Quellcode sparen muss, 
oder an einem Wettbewerb für den dümmsten Programmierer teilnehmen will. 
Ansonsten ist "kompakter Quellcode" sicher kein Ziel das man antreben 
sollte. Versuchs mal mit lesbar und erweiterbar. Ist aber nur was für 
Erwachsene.

von Einer K. (Gast)


Lesenswert?

Karl schrieb:
> Isses schon so schlimm wie goto,....?
Goto könnte man auch für das erste Beispiel verwenden.
Dann würden sich die eingestreuten return Statements vermeiden lassen.

Michael R. schrieb:
> Wenn man jetzt den "do { x; y; z; } while (0)" Trick empfiehlt, gibts
> sicher wieder Geschrei ;-)
Natürlich!

Ich bevorzuge auch einzeln stehende if Statments, vor if/else Kaskaden.
Switch/case ist mir auch nicht unbedingt recht, da es einen weiteren 
Block {} einführt. Und dazu noch Blöcke ohne {} beinhaltet.

Block Verschachtlungen bis zur Tiefe 2 sind für mich noch leicht zu 
erfassen. Über eine Tiefe von 5 bis 7, ist Ende. Ins Besondere, wenn da 
noch komplizierte Bedingungen im Spiel sind.
Das überlasse ich dann lieber Typen wie Chuck Norris.


Aber wie immer:
1. die konkrete Anwendung
2. die Firmen Richtlinie
3. persönliche Vorlieben
entscheiden.

von Walter K. (Gast)


Lesenswert?

Cyblord -. schrieb:
> Jobst Q. schrieb:
>> Der Vollständigkeit halber gäbe es auch noch die kompakte Lösung:
>>
>> SchalteEnergieAmpel((RestZeit >= ENERGIE_SCHWELLE_OBEN)?GRUEN:
>>                     (RestZeit <= ENERGIE_SCHWELLE_UNTEN)?ROT:GELB);
>
> Kompakt ist super, wenn man Speicherplatz für den Quellcode sparen muss,
> oder an einem Wettbewerb für den dümmsten Programmierer teilnehmen will.

Was hat die sinnvolle Benutzung des ternären Operators ?: in C mit 
'duemmsten Programmierer' zu tun?

von Cyblord -. (cyblord)


Lesenswert?

Walter K. schrieb:

> Was hat die sinnvolle Benutzung des ternären Operators ?: in C mit
> 'duemmsten Programmierer' zu tun?

Wer noch nicht mal den Kern der Kritik versteht sollte nicht versuchen 
auch noch darüber zu diskutieren.

von A. S. (Gast)


Lesenswert?

Cyblord -. schrieb:
> Kompakt ist super, wenn man Speicherplatz für den Quellcode sparen muss,
> oder an einem Wettbewerb für den dümmsten Programmierer teilnehmen will.
> Ansonsten ist "kompakter Quellcode" sicher kein Ziel das man antreben
> sollte. Versuchs mal mit lesbar und erweiterbar. Ist aber nur was für
> Erwachsene.

Das stimmt einfach nicht. Wenn Du in größeren Projekten arbeitest, ist 
Lesbarkeit das primäre Ziel. Und da hängt es von den projekt- und 
teamspezifischen Gewohnheiten ab, ob lange Zeilen gut sind. Wenn die 
IDE einzeilige Suchergebnisse liefert UND 80 Zeichen im Editor sichtbar 
sind, dann musst Du schon lange knobeln um folgenden Code besser zu 
machen:
1
    if(     RestZeit >= ENERGIE_SCHWELLE_OBEN)  {SchalteEnergieAmpel(GRUEN);}
2
    else if(RestZeit <= ENERGIE_SCHWELLE_UNTEN) {SchalteEnergieAmpel(ROT);}
3
    else                                        {SchalteEnergieAmpel(GELB);}
Auch wenn es den Programmierrichtlinien für Newbies widerspricht.

von Egon N. (egon2321)


Lesenswert?

Achim S. schrieb:
> if(     RestZeit >= ENERGIE_SCHWELLE_OBEN)
> {SchalteEnergieAmpel(GRUEN);}
>     else if(RestZeit <= ENERGIE_SCHWELLE_UNTEN)
> {SchalteEnergieAmpel(ROT);}
>     else
> {SchalteEnergieAmpel(GELB);}

Bin ich der einzige der sich über ein cmp zwischen "RestZeit" und 
"ENERGIE_SCHWELLE_*" wundert?

Allein das ist schon der nächste Punkt der Lesbarkeit. Man sollte nicht 
nachschlagen müssen was welche Variablen machen...

von A. S. (Gast)


Lesenswert?

Egon N. schrieb:
> Bin ich der einzige der sich über ein cmp zwischen "RestZeit" und
> "ENERGIE_SCHWELLE_*" wundert?

Naja, die Begriffe sind wie der Code (hoffentlich) sinnfreie Beispiele. 
In meiner Antwort ging es z.B. nur um die Anordnung, daher originale 
Namen. Eine echte Aufgabe (und damit sinnvolle Namen) kennen wir eh 
nicht. Darum macht es beispielsweise (wie oben) auch keinen Sinn, über 
Lösungen zu streiten, die einer ganz anderen if-Struktur bedürfen.

von Bär Luskoni (Gast)


Lesenswert?

Egon N. schrieb:
> Bin ich der einzige der sich über ein cmp zwischen "RestZeit" und
> "ENERGIE_SCHWELLE_*" wundert?

Nein, da sind noch mindestens 4 Andere. Wenn mein Kollege wiederkommt, 
sind es dann sogar 5.

von Cyblord -. (cyblord)


Lesenswert?

Achim S. schrieb:
> Auch wenn es den Programmierrichtlinien für Newbies widerspricht.

Süß. Der Blinde möchte Picasso über Farbe belehren.

von Jobst Q. (joquis)


Lesenswert?

Cyblord -. schrieb:
> Kompakt ist super, wenn man Speicherplatz für den Quellcode sparen muss,
> oder an einem Wettbewerb für den dümmsten Programmierer teilnehmen will.
> Ansonsten ist "kompakter Quellcode" sicher kein Ziel das man antreben
> sollte. Versuchs mal mit lesbar und erweiterbar. Ist aber nur was für
> Erwachsene.

Eine verschachtelte bedingte Zuweisung ist genauso erweiterbar wie eine 
if-else Konstruktion.

a = (b>c)? 1:
   (d<e)? 2:
   (f==g)? 3:
    ...
   (y!=z)? 24:
   0;


Kompakt ist auch super für die Lesbarkeit, für mich jedenfalls. Und ich 
schreibe Quelltext für mich und den Compiler, nicht für den 
Kindergarten.

Eine Zeile ist nun mal schneller zu überblicken als eine halbe Seite. Je 
weniger Ballast und Redundanzen, umso schlechter können sich Fehler 
verstecken.

Geschwätzigen Quellcode zu schreiben lohnt sich nur, wenn man nicht nach 
Funktionalität, sondern nach Zeilen bezahlt wird.

von Curby23523 N. (Gast)


Lesenswert?

1
int a = 10;
2
bool b;
3
4
if(a == 10)
5
  b = true;
6
else
7
  b = false;

oder?
1
int a = 10;
2
bool b;
3
4
if(a == 10){
5
  b = true;
6
}
7
else{
8
  b = false;
9
}

oder?
1
int a = 10;
2
bool b;
3
4
b = (a == 10) ? true : false;

C) finde ich in so einem einfachen Fall am besten.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Egon N. schrieb:
> Bin ich der einzige der sich über ein cmp zwischen "RestZeit" und
> "ENERGIE_SCHWELLE_*" wundert?
Ich finde es auch interessant, eine Energie mit einer Zeit zu 
vergleichen. Besonders, wenn man bedenkt, dass jeder Vergleich eine 
Subtraktion ist, denn dann steht da "Zeit-Energie"...

Die Lesbarkeit und Verständlichkeit eines Codes beginnt also tatsächlich 
weit, weit vor der Diskussion über die Verwendung des ternären 
Operators.

Curby23523 N. schrieb:
> C) finde ich in so einem einfachen Fall am besten.
Ich nehme D)
1
int a = 10;
2
bool b;
3
4
if (a == 10) b = true;
5
else         b = false;

Man könnte aber auch E) nehmen:
1
int a = 10;
2
bool b;
3
4
b = (a == 10);
Denn das Ergebnis des == Operators ist schon bool...

: Bearbeitet durch Moderator
von Tom (Gast)


Lesenswert?

Lothar M. schrieb:
> if (a == 10) b = true;

So wäre es noch konsequenter:
1
if ( (a == 10) == true )
2
     b = true;

von Karl (Gast)


Lesenswert?

Possetitjel schrieb:
> Dabei wäre es
> so einfach, es besser zu machen.
>
> set SwitchToSummertime true
> ...
> if { rtc.wday != 7 }  then { set SwitchToSummertime false }
> if { rtc.day < 25 }   then { set SwitchToSummertime false }
> if { rtc.month != 3 } then { set SwitchToSummertime false }
> ...

Dein Ernst?

Das ist der totale Schrott!

Du willst sicher wieder eine Begründung:

Erstens: Muss der Controller hier alle if-Anfragen prüfen, obwohl er bei 
meiner Abfrage schon in 6 von 7 Fällen bei der Sonntags-Abfrage 
aussteigen kann. Unnötige Programmlaufzeit.

Zweitens: Wird damit erheblich mehr Code erzeugt. Unnötiger 
Speicherbedarf.

Drittens: Braucht es dann noch zwei zusätzliche ifs und zwei zusätzliche 
Variablen. Unnötig kompliziert.

Da wundert es mich nicht, dass die Leute rumjammern, dass sie mit dem 
8-Bitter nicht auskommen und es der 32-Bitter sein muss, weil der 
Speicher nicht reicht. Oder vielleicht doch lieber der Raspi, um die LED 
zu schalten?

Wenn Du schon an so einfachen Programmkonstrukten scheiterst, wie sehen 
dann umfangreichere Programme für Dich aus? Vielleicht doch das falsche 
Hobby?

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Tom schrieb:
> So wäre es noch konsequenter:
>
1
> if ( (a == 10) == true )
2
>      b = true;
3
>
urgs... wer sowas schreibt, frisst auch kleine Kinder ;-)

von Jobst Q. (joquis)


Lesenswert?

Curby23523 N. schrieb:
> int a = 10;
> bool b;
>
> if(a == 10)
>   b = true;
> else
>   b = false;

geht kürzer:

bool b = true;

Wenn man a gerade 10 zugewiesen hat, wozu dann noch mit 10 vergleichen?

von Einer K. (Gast)


Lesenswert?

Jobst Q. schrieb:
> Curby23523 N. schrieb:
>> int a = 10;
>> bool b;
>>
>> if(a == 10)
>>   b = true;
>> else
>>   b = false;
>
> geht kürzer:
>
> bool b = true;

Ich finde es grausam, für sowas ein if einzusetzen.

Eher so:
1
int  a = 10;
2
bool b = 10 == a;

Ich setze bei Vergleichen meist die Konstante nach links.
Also so: if(10 == a)
und eher nicht: if(a == 10)

Um halt das evtl ungewollte if(a = 10) zu vermeiden.
Denn, wenn ich versehentlich if(10 = a) schreibe schimpft der Kompiler 
mit mir, während er das versehentliche if(a = 10) klaglos schluckt.

von Karl (Gast)


Lesenswert?

Jobst Q. schrieb:
> Wenn man a gerade 10 zugewiesen hat, wozu dann noch mit 10 vergleichen?

https://de.wikipedia.org/wiki/Beispiel

Dass sich dazwischen noch Code befinden könnte, der a verändert...

von Karl (Gast)


Lesenswert?

Arduino F. schrieb:
> Um halt das evtl ungewollte if(a = 10) zu vermeiden.
> Denn, wenn ich versehentlich if(10 = a) schreibe schimpft der Kompiler
> mit mir, während er das versehentliche if(a = 10) klaglos schluckt.

Nimm halt eine richtige Programmiersprache, dann passiert sowas nicht.

Oder ist der Algorithmus für Deine Einkaufsliste auch: Wenn Null == 
Bier, dann Nachkaufen?

Hochsprachen sollten eigentlich dazu dienen, den Maschinencode 
menschenkombatibel zu machen. Also Algorithmen so formulieren zu können, 
wie Menschen sie denken. Stattdessen sehe ich hier einen Haufen 
Beispiele, die sich irgendwelcher Hilfskonstrukte bedienen, nur um 
menschenkombatiblen Code zu vermeiden.

Da ist der Sinn einer Hochsprache irgendwie verfehlt.

von Einer K. (Gast)


Lesenswert?

Karl schrieb:
> Nimm halt eine richtige Programmiersprache, dann passiert sowas nicht.
Du meinst besser Forth, als C++?
Vielleicht, sollte ich mal drüber nachdenken....


Bei sowas if(a = 10) kann man schon mal länger nach einem Fehler suchen.
Das Auge flutscht drüber weg, und der Kompiler jammert nicht.

if(10 == a) sieht vielleicht anfangs etwas komisch aus, aber man kann 
sich daran gewöhnen und ist Funktional gleichwertig zu if(a == 10)

Also wohl wieder eher ein Fall von: "Was der Bauer nicht kennt, das 
frisst er nicht"

von Dergute W. (derguteweka)


Lesenswert?

Moin,

Man koennte dem compiler auch sagen, dass er halt mal ein Auge auf 
solche Konstrukte haben soll und doch ein wenig grummeln, wenn er eins 
findet:
1
hello.c: In function main:
2
hello.c:4:18: warning: suggest parentheses around assignment used as truth value [-Wparentheses]
3
 int main() { if (a=5) printf("Hello\n"); }

Gruss
WK

von Jobst Q. (joquis)


Lesenswert?

Karl schrieb:
> Dass sich dazwischen noch Code befinden könnte, der a verändert..

könnte man verdeutlichen indem man ... dazwischen setzt.

von Karl (Gast)


Lesenswert?

Arduino F. schrieb:
> Du meinst besser Forth, als C++?

Pascal, Ada...

> if(10 == a) sieht vielleicht anfangs etwas komisch aus, aber man kann
> sich daran gewöhnen und ist funktional gleichwertig zu if(a == 10)
1
lds r16, a
2
cpi r16, 10
3
breq j10
4
rjmp j20
5
j10:
6
  // if
7
j20:
8
  // end if

Sieht auch komisch aus, kann man sich auch dran gewöhnen und funktional 
ist es gleichwertig zu if a = 10.

Nochmal: Der Sinn einer Hochsprache ist, dass man sich eben nicht 
komische Konstrukte ausdenken muss, sondern Algorithmen menschlich 
verständlich schreiben kann. Sonst kannste auch Assembler schreiben - 
was ich seit Jahren mache.

Wenn eine Sprache solche Konstrukte verlangt, dann isses vielleicht 
keine Hochsprache, sondern ein besserer Makro-Assembler.

von Harlekin (Gast)


Lesenswert?

Karl schrieb:
>> set SwitchToSummertime true
>> ...
>> if { rtc.wday != 7 }  then { set SwitchToSummertime false }
>> if { rtc.day < 25 }   then { set SwitchToSummertime false }
>> if { rtc.month != 3 } then { set SwitchToSummertime false }
>> ...
>
> Das ist der totale Schrott!
>
> Du willst sicher wieder eine Begründung:
>
> Erstens: Muss der Controller hier alle if-Anfragen prüfen, obwohl
> er bei meiner Abfrage schon in 6 von 7 Fällen bei der
> Sonntags-Abfrage aussteigen kann. Unnötige Programmlaufzeit.

Wie oft wird die Funktion aufgerufen? Einmal pro Tag?

>
> Zweitens: Wird damit erheblich mehr Code erzeugt. Unnötiger
> Speicherbedarf.
> Drittens: Braucht es dann noch zwei zusätzliche ifs und zwei
> zusätzliche Variablen. Unnötig kompliziert.

Was nützen die beiden Punkte, wenn sich wegen fehlender 
Übersichtlichkeit ein Fehler eingeschlichen hat?

Bei zeitkritischen Funktionen werde ich in Zukunft weiterhin mal 
verschachteln. Ansonsten werde ich zugunsten der Lesbarkeit, 
Zusatzoperationen in Kauf nehmen.

Ich durfte mich oft genug durch Code kämpfen, wo sich verschachtelte if 
else Konstrukte über mehrere Bildschirmseiten erstreckten. Da hilft 
selbst korrektes Einrücken wenig. Selbst bei obigem Beispiel brauche ich 
wegen der Verschachtelung einfach länger.
Beitrag "Re: Art der if-Auswahl nur Geschmackssache?"

von Jobst Q. (joquis)


Lesenswert?

Karl schrieb:
> Hochsprachen sollten eigentlich dazu dienen, den Maschinencode
> menschenkombatibel zu machen.

Meinst du damit solche Ungetüme:

Karl schrieb:
> Aus aktuellem Anlass:procedure rtc_checksummer();
> begin
>   if rtc.summ and Rsauto <> 0 then begin  // prüfen auf Flag Auto
>     if rtc.wday = 7 then begin  // wenn Sonntag
>       if rtc.day >= $25 then begin  // wenn letzter Sonntag im Monat
>         if rtc.month = $03 then begin  // wenn März, Umschalten auf
> Sommerzeit
>           if (rtc.summ and Rsumm) = 0 then begin  //wenn Flag nicht
> gesetzt
>             if rtc.hh = $02 then begin
>               rtc.hh := $03;
>               rtc.summ := Rsauto or Rsumm;
>               rtc_setsummer();  // Sommerzeit schreiben
>             end;
>           end;
>         end else if rtc.month = $10 then begin  // wenn Oktober,
> Umschalten auf Winterzeit
>           if (rtc.summ and Rsumm) <> 0 then begin  //wenn Flag gesetzt
>             if rtc.hh = $03 then begin
>               rtc.hh := $02;
>               rtc.summ := Rsauto;
>               rtc_setsummer();  // Winterzeit schreiben
>             end;
>           end;
>         end;
>       end;
>     end;
>   end;
> end;

Das ist wahrhaft menschencombatibel.

https://www.urbandictionary.com/define.php?term=combatible

von Tom (Gast)


Lesenswert?

1
bool is_sunday = (rtc.wday == 7);
2
bool is_last_week_in_month = (rtc.day > 24); // only for months with 31 days (e.g. March and October)
3
bool is_last_sunday_in_month = is_sunday && is_last_week_in_month;
4
5
bool is_march = (rtc.month == 3);
6
bool is_october = (rtc.month == 10);
7
8
bool do_switch_to_summertime = is_march && is_last_sunday_in_month;
9
bool do_switch_to_wintertime = is_october && is_last_sunday_in_month;
10
11
if (do_switch_to_summertime)
12
   rtc_set_summer();
13
if (do_switch_to_wintertime)
14
   rtc_set_winter();

von Possetitjel (Gast)


Lesenswert?

Karl schrieb:

>> set SwitchToSummertime true
>> ...
>> if { rtc.wday != 7 }  then { set SwitchToSummertime false }
>> if { rtc.day < 25 }   then { set SwitchToSummertime false }
>> if { rtc.month != 3 } then { set SwitchToSummertime false }
>> ...
>
> Dein Ernst?

Selbstverständlich. Mein voller Ernst.


> Das ist der totale Schrott!

:)


> Du willst sicher wieder eine Begründung:

Ja, wenn sie sich nicht auf den Kernpunkt beschränkt,
dass ich eben dämlich bin, wäre eine Begründung ganz
nett...


> Erstens: Muss der Controller hier alle if-Anfragen
> prüfen, obwohl er bei meiner Abfrage schon in 6
> von 7 Fällen bei der Sonntags-Abfrage aussteigen
> kann. Unnötige Programmlaufzeit.

Das stimmt.

Die Kette von Einzelbedingungen, die ich hingeschrieben
habe, wird immer komplett ausgewertet, das ist in der
Laufzeit etwas ungünstiger als geschachteltes if.
Das lässt sich aber sehr leicht abmildern.

> Zweitens: Wird damit erheblich mehr Code erzeugt.
> Unnötiger Speicherbedarf.

Erheblich... naja.

Die Zahl der Vergleiche und der Sprünge wird nicht
(wesentlich) beeinflusst, nur die Variablenzuweisung
kommt neu dazu. Sind dann halt 21 statt 14 Befehle.
Auch das lässt sich sehr leicht abmildern, ohne meine
Grundidee aufzugeben.

> Drittens: Braucht es dann noch zwei zusätzliche ifs
> und zwei zusätzliche Variablen. Unnötig kompliziert.

Ist im Regelfall nicht relevant.


> Da wundert es mich nicht, dass die Leute rumjammern,
> dass sie mit dem 8-Bitter nicht auskommen und es
> der 32-Bitter sein muss, weil der Speicher nicht
> reicht. Oder vielleicht doch lieber der Raspi, um
> die LED zu schalten?

Lächerlich.

Ich wollte ein Prinzip, eine Grundidee zeigen und habe
NATÜRLICH darauf verzichtet, sie durch Optimierung
zu verwässern.
Selbstverständlich KANN man das in der Praxis etwas
kompakter formulieren, ohne die Grundidee aufzugeben.

Dazu kommt noch: Die Bedingung ist in Deinem Beispiel
nur ein längliches UND aus mehreren Teilbedingungen;
ein strukturell so wenig komplexer Ausdruck lässt sich
auch anders als von mir vorgeschlagen behandeln.


> Wenn Du schon an so einfachen Programmkonstrukten
> scheiterst,

Schätzungsweise definieren wir "scheitern" deutlich
verschieden.

> wie sehen dann umfangreichere Programme für Dich
> aus?

So wie für alle anderen auch: Beschissen.
Zeige mir den Programmierer, der nicht viel lieber
selbst programmiert, als fremde Quelltexte zu lesen.

von Jobst Q. (joquis)


Lesenswert?

if((rtc.wday != 7)||(rtc.day < 25)||(rtc.min != 0)||(rtc.sec != 
0))return;
if((rtc.mon == 3) && (rtc.h == 2)) rtc_set_summer();
if((rtc.mon == 10) && (rtc.h == 3)) rtc_set_winter();

: Bearbeitet durch User
von Possetitjel (Gast)


Lesenswert?

Karl schrieb:

> Arduino F. schrieb:
>> Um halt das evtl ungewollte if(a = 10) zu vermeiden.
>> Denn, wenn ich versehentlich if(10 = a) schreibe
>> schimpft der Kompiler mit mir, während er das
>> versehentliche if(a = 10) klaglos schluckt.
>
> Nimm halt eine richtige Programmiersprache, dann
> passiert sowas nicht.

Karl, das ist kindisches Gezänk, das bringt doch
niemanden weiter.

Die Diskussion hatten wir doch neulich schon: Nenne
bitte eine Programmiersprache, die es hinsichtlich
* Ressourceneffizienz,
* Portabilität,
* Ausdruckskraft
ungefähr mit C aufnehmen kann, aber nicht die zahlreichen
Macken von C hat!

Das Ergebnis war auch schon neulich: Es gibt keine.

Es ist völlig egal, ob man C eine "richtige Programmier-
sprache" oder nur einen "portablen Makroassembler" nennt --
der betrübliche Fakt ist der, dass man keine Alternative
hat.

von Possetitjel (Gast)


Lesenswert?

Jobst Q. schrieb:

> Kompakt ist auch super für die Lesbarkeit, für mich
> jedenfalls.

Da schließe ich mich weitgehend an.


> Und ich schreibe Quelltext für mich und den Compiler,
> nicht für den Kindergarten.

Hier nicht.
Ich denke, es ist ganz gut, dass wir keine Kollegen sind
und auch sonst nicht zusammen an einem Projekt arbeiten.


> Je weniger Ballast und Redundanzen, umso schlechter
> können sich Fehler verstecken.

Aufgrund dieses fundamentalen Zusammenhanges sind Leute
wie C. E. Shannon, R. W. Hamming, die Herrn Reed und
Solomon usw. bis heute unbeachtete Spinner geblieben,
und Paritätsbits haben ihren vedienten Platz im
Kuriositätenkabinett der Technik gefunden... :)

von Harlekin (Gast)


Lesenswert?

Jobst Q. schrieb:
> if((rtc.wday != 7)||(rtc.day < 25)||(rtc.min != 0)||(rtc.sec !=
> 0))return;
> if((rtc.mon == 3) && (rtc.h == 2)) rtc_set_summer();
> if((rtc.mon == 10) && (rtc.h == 3)) rtc_set_winter();

Elegant, schnell, kompakt. Bravo


Für Karl sollten wir die Reihenfolge der ersten Zeile optimieren ;-)

if((rtc.min != 0)||(rtc.sec !=0)||(rtc.wday != 7)||(rtc.day < 
25))return;

Begründung:
Wenn ich mich recht entsinne werden in C die Bedingungen von links 
geprüft und sobald eine Oderbedingung true ist, wird zurückgesprungen.

min ist 1/60
sec ist 1/60
wday ist 1/7
day ist ca 1/6

von Possetitjel (Gast)


Lesenswert?

Axel S. schrieb:

>> Im konkreten Beispiel stimmt das -- weil sich nämlich beide
>> Bedingungen auf dieselbe Variable beziehen. Es ist also aus
>> inhaltlichen Gründen sowieso sichergestellt, dass sich die
>> Fälle gegenseitig ausschließen.
>>
>> Wenn mehrere Variablen mit voneinander abhängigen Bedingungen
>> eine Rolle spielen, ist das aber keineswegs mehr so.
>
> Doch. Bei einer if ... else if ... else if ... Kaskade ist
> sichergestellt daß maximal einer der Blöcke ausgeführt wird.

Missverständnis. Meine Schuld. (s.u.)


> Im Prinzip ist das genauso [1] wie eine switch() Anweisung,
> wo auch immer nur einer der Blöcke ausgeführt wird. Nur daß
> man mit switch() nur sehr begrenzte Tests hat - man kann nur
> eine Variable auf bestimmte konstante Werte testen. if()
> hingegen kann beliebig komplexe Tests ausführen. Nicht daß
> man Tests kompliziert machen sollte, aber bereits die simple
> Unterteilung eines Bereichs in 3 Teile ist zu kompliziert
> für switch(), aber trivial für if().

Die Verwandschaft zwischen if/elseif und switch/break ist
erstmal klar; bezüglich des letztlich ausgeführten Anweisungs-
blockes sind beides quasi 1-aus-N-Decoder.

Ich meinte aber etwas anderes:

Im Beispiel gibt es eine Variable (mit einem Wertebereich,
über dem eine Ordnungsrelation definiert ist), und es gibt
zwei Schwellwerte.
Da es zwei Schwellwerte gibt, existieren drei unterscheidbare
Teilintervalle, in denen der Variablenwert liegen kann.
Um die Steuervariable gegen die beiden Schwellwerte zu
testen, sind zwei Bedingungsausdrücke notwendig, die
logischerweise zwei Wahrheitswerte liefern.

Mit zwei Wahrheitswerten sind aber VIER Kombinationen
ausdrückbar!

Der scheinbare Widerspruch rührt natürlich daher, dass die
beiden Bedingungen nicht unabhängig sind, sondern sich auf
dieselbe Variable beziehen: Sie kann nur ENTWEDER im einen
ODER im anderen ODER im dritten Intervall liegen.

Die Kombination, dass sie KLEINER als die untere, aber
gleichzeitig GRÖSSER als die obere Schwelle ist, kann aus
inhaltlichen Gründen nicht auftreten, der Fall ist per se
ausgeschlossen.

Deswegen -- weil es zwar mehrere Bedingungen, aber nur ein
und dieselbe Steuervariable gibt -- ist die if/elsif-
Konstruktion im vorliegenden Fall machbar und sinnvoll.
Hochtrabend mengentheoretisch formuliert: Die möglichen
Fälle bilden inhaltlich eine Partition, und die if/elseif-
Konstrukion bildet diese Partition korrekt ab.

So. Jetzt mein Punkt: Es GIBT Fälle, in denen mehrere
Bedingungsausdrücke vorliegen, die sich aber NICHT ALLE
wie im Beispiel auf dieselbe Steuervariable beziehen,
sondern in denen mehrere verschiedene Steuervariablen in
unterschiedlicher Kombination vorkommen.
Überdies kann der Fall eintreten, dass sich die Bedingungen
für die unterschiedlichen Steuervariablen gegenseitig
beeinflussen ("Der Busfahrer kann männlich oder weiblich
sein; wenn es ein Mann ist, kann er einen Bart tragen oder
nicht. Wenn es eine Frau ist, interessiert der Bart nicht.")

In diesen -- und PRIMÄR in diesen -- Fällen ist meiner
unmaßgeblichen Meinung nach die geteilte Konstruktion mit
dem if-Abschnitt und der abschließenden switch-Anweisung
sinnvoll.
(Im Prinzip ist das nämlich nichts weiter wie eine Art
Normalform: Erstmal wird alles auf garantiert disjunkte
Basisfälle zurückgeführt; dann werden bei Bedarf im switch
die Basisfälle wieder gruppenweise zusammengefasst.)


> Possetitjel schrieb:
>> Sehr mutig, dass Du so genau weisst, was ich übersichtlich
>> finde.
>
> [...]
>
> Dann ist eine Kaskade von if()'s deutlich besser lesbar,
> weil ausgeführter Code und die zugehörige Bedingung
> beieinander stehen. Und nicht durch 2½ Bildschirmseiten
> Codewüste voneinander getrennt sind.

Ich hoffe, mein Punkt ist klarer geworden: Wenn von vornherein
INHALTLICH disjunkte Fallklassen vorliegen, ist if/elseif
optimal, weil es genau das abbildet. (Das ist im Beispiel der
Fall.)

Das bedeutet aber im Umkehrschluss nicht, dass die diskutierte
if/switch-Konstruktion generell sinnlos ist: Da das eine Art
Normalformkonstruktion ist, ist sie gerade in komplizierten
Fällen nützlich, wenn man KEINE offensichtlich disjunkten
Fallklassen hat.

(Sie ist auch nützlich, wenn man SEHR VIELE diskjunkte Fälle
hat, aber das ist ein anderes Thema.)

von Dirk (Gast)


Lesenswert?

Karl schrieb:
> Der GROSSE Vorteil von if ... else ist, dass man damit sehr effizient
> Abfragen zusammenfassen kann, die der Compiler dann sehr effizient in
> Maschinencode umsetzt. Wer diesen Vorteil gerade auf µCs nicht nutzt,
> weil er davon Kopfschmerzen bekommt, sollte vielleicht besser _Bäcker_
> werden.
Der Bäcker der vermutlich seit dem letzten Wochenende zu späte Brötchen 
backt, weil er versucht hat seine komplizierte Sommerzeitumstellung in 
einem µC zu zu testen oder die Chance verpasst hat seine süße 
Treppenkunst der 'Programmierung' auch in einem Feldversuch zu testen 
hat sicherlich nichts von konventioneller Effizienz verstanden:
1
01:if rtc.summ and Rsauto <> 0 then begin  // prüfen auf Flag Auto
2
02:  if rtc.wday = 7 then begin  // wenn Sonntag
3
03:    if rtc.day >= $25 then begin  // wenn letzter Sonntag im Monat
4
04:      if rtc.month = $03 then begin  // wenn März, Umschalten auf Sommerzeit
5
05:        if (rtc.summ and Rsumm) = 0 then begin  //wenn Flag nicht gesetzt
6
06:          if rtc.hh = $02 then begin
7
07:            rtc.hh := $03;
8
08:            rtc.summ := Rsauto or Rsumm;
9
09:            rtc_setsummer();  // Sommerzeit schreiben
10
10:          end;
11
11:        end;
12
12:      end else if rtc.month = $10 then begin  // wenn Oktober, Umschalten auf Winterzeit
13
13:        if (rtc.summ and Rsumm) <> 0 then begin  //wenn Flag gesetzt
14
14:          if rtc.hh = $03 then begin
15
15:            rtc.hh := $02;
16
16:            rtc.summ := Rsauto;
17
17:            rtc_setsummer();  // Winterzeit schreiben
18
18:          end;
19
19:        end;
20
20:      end;
21
21:    end;
22
22:  end;
23
22:end;
Zeile 09:  rtc_setsummer();  // Sommerzeit schreiben
Zeile 17:  rtc_setsummer();  // Winterzeit schreiben
==> der Bäcker versucht dem Compiler mittels Kommentar den Code zu 
erklären, während traditionelle Programmierer Kommentare dazu verwenden 
den verwendeten (!) Code für Menschen lesbarer zu gestalten.
Natürlich kann ein Bäcker nicht wissen, dass praktisch (für Menschen) 
selbsterklärender Code:
"wenn (rtc.tag.akt) größer (tag.36) dann evt. Umstellung" (Pascal "if 
rtc.day >= $25 then") durch die Kommentierung mit einem Wochentag der 
nicht in der Zeile vorkommt ("... Sonntag im Monat") nicht besser 
funktioniert.

Die Frage:
>Was bitte ist daran unlesbar? Und, schon Kopfschmerzen?
die ein Bäcker bei so einem Kunstwerk an die Öffentlichkeit stellen 
muss, da er naturgemäß keinen eigenen Zugriff auf den Quelltext hat, 
verbietet diesem natürlich den Code leserlicher zu gestalten.

(etwas kreativ, u.U. nicht mit jedem Pascal-Compiler religiös vereinbar, 
aber technisch identisch mit dem original Backwerk):
1
{$define th:=then } {$define th_:=then begin}
2
{$define el:=else } {$define el_:=else begin}
3
{$define _el:=end} {$define _th:=end}
4
5
01: if rtc.summ and Rsauto <> 0 // prüfen auf Flag Auto
6
02: th if rtc.wday = 7          // wenn Sonntag
7
03:    th if rtc.day >= $25     // wenn letzter Sonntag im Monat
8
04:       th if rtc.month = $03 // wenn März, Umschalten auf Sommerzeit
9
05:       th_ if (rtc.summ and Rsumm) = 0 // wenn Flag nicht gesetzt
10
06:           th if rtc.hh = $02
11
07:              th_ rtc.hh := $03;
12
08:                  rtc.summ := Rsauto or Rsumm;
13
09:                  rtc_setsummer(); // Sommerzeit schreiben
14
10:                 _th;
15
11:          _th
16
12:       el if rtc.month = $10 // wenn Oktober, Umschalten auf Winterzeit
17
13:          th if (rtc.summ and Rsumm) <> 0//wenn Flag gesetzt
18
14:             th if rtc.hh = $03
19
15:                th_ rtc.hh := $02;
20
16:                    rtc.summ := Rsauto;
21
17:                    rtc_setsummer(); // Winterzeit schreiben
22
18:                   _th;
23
24
// verkürzte Version mit gleichrangigen Bedingungen, um sowohl Kommentar
25
// als auch hexadezimale Brötchenfehler lesen zu können:
26
{$define a_:= and } {$define o_:= or }
27
28
01: if (rtc.summ and Rsauto <> 0 ) // wenn Flag=Auto
29
02: a_ (rtc.wday = 7)              // und Sonntag
30
03: a_ (rtc.day >= $25)            // und Tag > 37 (letzte Woche)
31
04:    th if (rtc.month = $03) // dann wenn März,
32
05:       th_ if ((rtc.summ and Rsumm) = 0) //dann wenn Flag nicht gesetzt
33
06:           a_ (rtc.hh = $02 ) // und Stunde=2
34
07:           th_ rtc.hh := $03;
35
08:               rtc.summ := Rsauto or Rsumm; //'Umschalten' auf Szt
36
09:               rtc_setsummer(); // Sommerzeit schreiben (ident mit WZ_write?)
37
10:              _th; // flag !=rsumm & stunde = 2
38
11:          _th // month $03
39
12:       el if (rtc.month = $10) //sonst wenn Okt(Dez:10;Hex:$0a),
40
13:          a_ ((rtc.summ and Rsumm) <> 0) //und Flag gesetzt
41
14:          a_ (rtc.hh = $03)              //und Stunde = 3
42
15:          th_ rtc.hh := $02;
43
16:              rtc.summ := Rsauto; // 'Umschalten' auf Wz
44
17:              rtc_setsummer(); // Winterzeit schreiben
45
18:             _th; // month $10 & flag == rsumm & stunde = 3
Vorteil: zumindest lesbarer als gleichrangige UND-Bedingungen als 
abhängige "wenn ... dann"-Bedingung zu texten (i.A. werten Compiler ohne 
spezielle Option es exakt gleich aus)
(Nebenvorteil: da viele Bäcker Angst haben einen µC mit ein paar Takten 
zu viel zu überlasten, könnten diese den häufigsten Ausschlussgrund 
(day<>7) an den Anfang der Abfrage stellen und so den µC etwas schonen)

Zusammenfassung: lesbarer Code kann zwar ein paar Takte 'kosten' aber 
Karl der Bäcker bräuchte mehrere Zeitumstellungen bis er die Fehler in 
seinem Code experimentell überprüfen könnte.
Umfangreiche Programmkonstrukte, wie eine Zeitumstellung vor Ende des 36 
Tages im Monat, dürften Bäcker die Karl aus seinen Kopfschmerzen kennt 
sicherlich überfordern. Umfangreiche Programme dürften für Bäcker wie 
Karl wohl deutlich mehr "begin" "end" enthalten.


Arduino F. schrieb:
> Um halt das evtl ungewollte if(a = 10) zu vermeiden.
technisch sehr sinnvoll, aber etwas inkompatibel mit dt./eng. Schreib 
und wohl auch Denkmustern:
sprachlich: wenn der µC eine Spannung von 230V hat, dann liegt ein 
Fehler vor.
umgekehrt: wenn eine Spannung von 230V der µC hat, dann ....auch Fehler 
aber sprachlich wohl Jedi.

Beim programmieren:
if 230 = uC ... ok (aber Denkmurks)
if uC = 230 ... Bude fackelt ab ... (noch schlechter)
Es könnte sein, dass durch die damalige Sparmaßnahme beim 
Zuweisungsoperator in C der Keim der Zerstörung mit angelegt wurde, 
ähnlich dem Whitespace-Operator in C  (u.U. etwas übertrieben für eine 
so gefährliche Programmiersprache)

NB.: für den OP könnte es wohl Hoffnung mit C++20 geben (soll 2020 auf 
den Markt kommen) da dann der Spaceship-Operator endlich Einzug in die 
gefährliche Welt von C++ findet.

von Harlekin (Gast)


Lesenswert?

Dirk schrieb:
> Karl (schrieb) Der GROSSE Vorteil..

nettes Wortspiel ;-)

von Possetitjel (Gast)


Lesenswert?

Rolf M. schrieb:

> Possetitjel schrieb:
>> Das Problem bei if/elseif ist, dass es zwar syntaktisch
>> eine Anweisung bleibt, man aber mehrere unabhängige
>> Bedingungen abprüfen kann, und das ist Teufelswerk.
>
> Warum?

Bei if/elseif schließen sich die (die Fälle behandelnden)
Anweisungsblöcke per Konstruktion aus. Bei unabhängigen
Bedingungen "überlappen" sich die Teilmengen jedoch inhalt-
lich.
Das Konstrukt der Hochsprache spiegelt somit formal einen
ganz anderen Fall vor, als letztlich inhaltlich vorliegt.

Beispiel: Gegeben seien die Bedingungen c1, c2 und c3,
die jeweils wahr oder falsch sein können. Es ergeben
sich formal acht Fallklassen.

Wenn alle acht formal denkbaren Fallklassen tatsächlich
inhaltlich verschieden sind, ist die Sache noch relativ
einfach: Man kommt mit if/then auf drei Ebenen durch die
Sache durch, muss aber Bedingungen mehrfach schreiben
(was nicht dramatisch, aber auch nicht schön ist -- "DRY").
Alternativ kann man auch if/elseif auf einer Ebene verwenden,
muss dann aber alle drei Bedingungen viermal negiert und
viermal nichtnegiert schreiben. Geht auch.

Die if/switch-Konstruktion sieht dort so aus:
1
 
2
set fallnr 0 
3
if { $c1 } then { set  fallnr 4 }
4
if { $c2 } then { incr fallnr 2 }
5
if { $c3 } then { incr fallnr 1 }
6
7
switch $fallnr { 
8
... 
9
}

Noch schlimmer wird es aber, wenn die formal denkbaren
Fälle inhaltlich NICHT alle unterschiedlich sind.

Bei der if/then/else- bzw. if/elseif-Variante schreibt
man entweder den die Fälle behandelnden Code mehrfach,
oder man lagern ihn in Funktionen aus, oder man versucht,
die Bedingungen umzuformulieren.
Letzteres ist gefährlich; es ist eine Konzentrationsübung,
dabei Vollständigkeit und Widerspruchsfreiheit abzusichern.

Verwendet man die if/switch-Konstruktion, muss man von der
Struktur her überhaupt nichts anpassen, sondern ändert
lediglich die Zuordnung der Fallnummern in der switch-
Anweisung.

Dieses Beispiel mit drei binären Bedingungen ist noch
ziemlich gutartig; wenn es drei Bedingungen mit drei,
drei und zwei Belegungen gibt, sind das formal schon
18 Fallklassen.

> "Fallnummer"? Also wenn man das so macht, dann sollte
> man (ähnlich wie Tom es gezeigt hat) den Fällen auch
> Namen geben. Irgendwelche Magic-Numbers machen es alles
> andere als übersichtlicher. Dann muss ich beim Lesen des
> switch/case erst wieder oben nachschauen, welches
> Ergebnis der if-Abfrage denn nun zu Fall Nummer 3 gehört,
> was den Lesefluss meiner Meinung nach stark stört.

Das stimmt -- aber hast Du einen besseren Vorschlag?


> Hmm, dann funktionieren unsere Gehirne verschieden ;-)

Das ist gut möglich :)


> Wenn ich das im Kopf durchgehen würde, würde ich sagen:
> [...]
>
> Da hab ich ja im zweiten Satz schon vergessen, welcher
> Fall im ersten Satz welcher Bedingung zugeordnet war.

Ja... das Beispiel ist ungünstig, weil für die LEDs die
if/elseif-Kette aus inhaltlichen Gründe die richtige
Konstruktion ist.
Das will ich auch gar nicht bestreiten; es ging mir nur
darum, dass der if/switch-Vorschlag nicht SO bescheuert
ist, wie er zunächst aussieht -- auch wenn er für das
konkrete Beispiel nicht gut passt.


>> Im konkreten Beispiel stimmt das -- weil sich nämlich beide
>> Bedingungen auf dieselbe Variable beziehen. Es ist also aus
>> inhaltlichen Gründen sowieso sichergestellt, dass sich die
>> Fälle gegenseitig ausschließen.
>>
>> Wenn mehrere Variablen mit voneinander abhängigen Bedingungen
>> eine Rolle spielen, ist das aber keineswegs mehr so.
>
> Ja, ab einer gewissen Komplexität ist die Trennung durchaus
> sinnvoll. Aber zwingend ist für mich dann auch wieder, dass
> keine magischen Fallnummern verwendet werden, sondern
> aussagekräftige Bezeichnungen.

Ich bin für alle Ideen offen.

Die erstrebte Vereinfachung kommt ja dadurch zustande, dass
ich mit den Fallnummern rechne. Mir fällt aber nix ein, wie
man das mit Klartext-Bezeichnungen kombinieren könnte.
Bitfrickeln ist auch nicht schön; es kann ja drei oder fünf
Unterklassen je Steuervariable geben, dann würden Lücken in
den Fallnummern entstehen, was die Sache wieder verkompliziert.

Im Prinzip ist Dein Einwand aber gut; man braucht eine
idiotensichere (=für mich taugliche) Methode, die Fallnummern
auf Klartextbezeichnungen abzubilden.

von Karl (Gast)


Lesenswert?

Jobst Q. schrieb:
> if((rtc.wday != 7)||(rtc.day < 25)||(rtc.min != 0)||(rtc.sec !=
> 0))return;
> if((rtc.mon == 3) && (rtc.h == 2)) rtc_set_summer();
> if((rtc.mon == 10) && (rtc.h == 3)) rtc_set_winter();

Macht nur leider nicht das Gleiche wie mein Code.

Tom schrieb:
> bool is_sunday = (rtc.wday == 7);
> bool is_last_week_in_month = (rtc.day > 24); // only for months with 31
> days (e.g. March and October)
> bool is_last_sunday_in_month = is_sunday && is_last_week_in_month;
>
> bool is_march = (rtc.month == 3);
> bool is_october = (rtc.month == 10);
>
> bool do_switch_to_summertime = is_march && is_last_sunday_in_month;
> bool do_switch_to_wintertime = is_october && is_last_sunday_in_month;
>
> if (do_switch_to_summertime)
>    rtc_set_summer();
> if (do_switch_to_wintertime)
>    rtc_set_winter();

Das hätte ich gern mal als ASM-Listing gesehen. Glaube kaum, dass 7 
unnötige Zwischenvariablen effektiver umgesetzt werden als if-Abfragen.

Possetitjel schrieb:
> Karl, das ist kindisches Gezänk, das bringt doch
> niemanden weiter.

Ich empfinde es eher als kindisch, sich absonderliche Konstrukte 
auszudenken, weil man mit der Struktur einer if .. else if.. else 
Abfolge überfordert ist.

Ist das wirklich so schwierig? Bei den meisten der obigen "alternativen" 
Konstrukte sage ich mir: Ja, kann man machen, aber: WARUM?

von Himmel hilf (Gast)


Lesenswert?

Karl schrieb:

> Ja, kann man machen, aber: WARUM?

Warum? Verschleiern, tarnen, täuschen, Finten setzen. Wozu? Um es 
Anderen maximal zu erschweren oder am Besten unmöglich machen, den unter 
Elend und Entbehrungen erzeugten Code zu entziffern.

von Dirk (Gast)


Lesenswert?

Karl schrieb:
> Macht nur leider nicht das Gleiche wie mein Code.
du hast als Betroffener sicherlich keinen direkten Zugang zu Deinem Code 
und konntest den in einem µC testen, ansonsten wäre Dir die verpasste 
Zeitumstellung bei Deinem umfangreichen Projekt aufgefallen.,


> Possetitjel schrieb:
>> Karl, das ist kindisches Gezänk, das bringt doch
>> niemanden weiter.
>
> Ich empfinde es eher als kindisch, sich absonderliche Konstrukte
> auszudenken, weil man mit der Struktur einer if .. else if.. else
> Abfolge überfordert ist.
Solche kindischen Empfindungen passieren wenn Karl überfordert ist 
seinen fehlerhaften Problemcode aus seinem Zeitumstellungsprojekt 
eigenständig zu überprüfen.

> Ist das wirklich so schwierig? Bei den meisten der obigen "alternativen"
> Konstrukte sage ich mir: Ja, kann man machen, aber: WARUM?
Solche Selbstgespräche sind sicherlich nicht hilfreich wenn es darum 
geht WARUM die Zeitumstellung bei Karl auch dieses Jahr nicht geklappt 
hat.

von Egon N. (egon2321)


Lesenswert?

Tom schrieb:
> Lothar M. schrieb:
>> if (a == 10) b = true;
>
> So wäre es noch konsequenter:if ( (a == 10) == true )
>      b = true;

Genfer Konvention sagt dir was? Solche Codekonstrukte wurden im gleichen 
Atemzug mit chemischen Kampfstoffen verboten.

: Bearbeitet durch User
von Karl (Gast)


Lesenswert?

Dirk schrieb:
> du hast als Betroffener sicherlich keinen direkten Zugang zu Deinem Code
> und konntest den in einem µC testen, ansonsten wäre Dir die verpasste
> Zeitumstellung bei Deinem umfangreichen Projekt aufgefallen.,

Der Code läuft seit Jahren in meiner Heizung und hat bisher noch jede 
Zeitumstellung mitgemacht.

Wenn Du eine Packed-BCD-Zahl nicht als solche erkennst, solltest Du 
Bäcker werden.

von Jobst Q. (joquis)


Lesenswert?

Possetitjel schrieb:
>> Und ich schreibe Quelltext für mich und den Compiler,
>> nicht für den Kindergarten.
>
> Hier nicht.

Hier halte ich mich an die Vorgaben der Fragesteller und der anderen 
Teilnehmer, soweit es geht.

Das ist aber nur ein winziger Bruchteil der von mir geschriebenen 
Quelltexte. Ich bin eben selbstständig und der einzige, der mit meinen 
Quellen arbeiten muss. Also haben sich durch Erfahrungen von Jahrzehnten 
Regeln herauskristallisiert, die für mich und meine Arbeit optimal 
sind.Ich habe das Privileg, nicht berücksichtigen zu müssen, was andere 
für optimal halten.

> Ich denke, es ist ganz gut, dass wir keine Kollegen sind
> und auch sonst nicht zusammen an einem Projekt arbeiten.
>
Warum? Dass ich nach eigenen Regeln programmiere, heißt ja nicht, dass 
ich mich nicht nach anderen Regeln richten kann. Mit manchen könnte ich 
mich sicher auf gemeinsame Regeln einigen, mit allen natürlich nicht.

>> Je weniger Ballast und Redundanzen, umso schlechter
>> können sich Fehler verstecken.
>
> Aufgrund dieses fundamentalen Zusammenhanges sind Leute
> wie C. E. Shannon, R. W. Hamming, die Herrn Reed und
> Solomon usw. bis heute unbeachtete Spinner geblieben,
> und Paritätsbits haben ihren vedienten Platz im
> Kuriositätenkabinett der Technik gefunden... :)

Das soll wohl Ironie sein, aber in der Nachrichtentechnik hat Redundanz 
eine ganz andere Bedeutung als in der Programmierung und die Fehler sind 
ganz anderer Art.

von Jobst Q. (joquis)


Lesenswert?

Karl schrieb:
> Jobst Q. schrieb:
>> if((rtc.wday != 7)||(rtc.day < 25)||(rtc.min != 0)||(rtc.sec !=
>> 0))return;
>> if((rtc.mon == 3) && (rtc.h == 2)) rtc_set_summer();
>> if((rtc.mon == 10) && (rtc.h == 3)) rtc_set_winter();
>
> Macht nur leider nicht das Gleiche wie mein Code.

Was dein Code macht, weiß leider keiner außer dir. Die Umgebung hast du 
uns nicht mitgeteilt.

von Dirk (Gast)


Lesenswert?

Karl schrieb:
> Dirk schrieb:
>> du hast als Betroffener sicherlich keinen direkten Zugang zu Deinem Code
>> und konntest den in einem µC testen, ansonsten wäre Dir die verpasste
>> Zeitumstellung bei Deinem umfangreichen Projekt aufgefallen.,
>
> Der Code läuft seit Jahren in meiner Heizung und hat bisher noch jede
> Zeitumstellung mitgemacht.
> Wenn Du eine Packed-BCD-Zahl nicht als solche erkennst, solltest Du
> Bäcker werden.
das heisst du hast wirklich keine Möglichkeit eigenständig Deinen 
veröffentlichten Code mit Hilfe einer Texterkennung nach "BCD" zu 
untersuchen und bleibst als Abhängiger darauf beschränkt anderen 
Menschen etwas 'empfehlen' zu müssen.

Du hast den Geschmack der Packed-BCD-Zahl sicherlich beim backen erlebt 
und  deswegen sind bei dir
>Zeile 09:  rtc_setsummer();  // Sommerzeit schreiben
>Zeile 17:  rtc_setsummer();  // Winterzeit schreiben
weiterhin identisch.

von Karl (Gast)


Lesenswert?

Dirk schrieb:
> deswegen sind bei dir
>>Zeile 09:  rtc_setsummer();  // Sommerzeit schreiben
>>Zeile 17:  rtc_setsummer();  // Winterzeit schreiben
> weiterhin identisch.

Ja. Weil da nur die aktuelle Stunde aus rtc.hh und die Flags (Auto und 
Sommerzeit) aus rtc.summ per TWI in die RTC geschrieben werden. Ist die 
gleiche Funktion, warum sollte ich die zweimal unter verschiedenen Namen 
haben?

Das Entscheidende ist die Zeile darüber, in der das Flag gesetzt wird, 
welches dann stromausfallsicher in der RTC abgelegt wird.

von Dirk (Gast)


Lesenswert?

Karl schrieb:
> Dirk schrieb:
>> deswegen sind bei dir
>>>Zeile 09:  rtc_setsummer();  // Sommerzeit schreiben
>>>Zeile 17:  rtc_setsummer();  // Winterzeit schreiben
>> weiterhin identisch.
>
> Ja. Weil da nur die aktuelle Stunde aus rtc.hh und die Flags (Auto und
> Sommerzeit) aus rtc.summ per TWI in die RTC geschrieben werden. Ist die
> gleiche Funktion, warum sollte ich die zweimal unter verschiedenen Namen
> haben?
das kannst du nicht wissen wenn du aus Unerfahrenheit generell fremde 
Menschen wie mich für solche programmtechnischen Kleinigkeiten fragen 
musst, aber auch Kommentare können falsch sein d.h. wenn der Kommentar 
behauptet "Winterzeit schreiben" obwohl im Programm der jeweils aktuelle 
Wert geschrieben wird, dann ist das ein eindeutiger Kommentarfehler.
>Zeile 09:  rtc_setsummer();  // Wert schreiben
>Zeile 17:  rtc_setsummer();  // Wert schreiben
wäre zumindest nicht falsch und falls du einmal leslichen Code 
schreiben möchtest anstatt vieler begin/end, dann wäre
>Zeile 09:  rtc_setsummer();  // stromausfallsicher Flag setzen
>Zeile 17:  rtc_setsummer();  // stromausfallsicher Flag setzen
eine Option.

> Das Entscheidende ist die Zeile darüber, in der das Flag gesetzt wird,
> welches dann stromausfallsicher in der RTC abgelegt wird.
Dein Programm wäre ohne Kommentare sicherlich leichter zu lesen, aber da 
Du auf fremde Menschen angewiesen bist um diese nach einfachsten Sachen 
zu fragen kannst du auch nicht lesen, dass auch das nicht im 
Code/Kommentar steht.

von Karl (Gast)


Lesenswert?

Häh?

von Dirk (Gast)


Lesenswert?

Karl schrieb:
>>> Dirk schrieb:
>> das kannst du nicht wissen wenn du aus Unerfahrenheit generell fremde
>> Menschen wie mich für solche programmtechnischen Kleinigkeiten fragen
>> musst, aber auch Kommentare können falsch sein d.h. wenn der Kommentar
>> behauptet "Winterzeit schreiben" obwohl im Programm der jeweils aktuelle
>> Wert geschrieben wird, dann ist das ein eindeutiger Kommentarfehler.
> Häh?
Menschen die nur hilflos fremde Menschen "Hä?" fragen können und keinen 
Zugang zum Quelltext haben auf den sie reagieren, bleibt die 
Unerfahrenheit mit der sie auf den Bäcker gekommen sind.

von TI:07 (Gast)


Lesenswert?

geht das auch ohne switch case oder if ?

Also Funktionsaufruf über einen struct ?

von Rolf M. (rmagnus)


Lesenswert?

TI:07 schrieb:
> Also Funktionsaufruf über einen struct ?

Was magst du damit meinen?

von Einer K. (Gast)


Lesenswert?

>Zeile 09:  rtc_setsummer();  // Sommerzeit schreiben
>Zeile 17:  rtc_setsummer();  // Winterzeit schreiben

Ich finde das absolut gruselig!

Eine Funktion/Methode, mit "set" im Name ist für mich ein Setter, und 
setSummer() setzt halt auf Sommerzeit. NIE würde ich auf die Idee 
kommen, dass die gleiche Funktion auch die Winterzeit einstellt.

Eine solche Funktion würde ich sofort umbenennen!
z.B. in rtc_togglesummer()
Das kann man ja sonst nach 6 Monaten selber nicht mehr verstehen.
(es sei denn, es ist das einzige Programm, was man in der Zeit anfasst)

von A. S. (Gast)


Lesenswert?

Cyblord -. schrieb:
> Süß. Der Blinde möchte Picasso über Farbe belehren.

Mh, unter welchem Namen zeigst Du denn in diesem Forum mal was von 
Deiner Kunst?

Und wenn Du selber mal ein großes Projekt betreust (nicht nur als Summe 
vieler kleiner), dann wirst Du erfahren, dass einige Regeln für normale 
Projekte nicht skalieren, an die Du (aus Deiner Erfahrung zurecht) noch 
glaubst.

von Tom (Gast)


Lesenswert?

TI:07 schrieb:
> geht das auch ohne switch case oder if ?
> Also Funktionsaufruf über einen struct ?
Ohne if und mit struct :
1
void set_summer(void) {printf("sommer\n");}
2
void set_winter(void) {printf("winter\n");}
3
void do_nothing(void) {;}
4
5
void doit(int mon, int day, int wday){
6
    static struct {void (*a)(void); void (*b)(void);void (*c)(void); } F = {set_summer, do_nothing, set_winter};
7
    static void (**p)(void) = (void(**)(void)) &F + 1;
8
    p[(mon==10 && wday==7 && day>24) - (mon==3 && wday==7 && day>24)]();
9
}
Sehr kompakt mit wenigen Zeilen und deshalb überaus lesbar.

von Possetitjel (Gast)


Lesenswert?

Jobst Q. schrieb:

>> Ich denke, es ist ganz gut, dass wir keine Kollegen sind
>> und auch sonst nicht zusammen an einem Projekt arbeiten.
>>
> Warum? Dass ich nach eigenen Regeln programmiere, heißt
> ja nicht, dass ich mich nicht nach anderen Regeln richten
> kann.

Naja, vor dem Hintergrund, dass Du ein Ein-Mann-Team bist,
wird Deine Bemerkung verständlicher.

Die Formulierung mit dem Kindergarten kam halt so 'rüber,
dass Dir völlig egal ist, ob andere Deine genialen Quell-
texte lesen können (wobei es durchaus sein kann, dass die
wirklich genial sind. Dein Codebeispiel für die Sommerzeit-
umstellung ist auch an Lesbarkeit schwerlich zu übertreffen.)


>>> Je weniger Ballast und Redundanzen, umso schlechter
>>> können sich Fehler verstecken.
>>
>> Aufgrund dieses fundamentalen Zusammenhanges sind Leute
>> wie C. E. Shannon, R. W. Hamming, die Herrn Reed und
>> Solomon usw. bis heute unbeachtete Spinner geblieben,
>> und Paritätsbits haben ihren vedienten Platz im
>> Kuriositätenkabinett der Technik gefunden... :)
>
> Das soll wohl Ironie sein,

Richtig erkannt.


> aber in der Nachrichtentechnik hat Redundanz eine
> ganz andere Bedeutung als in der Programmierung und
> die Fehler sind ganz anderer Art.

Bei "andere Art der Fehler" stimme ich teilweise zu.

Redundanz bewirkt aber immer dasselbe: Aufblähung des
Datenvolumens -- und Möglichkeit zur Erkennung und
Korrektur von Fehlern.

Wenn Menschen für die redundanzfreie Kommunikation
geschaffen wären, würden wir alle immer und überall
Steno schreiben. Tun wir aber nicht.

Klare, kompakten Formulierungen im Quelltext sind
immer erstrebenswert -- aber die Kompaktheit sollte
nur soweit getrieben werden, wie sie der Klarheit
nicht widerspricht.
Dein eigener Dreizeiler oben ist ja ein gutes Beispiel
dafür.

von Rolf M. (rmagnus)


Lesenswert?

Possetitjel schrieb:
> Redundanz bewirkt aber immer dasselbe: Aufblähung des
> Datenvolumens -- und Möglichkeit zur Erkennung und
> Korrektur von Fehlern.

Ich hätte jetzt erwartet, dass da kommt: Möglichkeit zum Einbauen von 
mehr Fehlern…

von Egon N. (egon2321)


Lesenswert?


von Jobst Q. (joquis)


Lesenswert?

Possetitjel schrieb:
> Bei "andere Art der Fehler" stimme ich teilweise zu.
>
> Redundanz bewirkt aber immer dasselbe: Aufblähung des
> Datenvolumens -- und Möglichkeit zur Erkennung und
> Korrektur von Fehlern.

Redundanz ist dann sinnvoll und nützlich, wenn man die Wahl hat zwischen 
redundanten Blöcken und die Fehler vor der Wahl erkennen kann. Wie in 
der Nachrichtentechnik.

In Programmen ist das aber nicht gegeben. Da wird einer der redundanten 
Blöcke aufgerufen und wenn da ein Fehler drin ist, ist das ein Fehler 
des ganzen Progamms, das dann evtl abstürzt.

Redundanzarme Programmierung mit gemeinsamen Funktionen oder Makros für 
ähnliche Operationen bietet Fehlern weniger Möglichkeiten, sich zu 
verstecken.

Die aufrufenden Funktionen werden kürzer und übersichtlicher, da kommt 
man Fehlern schneller auf die Spur. Sind sie in der aufgerufenen 
Funktion, werden sie schneller entdeckt, weil diese häufiger aufgerufen 
wird.

von Apollo M. (Firma: @home) (majortom)


Lesenswert?

Tom schrieb:
> void doit(int mon, int day, int wday){
>     static struct {void (*a)(void); void (*b)(void);void (*c)(void); } F
> = {set_summer, do_nothing, set_winter};
>     static void (**p)(void) = (void(**)(void)) &F + 1;
>     p[(mon==10 && wday==7 && day>24) - (mon==3 && wday==7 && day>24)]();
> }

Hi,
ich vermisse hier noch die zeit/stunde, weil die uhr wird nicht um 
mitternacht umgestellt.

schönes beispiel, man beachte die dereferenzierung durch eckige klammern 
des functionspointers! daher anstatt *() = []


wieder was gelernt, danke mt!

von Egon N. (egon2321)


Lesenswert?

Tom schrieb:
> TI:07 schrieb:
>> geht das auch ohne switch case oder if ?
>> Also Funktionsaufruf über einen struct ?
> Ohne if und mit struct :void set_summer(void) {printf("sommer\n");}
> void set_winter(void) {printf("winter\n");}
> void do_nothing(void) {;}
>
> void doit(int mon, int day, int wday){
>     static struct {void (*a)(void); void (*b)(void);void (*c)(void); } F
> = {set_summer, do_nothing, set_winter};
>     static void (**p)(void) = (void(**)(void)) &F + 1;
>     p[(mon==10 && wday==7 && day>24) - (mon==3 && wday==7 && day>24)]();
> }
>
> Sehr kompakt mit wenigen Zeilen und deshalb überaus lesbar.

Ich hab keine Ahnung was da passieren soll.

Wieso nicht einfach die Formel vom Gauss benutzen? Dadurch bekommt man 
bequem den letzten Sonntag ermittelt.

https://de.wikipedia.org/wiki/Gau%C3%9Fsche_Wochentagsformel

von Dergute W. (derguteweka)


Lesenswert?

Moin,

Egon N. schrieb:
> Ich hab keine Ahnung was da passieren soll.

Das ist kein gutes Zeichen.

Ich hab sowas schon mal woanders vorgeschlagen, hier isses halt noch ein 
Stueck verhauter...

Beitrag "Re: Mittels Switch Case zwischen Unterprogrammen in der main wechseln"

Gruss
WK

von Jobst Q. (joquis)


Lesenswert?

Tom schrieb:
> TI:07 schrieb:
>> geht das auch ohne switch case oder if ?
>> Also Funktionsaufruf über einen struct ?
> Ohne if und mit struct :
>
1
> void set_summer(void) {printf("sommer\n");}
2
> void set_winter(void) {printf("winter\n");}
3
> void do_nothing(void) {;}
4
> 
5
> void doit(int mon, int day, int wday){
6
>     static struct {void (*a)(void); void (*b)(void);void (*c)(void); } F 
7
> = {set_summer, do_nothing, set_winter};
8
>     static void (**p)(void) = (void(**)(void)) &F + 1;
9
>     p[(mon==10 && wday==7 && day>24) - (mon==3 && wday==7 && day>24)]();
10
> }
11
> 
12
>
> Sehr kompakt mit wenigen Zeilen und deshalb überaus lesbar.

Warum als struct und nicht gleich als Array?

Die Funktionen sind doch alle vom selben Typ.

von Thomas (Gast)


Lesenswert?

Weil TI:07 ein struct wollte. Das ganze war natürlich nicht 
ernstgemeint.

von Jobst Q. (joquis)


Lesenswert?

Tom schrieb:
> p[(mon==10 && wday==7 && day>24) - (mon==3 && wday==7 && day>24)]();

Ist denn sichergestellt, dass eine wahre Bedingung +1 als Ergebnis hat?
Soweit ich mich erinnere, ist nur Null und nicht-null festgelegt.

von Apollo M. (Firma: @home) (majortom)


Lesenswert?

Jobst Q. schrieb:
> Ist denn sichergestellt, dass eine wahre Bedingung +1 als Ergebnis hat?
> Soweit ich mich erinnere, ist nur Null und nicht-null festgelegt.

passt!

boolische ausdrücke liefern 0=false oder 1=true zurück

UND

als bedingung wird alles ungleich 0 als true interpretiert.


mt

von Karl (Gast)


Lesenswert?

Egon N. schrieb:
> Wieso nicht einfach die Formel vom Gauss benutzen? Dadurch bekommt man
> bequem den letzten Sonntag ermittelt.

Wow! Zwei Modulos durch 100 und 400. Das heisst, mal eben noch 
Ganzzahldivision reinschmeißen.

Der letzte Sonntag im Monat ist im März und Oktober IMMER der zwischen 
25.* und 31. Iss so.

*) In Packed-BCD, wie es eine RTC wie die DS1307 liefert: $25

Egon N. schrieb:
> http://www.instructables.com/id/The-Arduino-and-Daylight-Saving-Time-Europe/

Von da: if (dow == 7 && mo == 10 && d >= 25 && h == 3 && DST==1)

Das hatte ich so ähnlich für C auf dem ATmega. Das wurde bescheiden 
umgesetzt, daraufhin habe ich das in die einzelnen ifs aufgelöst und es 
wurde controllerfreundlich umgesetzt.

Das Problem war wenn ich mich recht erinnere, dass der Compiler bei 
dieser Variante erstmal alle Variablen aus dem SRAM in Register lädt, 
und diese dann vergleicht. Wobei jeder einzelne Vergleich im Controller 
als compare+branch ausgeführt wird, geht ja nicht anders.

In ifs aufgelöst wird genauso mit compare+branch verglichen, nur dass 
der Compiler nur die Variable lädt, die er für den Vergleich braucht, 
und auch nur in ein Register. Was neben der unnötigen zusätzlichen 
Laufzeit für das Laden auch Register spart. Was dazu führt, dass der 
Compiler diese gesparten Register nicht pushen und poppen muss. Was 
wiederum zu gespartem Speicherplatz führt.

Sämtliche anderen obigen Konstrukte benötigen genau die gleichen 
compares+branches, egal ob über Hilfsvariablen oder über eine Structure. 
Denn der Controller kann nunmal nur das: Einen Wert mit einem anderen 
Vergleichen und darauf ein Ergebnisregister auf true oder false setzen.

Zusätzlich benötigen diese Konstrukte aber weiteren Speicherplatz, 
müssen Variablen schreiben und lesen, oder benötigen zuätzliche 
Register, die gepushed und gepoppt werden müssen. Kann mir keiner 
erzählen, dass das effizienter ist. Schlechter lesbar ist es noch dazu.

Deswegen hätte ich ja gern mal die vom Compiler erzeugten 
Assemblerlistings zu obigen Konstrukten gesehen. Aber dafür reichts dann 
wieder nicht.

von Tom (Gast)


Angehängte Dateien:

Lesenswert?

Karl schrieb:
> dass der Compiler bei
> dieser Variante erstmal alle Variablen aus dem SRAM in Register lädt,
> und diese dann vergleicht. Wobei jeder einzelne Vergleich im Controller
> als compare+branch ausgeführt wird, geht ja nicht anders.
So blöd ist gcc nichtmal mit -O0.

von Possetitjel (Gast)


Lesenswert?

Jobst Q. schrieb:

> Redundanz ist dann sinnvoll und nützlich, wenn man
> die Wahl hat zwischen redundanten Blöcken und die
> Fehler vor der Wahl erkennen kann. Wie in der
> Nachrichtentechnik.
>
> In Programmen ist das aber nicht gegeben. [...]

Wir haben unterschiedliche Dinge im Blick.

Dass man auf Block- oder Anweisungsebene keine
Redundanz haben will, ist klar ("DRY").

Quelltexte sind aber nicht nur Mittel zur Mensch-
Maschine-Kommunikation, sondern auch zur Mensch-
Mensch-Kommunikation -- und sei es nur, dass ich
den Quelltext verstehen will, den ich vor drei
Jahren geschrieben habe.

Der redundanzfreie Operatorwahn, den C auslebt, ist
da meiner Meinung nach wahrnehmungspsychologisch nicht
hilfreich.
Wenn die Menschen für redundanzfreie schriftliche
Kommunikation geeignet wären, würde wir alle immer
und überall Steno schreiben. Tun wir aber nicht.

Die normale Schriftsprache enthält reichlich
Redundanz, und das ist auch gut so [TM].

von Herbert P (Gast)


Lesenswert?

Hallo allseits,

obwohl die Diskussion in diesem Thread langsam programmier-esoterisch 
geworden ist, möchte ich gerne nochmals auf den OT zurückkommen (konnte 
leider nicht früher posten). Ich habe den Code mal auf Verdacht mit ein 
paar defines ergänzt, damit er kompilierbar wird, und dann dem 
Compiler-Explorer von Mats Godbolt (https://godbolt.org/) zum Fraß 
vorgeworfen:
1
#include <stdint.h>
2
3
#define ENERGIE_SCHWELLE_OBEN 5
4
#define ENERGIE_SCHWELL_UNTEN 1
5
#define LED_ENERGIE_GN_ON *((uint16_t *)0x5000) = 1
6
#define LED_ENERGIE_RT_ON *((uint16_t *)0x5002) = 1
7
#define LED_ENERGIE_GE_ON *((uint16_t *)0x5004) = 1
8
9
//_1________________________________________
10
void EnergieRestAmpel1(uint16_t RestZeit)
11
{
12
if(RestZeit >= ENERGIE_SCHWELLE_OBEN)
13
{
14
 LED_ENERGIE_GN_ON;              
15
 return;
16
}
17
if(RestZeit <= ENERGIE_SCHWELL_UNTEN)
18
{
19
 LED_ENERGIE_RT_ON;              
20
 return;
21
}
22
 LED_ENERGIE_GE_ON;              
23
}
24
25
//_2_________________________________________
26
void EnergieRestAmpel2(uint16_t RestZeit)
27
{  
28
uint8_t Prio=3;
29
if(RestZeit >= ENERGIE_SCHWELLE_OBEN)  Prio=2;
30
if(RestZeit <= ENERGIE_SCHWELL_UNTEN)  Prio=1;
31
  
32
switch(Prio)
33
{
34
case 1:
35
 LED_ENERGIE_RT_ON;  
36
 break;
37
case 2:
38
 LED_ENERGIE_GN_ON;    
39
 break;
40
default:
41
 LED_ENERGIE_GE_ON;    
42
}
43
}

Was ich jetzt wirklich interessant finde, ist, dass er mit -O3 übersetzt 
für beide Funktionen identischen Code liefert. Hier das Ergebnis für den 
ARM64-Compiler, für AVR waren die beiden Funktionen ebenfalls gleich, 
andere habe ich nicht versucht:
1
EnergieRestAmpel1(unsigned short):
2
  uxth w0, w0
3
  cmp w0, 4
4
  bgt .L6
5
  cmp w0, 1
6
  ble .L7
7
  mov x0, 20484
8
  mov w1, 1
9
  strh w1, [x0]
10
  ret
11
.L6:
12
  mov x0, 20480
13
  mov w1, 1
14
  strh w1, [x0]
15
  ret
16
.L7:
17
  mov x0, 20482
18
  mov w1, 1
19
  strh w1, [x0]
20
  ret
21
EnergieRestAmpel2(unsigned short):
22
  uxth w0, w0
23
  cmp w0, 4
24
  bgt .L9
25
  cmp w0, 1
26
  bgt .L10
27
  mov x0, 20482
28
  mov w1, 1
29
  strh w1, [x0]
30
  ret
31
.L9:
32
  mov x0, 20480
33
  mov w1, 1
34
  strh w1, [x0]
35
  ret
36
.L10:
37
  mov x0, 20484
38
  mov w1, 1
39
  strh w1, [x0]
40
  ret

Das heißt, dass alle Überlegungen sich wirklich ausschließlich auf die 
Lesbarkeit beschränken können, in Puncto Laufzeit ist jegliche 
Diskussion überflüssig. :)

Gruß
Herby

von Apollo M. (Firma: @home) (majortom)


Lesenswert?

Herbert P schrieb:
> Das heißt, dass alle Überlegungen sich wirklich ausschließlich auf die
> Lesbarkeit beschränken können, in Puncto Laufzeit ist jegliche
> Diskussion überflüssig. :)

... der beweis geht fehl, weil kein induktiver beweis vorliegt, die 
schnelle verallgemeinerung ist hier eher wunsch als realität.

z.b. meine erfahrung mit gcc und avr ist, das bei umfangreichen 
bedingungen ich zwar regelmäßig switch/case bzgl. lesbarkeit mir wünsche 
aber if/else oft bessere code size liefert.

ich probier deshalb ab einer gefühlten mächtigkeit der vorliegenden 
bedingungen beide varianten bzgl. code size. die laufzeit war nie thema.

ich bin ansonsten ein switch/case kind, weil da mache ich weniger fehler 
...
und habe irgenwie einen besseren überblick was sache ist.


mt

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Apollo M. schrieb:
> die laufzeit war nie thema.
Das ist das Problem mit den Softwerkern. Am Besten vor dem Vergelcih 
noch einen Cast auf einen 64-Bit-Float. So bekommt man jeden GHz-Boliden 
langsam...

von Jobst Q. (joquis)


Lesenswert?

Possetitjel schrieb:
> Quelltexte sind aber nicht nur Mittel zur Mensch-
> Maschine-Kommunikation, sondern auch zur Mensch-
> Mensch-Kommunikation -- und sei es nur, dass ich
> den Quelltext verstehen will, den ich vor drei
> Jahren geschrieben habe.

Das will ich auch.

>
> Der redundanzfreie Operatorwahn, den C auslebt, ist
> da meiner Meinung nach wahrnehmungspsychologisch nicht
> hilfreich.

Da habe ich wohl eine andere Wahrnehmung als du.
(x+1)*y  habe ich schneller und sicherer überblickt als

Addiere 1 zu x und multipliziere das Ganze mit y.

Dass C sparsam mit Schlüsselwörtern umgeht und stattdessen lieber 
einfache Zeichen nimmt, sehe ich als einen seiner größten Vorteile. Über 
die Zeichen nehme ich die Struktur wahr und die Buchstaben bleiben 
größtenteil für den Inhalt (Variablen und Funktionen) reserviert.

Wörter wie begin und end sind weder redundanter noch verständlicher als 
{ und }. Als unbedarfter Leser von Pascal-Quelltexten würde ich mich 
wundern, warum man dem Programm zigmal sagen muss, dass es anfangen soll 
und ebenso oft, dass es aufhören soll.

Solche unnötigen Buchstabenwüsten zähle ich auch nicht unter Redundanz, 
sondern unter Ballast.

Hilfreiche Redundanz kenne ich auch. So habe ich mir angewöhnt, bei 
größeren Blöcken die schließenden Klammern mit den Bedingungen ihres 
Anfangs zu kommentieren.

      }// for(i=0;i<NSets;i++)
    }// while(*s)
  }// else zu if(r != l)

#endif // #ifdef ARM

von Karl (Gast)


Lesenswert?

Jobst Q. schrieb:
> So habe ich mir angewöhnt, bei
> größeren Blöcken die schließenden Klammern mit den Bedingungen ihres
> Anfangs zu kommentieren.
>       }// for(i=0;i<NSets;i++)
>     }// while(*s)
>   }// else zu if(r != l)

Und das ist weniger als ein begin...end?

Es gibt moderne IDE, die haben sowas wie Autovervollständigen oder 
intelligente Klammerung. So wie ein C die } automatisch nach der { 
gesetzt werden kann, geht das in Pascal auch. Nervt manchmal, aber in 
Pascal sehe ich ein irrtümlich gesetztes end; eher als eine irrtümlich 
gesetzte Klammer in C.

Ganz häßlich sind in C Folgen von )})}.

Abgesehen davon kann man sowohl in C begin...end anstatt {} verwenden 
als auch in Pascal {} statt begin...end, wenn man sich das hindefiniert. 
Das ist nicht der entscheidende Unterschied.

von Herbert P. (Gast)


Lesenswert?

Apollo M. schrieb:
> Herbert P schrieb:
>> Das heißt, dass alle Überlegungen sich wirklich ausschließlich auf die
>> Lesbarkeit beschränken können, in Puncto Laufzeit ist jegliche
>> Diskussion überflüssig. :)
>
> ... der beweis geht fehl, weil kein induktiver beweis vorliegt, die
> schnelle verallgemeinerung ist hier eher wunsch als realität.

Mein Post war weder Verallgemeinerung noch der Versuch eines Beweises 
sondern beschränkte sich ausschließlich auf das publizierte Codestück. 
Es ist richtig, dass komplexere Konstrukte nicht ganz so einfach zu 
betrachten sind, aber in der Regel werden bei einer Handvoll Vergleichen 
(ich hab das mal bis zu 10 untersucht) if-else-Ketten und vergleichbare 
Switch-Awnweisungen zu gleichem Maschinenencode übersetzt bei 
Optimierung. Bei mehr Fällen macht switch in der Regel dann 
Sprungtabellen, außer odie Löcher werden zu groß.

Bei Sonderfällen oder extremen Konstrukten treten dann auch schnell 
Unterschiede zwischen den Compilern auf, so dass man hier eigentlich gar 
nicht verallgemeinern kann.

Gruß
Herby

von A. S. (Gast)


Lesenswert?

Jobst Q. schrieb:
> Hilfreiche Redundanz kenne ich auch. So habe ich mir angewöhnt, bei
> größeren Blöcken die schließenden Klammern mit den Bedingungen ihres
> Anfangs zu kommentieren.
>
>       }// for(i=0;i<NSets;i++)
>     }// while(*s)
>   }// else zu if(r != l)

Verwende da lieber einen Editor. (nicht "Editor" von Windows;-). Der 
Kommentar veraltet zu schnell.

Lothar M. schrieb:
> Am Besten vor dem Vergelcih
> noch einen Cast auf einen 64-Bit-Float.
Gut lesbarer Code ist selten Grund für Laufzeitprobleme. Ein solcher 
Cast wäre Gift für beides. Im Gegenteil: Wenn ich einen Code gut lesen 
kann, erkenne ich solchen Unsinn eher und es wird ... nur besser.

von Possetitjel (Gast)


Lesenswert?

Jobst Q. schrieb:

> Da habe ich wohl eine andere Wahrnehmung als du.

Mag sein...


> (x+1)*y  habe ich schneller und sicherer überblickt als
>
> Addiere 1 zu x und multipliziere das Ganze mit y.

Nun ja, das liegt mMn einfach daran, dass die(se) mathe-
matischen Operatoren in C im großen und ganzen dieselbe
Bedeutung haben wie in der Mathematik, und die Arithmetik
hat man schon einige Jahre in der Grundschule trainiert...


> Dass C sparsam mit Schlüsselwörtern umgeht und stattdessen
> lieber einfache Zeichen nimmt, sehe ich als einen seiner
> größten Vorteile.

Hmm. Du machst mich nachdenklich. Vielleicht habe ich mir
einfach die falsche Begründung für meine Beobachtung
zurechtgelegt.

Bescheuert finde ich z.B., dass sowohl "=" als auch "=="
belegt -- und i.d.R. auch im selben Kontext zulässig sind,
so dass automatische Prüfung durch den Compiler schwierig
ist.
Die Verwendung von "*" für Pointer ist auch so ein Beispiel;
wieso muss ich einen "Mal"-Punkt schreiben, wenn ich gar
nicht multiplizieren will?
Von "++" und "--" schweigen wir lieber. Ich gestehe aber
zu, dass das nix mit den Operatoren an sich zu tun hat,
sondern damit, wie man sie in Ausdrücken verwenden darf
(--> Seiteneffekte).

Es gibt noch weitere Beispiele; "&&" etwa, was ein
Etikettenschwindel ist, weil boolesches AND kommutativ
ist, && aber nicht.

> Über die Zeichen nehme ich die Struktur wahr [...]
> Wörter wie begin und end sind weder redundanter noch
> verständlicher als { und }.

...wobei mir in diesem Zusammenhang eine hübsche Ironie
aufgefallen ist: Wenn man die Programmstruktur primär
über "{" und "}" wahrnimmt -- warum schreibt dann niemand
Blocksatz mit 72 Zeichen je Zeile?
Warum wird dann -- teils erbittert -- über die Formatie-
rung von Quelltexten gestritten?
Die Wahrheit ist: Alle "konventionellen" Programmier-
sprachen sind auch "graphische" Programmiersprachen; die
Klammern sind für den Compiler, die Einrückung für den
Menschen. :)

Das nur als Anmerkung; in der Sache gebe ich Dir Recht.


> Hilfreiche Redundanz kenne ich auch. So habe ich mir
> angewöhnt, bei größeren Blöcken die schließenden Klammern
> mit den Bedingungen ihres Anfangs zu kommentieren.
>
>       }// for(i=0;i<NSets;i++)
>     }// while(*s)
>   }// else zu if(r != l)
>
> #endif // #ifdef ARM

Das finde ich witzig, denn das mache ich auch.

Die Gefahr, dass der Kommentar veraltet, besteht zwar, ist
aber meiner Erfahrung nach nicht übermäßig groß, weil ich
bei Änderungen an der Steuerstruktur sowieso die Einrückung
und Klammerung anpassen muss; da fällt das Korrigieren des
Kommentars mit ab.

von Egon N. (egon2321)


Lesenswert?

Possetitjel schrieb:
> Bescheuert finde ich z.B., dass sowohl "=" als auch "=="
> belegt -- und i.d.R. auch im selben Kontext zulässig sind,
> so dass automatische Prüfung durch den Compiler schwierig
> ist.
> Die Verwendung von "*" für Pointer ist auch so ein Beispiel;
> wieso muss ich einen "Mal"-Punkt schreiben, wenn ich gar
> nicht multiplizieren will?
> Von "++" und "--" schweigen wir lieber. Ich gestehe aber
> zu, dass das nix mit den Operatoren an sich zu tun hat,
> sondern damit, wie man sie in Ausdrücken verwenden darf
> (--> Seiteneffekte).

https://de.wikibooks.org/wiki/C-Programmierung:_Liste_der_Operatoren_nach_Priorit%C3%A4t

Hast du bei einer Zeile Code keine Ahnung was der Operator macht auf den 
ersten Blick - richtig klammern! Ist das nicht sofort ersichtlich ist 
der Code als Müll zu betrachten.

von Possetitjel (Gast)


Lesenswert?

Karl schrieb:

> Jobst Q. schrieb:
>> So habe ich mir angewöhnt, bei
>> größeren Blöcken die schließenden Klammern mit den
>> Bedingungen ihres Anfangs zu kommentieren.
>>       }// for(i=0;i<NSets;i++)
>>     }// while(*s)
>>   }// else zu if(r != l)
>
> Und das ist weniger als ein begin...end?

Wieso "weniger"?

Längere Blöcke mit mehreren Schachtelungsebenen übersieht
man selbst auf dem Monitor nicht mehr so einfach. Ich sehe
da NICHT mehr auf einen Blick, welche schließende zu welcher
öffnenden Klammer gehört; da sind diese Kommentare schon
hilfreich.


> Es gibt moderne IDE, die haben sowas wie Autovervollständigen
> oder intelligente Klammerung. So wie ein C die } automatisch
> nach der { gesetzt werden kann, geht das in Pascal auch.

Das hilft gar nix, wenn man bemerkt, dass man NACH einem
bestimmten Anweisungsblock noch eine Anweisung einfügen muss,
und beim Ändern den falschen Block (=die falsche schließende
Klammer) erwischt.
Die Kommentare sind sinnvoll für's Ändern -- nicht für's
Neu-Schreiben.


> Ganz häßlich sind in C Folgen von )})}.

Schließende geschweifte Klammern markieren ein Block-Ende;
das bekommt eine Zeile für sich.

von Jemand (Gast)


Lesenswert?

Possetitjel schrieb:
> Längere Blöcke mit mehreren Schachtelungsebenen übersieht
> man selbst auf dem Monitor nicht mehr so einfach. Ich sehe
> da NICHT mehr auf einen Blick, welche schließende zu welcher
> öffnenden Klammer gehört; da sind diese Kommentare schon
> hilfreich.

Welchen Editor verwendest du? Diese Software muss ich unbedingt meiden, 
scheint ja gar nichts zu können.

von Karl (Gast)


Lesenswert?

Possetitjel schrieb:
> Längere Blöcke mit mehreren Schachtelungsebenen übersieht
> man selbst auf dem Monitor nicht mehr so einfach...

Auch wenn es bei mir abgeschaltet ist, weil ich es nicht nutze: Moderne 
IDE haben Codefaltung auf mehreren Ebenen.

Ebenso markieren diese IDE schließende zusammen mit öffnenden Klammern, 
ebenso das zu einem "end" gehörige "begin" und umgekehrt.

Possetitjel schrieb:
> Schließende geschweifte Klammern markieren ein Block-Ende;
> das bekommt eine Zeile für sich.

Das machst Du vielleicht so, aber es gibt genug Code wo das nicht 
gemacht wird.

von A. S. (Gast)


Lesenswert?

Possetitjel schrieb:
> Wenn man die Programmstruktur primär
> über "{" und "}" wahrnimmt -- warum schreibt dann niemand
> Blocksatz mit 72 Zeichen je Zeile?
> Warum wird dann -- teils erbittert -- über die Formatie-
> rung von Quelltexten gestritten?

Hier gibt es eine Redundanz: Der Blocksatz UND die Klammern bestimmen 
die Struktur, genauso wie bei begin/end.

Bei Python ist es nur der Blocksatz, was m.E. "fragil" ist.

Bei C stört {/} einfach weniger als begin/end, weil es einfach schneller 
erfassbar ist.

In der Mathematik haben wir uns daran gewöhnt, dass die schnelle 
Erfassbarkeit wichtig ist. Wir erkennen es nichtmal. Operatoren, 
Ziffern, Variablen, etc. sind meist nur je ein Zeichen. Daneben noch 
Schreibweisen, die weiter Einkürzen (z.B. 3E7). Eine Formel y=2x wäre 
nacht Wirth wohl sowas wie "FunktionsWERT IST Zwei MAL 
FunktionsARGUMENT". Den Vergleich kann man für witzig oder blöd halten, 
Fakt ist, dass selbst Schulkinder (aus gutem Grund) y=2*x normal finden.

von Possetitjel (Gast)


Lesenswert?

Karl schrieb:

> Possetitjel schrieb:
>> Längere Blöcke mit mehreren Schachtelungsebenen übersieht
>> man selbst auf dem Monitor nicht mehr so einfach...
>
> Auch wenn es bei mir abgeschaltet ist, weil ich es nicht
> nutze: Moderne IDE haben Codefaltung auf mehreren Ebenen.

Ja, stimmt, ... die boshafte Frage des Vorposters zielte
ja auch in diese Richtung.


> Ebenso markieren diese IDE schließende zusammen mit
> öffnenden Klammern, ebenso das zu einem "end" gehörige
> "begin" und umgekehrt.

Das macht selbst der (von mir i.d.R. verwendete) mcedit;
das hilft aber nix, wenn ein Papierausdruck vor mir liegt.
Und -- ja! Ich verwende Papierausdrucke für (informelle)
Code-Reviews.


> Possetitjel schrieb:
>> Schließende geschweifte Klammern markieren ein
>> Block-Ende; das bekommt eine Zeile für sich.
>
> Das machst Du vielleicht so, aber es gibt genug
> Code wo das nicht gemacht wird.

Sicher -- aber man kann ja die Programmiersprache nicht
für JEDE Unsitte verantwortlich machen.

von Sheeva P. (sheevaplug)


Lesenswert?

Possetitjel schrieb:
> Die Wahrheit ist: Alle "konventionellen" Programmier-
> sprachen sind auch "graphische" Programmiersprachen; die
> Klammern sind für den Compiler, die Einrückung für den
> Menschen. :)

Signaturverdächtig. ;-)

von Karl (Gast)


Lesenswert?

Possetitjel schrieb:
> aber man kann ja die Programmiersprache nicht
> für JEDE Unsitte verantwortlich machen.

Bei C? Doch!

von Possetitjel (Gast)


Lesenswert?

Achim S. schrieb:

> Possetitjel schrieb:
>> Wenn man die Programmstruktur primär
>> über "{" und "}" wahrnimmt -- warum schreibt dann niemand
>> Blocksatz mit 72 Zeichen je Zeile?
>> Warum wird dann -- teils erbittert -- über die Formatie-
>> rung von Quelltexten gestritten?
>
> Hier gibt es eine Redundanz: Der Blocksatz UND die Klammern
> bestimmen die Struktur, genauso wie bei begin/end.

Naja, was heisst "bestimmen"?
Wie schon geschrieben: Die Klammern sind für den Compiler,
die Einrückung ist für den Menschen.


> Bei Python ist es nur der Blocksatz, was m.E. "fragil" ist.

Ja, ich halte das auch für keine gute Idee.
Besser eine IDE, die automatisch gemäß der Klammerung einrückt,
als ein Interpreter, der gemäß der Einrückung klammert :)


> Bei C stört {/} einfach weniger als begin/end, weil es
> einfach schneller erfassbar ist.
>
> In der Mathematik haben wir uns daran gewöhnt, dass die
> schnelle Erfassbarkeit wichtig ist. Wir erkennen es nichtmal.
> [...]

Jaja...

Jobst hat mich schon bei meiner ersten Antwort unsicher
gemacht. Wer nach Noten singen oder musizieren kann, der
weiss ja aus eigener Erfahrung, dass man auch einen sehr
speziellen Code mit ziemlich merkwürdigen Zeichen flüssig
lesen lernen kann.

Es können also nicht die Sonderzeichen an sich sein, die
mich an C stören, es muss irgend etwas anderes sein.


> Operatoren, Ziffern, Variablen, etc. sind meist nur je
> ein Zeichen. Daneben noch Schreibweisen, die weiter
> Einkürzen (z.B. 3E7). Eine Formel y=2x wäre nacht Wirth
> wohl sowas wie "FunktionsWERT IST Zwei MAL FunktionsARGUMENT".
> Den Vergleich kann man für witzig oder blöd halten, [...]

Nee, weder -- noch.
Ich denke, der Vergleich ist ungerecht gegenüber Wirth.

Die Grundrechenarten werden in Pascal genauso codiert wie
im normalen Leben; das ist nicht der Punkt. Die Sache mit
der Zuweisung (":=") ist mMn deutlich vernünftiger als in C.
Einzig unrühmliche Ausnahme ist begin/end; das hätte nicht
sein müssen.

Ihr habt mich (teilweise) bekehrt: Das Problem ist offen-
sichtlich NICHT, dass viele Operatoren durch Sonderzeichen
codiert werden -- das funktioniert ja in anderen Programmier-
sprachen und auch gänzlich außerhalb des Computers recht gut.
Also muss das Problem in den Operatoren selbst liegen. Hmm.
Mal überdenken.

von Possetitjel (Gast)


Lesenswert?

Sheeva P. schrieb:
> Possetitjel schrieb:
>> Die Wahrheit ist: Alle "konventionellen" Programmier-
>> sprachen sind auch "graphische" Programmiersprachen; die
>> Klammern sind für den Compiler, die Einrückung für den
>> Menschen. :)
>
> Signaturverdächtig. ;-)

:)

von Jobst Q. (joquis)


Lesenswert?

Karl schrieb:
> Possetitjel schrieb:
>> aber man kann ja die Programmiersprache nicht
>> für JEDE Unsitte verantwortlich machen.
>
> Bei C? Doch!

Man muss nicht alles machen, was nicht verboten ist.

Und man muss auch nicht alles verbieten, was unsinnig ist.

Scharfes Werkzeug ist immer auch gefährlich. Aber es bringt auch nichts, 
deshalb mit stumpfem Werkzeug zu arbeiten.

Und es kommt auch auf die Ebene an. Von einer Fräsmaschine kann man 
einige Sicherheitsvorkehrungen erwarten, von einem einfachen Stechbeitel 
nicht.

von Bernd N (Gast)


Lesenswert?

>       }// for(i=0;i<NSets;i++)
>     }// while(*s)
>   }// else zu if(r != l)

Code mit Code kommentieren :-) auweia, C Legasteniker.

von A. S. (Gast)


Lesenswert?

Possetitjel schrieb:
> Also muss das Problem in den Operatoren selbst liegen. Hmm. Mal
> überdenken.

Da sind deine Argumente ja richtig. * für prt, ++ für +=1 (und das für 
...).

Das lässt sich wohl nur erklären, wenn man auf damaliger Tastaturen 
schaut. Die trigraphen geben einen ersten Hinweis.

Wenn Buchstaben ausscheiden, und möglichst wenige Schlüsselwörter, ...


If(*x+++++y)...

von Jobst Q. (joquis)


Lesenswert?

Bernd N schrieb:
> Code mit Code kommentieren :-) auweia, C Legasteniker.

Peinlich. Bevor man andere als Legastheniker beschimpft, sollte man sich 
erkundigen, wie es geschrieben wird.

von Einer K. (Gast)


Lesenswert?

Achim S. schrieb:
> If(*x+++++y)...

Da geht aber noch min ein Stern mehr!
 If(**x+++++y)...

von Rolf M. (rmagnus)


Lesenswert?

Bernd N schrieb:
>>       }// for(i=0;i<NSets;i++)
>>     }// while(*s)
>>   }// else zu if(r != l)
>
> Code mit Code kommentieren :-) auweia, C Legasteniker.

Den Zweck nicht verstanden? Bei #ifdefs mache ich das immer. Wenn man 
davon mehrere im Code hat, wird das sonst sehr unübersichtlich, vor 
allem, weil man bei denen ja in der Regel keine Einrückung zur 
Gliederung des darin enthaltenen Codes nutzt.
Bei XML ist es sogar vorgeschrieben, beim Schließen des Tags dessen Name 
nochmal zu wiederholen.

Achim S. schrieb:
> Possetitjel schrieb:
>> Also muss das Problem in den Operatoren selbst liegen. Hmm. Mal
>> überdenken.
>
> Da sind deine Argumente ja richtig. * für prt, ++ für +=1 (und das für
> ...).

Bevor ich C genutzt habe, fand ich x = x + 1 irgendwie etwas 
umständlich, aber es gab nichts anderes, also war das eben so. Und es 
ist ja nun auch wirklich kein Hexenwerk, die Bedeutung von ++ und += zu 
lernen.

> If(*x+++++y)...

Manche betätigen sich damit ja geradezu künstlerisch:
1
while ( x --> 0)

von Karl (Gast)


Lesenswert?

Jobst Q. schrieb:
> Von einer Fräsmaschine kann man
> einige Sicherheitsvorkehrungen erwarten, von einem einfachen Stechbeitel
> nicht.

Leider montieren einige den Stechbeitel an einen 10kW-Motor, den sie auf 
einen Handkarren laden, fahren damit in der Gegend rum und sagen: Guck, 
meine Fräsmaschine.

Ich bin ja immer wieder beeindruckt, wenn darauf hingewiesen wird, dass 
man mit C doch AUCH guten Code schreiben könnte.

Warum wird es dann nicht gemacht?

Wenn man sich so die üblichen Einfallstore in Software anschaut: Strings 
schreiben über den definierten Bereich, beim Laden eines Bildes 
schreiben Metadaten über den erlaubten Bereich hinaus, Pointer zeigen 
auf ausführbaren Speicher... das sind so Sachen, da würde Dir ein 
Pascal- oder Ada-Compiler schon beim Kompilieren auf die Finger klopfen 
oder spätestens beim Ausführen eine Exception werfen, aber nicht einfach 
Schadcode ausführen als wäre nix dabei.

von Dirk B. (Gast)


Lesenswert?

*** Vorsicht: im Anschluss folgt ein längerer Text der entscheidbare, 
faktenfähige Inhalte enthalten könnte **
edit: ist wohl noch etwas längerer geworden: besondere Vorsicht
*** bitte nur lesen/reagieren, wenn ganze Sätze im Kontext gelesen und 
bei Reaktionen genannt werden können **

Possetitjel schrieb:
> Ihr habt mich (teilweise) bekehrt: Das Problem ist offen-
> sichtlich NICHT, dass viele Operatoren durch Sonderzeichen
> codiert werden -- das funktioniert ja in anderen Programmier-
> sprachen und auch gänzlich außerhalb des Computers recht gut.
es ist vermutlich schlimmer: Du hast es (nachweisbar durch die Nennung 
eigener Beispiele) selber (aktiv) verstanden und wurdest nicht (passiv) 
bekehrt...
> Also muss das Problem in den Operatoren selbst liegen. Hmm.
> Mal überdenken.
... zumindest fast. Also ich persönlich hab bspw. ein Problem Pilze zu 
essen, aber das Problem muss nicht in den Pilzen sein und ganz häufig 
ist es noch nicht einmal ein Problem d.h. solange Du nicht aus 
Umweltgründen (bspw. finanzielle Engpässe) dazu gezwungen bist die 
Operatoren zu benutzten (C-Programme schreiben)  ist "das Problem" ein 
Nil-Pointer auf eine völlig unstrukturierte Variable.

Dein Beispiel für eine falsche Kodierung des Schlüsselwortes "Redundant" 
dürfte sich damit vom Ergebnis her erledigt haben, aber ich hatte das 
als Konzept für eine Erklärung eingeplant, deswegen nochmal kurz:
Prüfung "Redundanz" Duden: "überflüssige Information": kann weg, muss 
aber nicht d.h. die Information erzeugt selber keinen Fehler UND 
implizit: es existiert auch eine Information, die beim weglassen der 
'redundanten' übrig bleibt.
1
C:
2
 int  i;// das 'Schlüsselsymbol' " " ist redundant und könnte mit:
3
 int i;
vereinfacht werden.
Pascal:
1
Var i:integer;;// ein ";" ist redundant.
2
Var i::integer;;// geht nicht: die aus C bekannte Redundamnz wird von P. nicht unterstützt (und umgekehrt beim
3
";")
Reduntante Blöcke Klammerungen:
1
if bedingung
2
 then begin      // Information: Anweisungen a b werden
3
        begin    // entweder beide oder nicht ausgeführt
4
          anw_a; // ? ist eine Information reduntant
5
          anw_b; // ! mind. eine Information kann wegegelassen
6
        end;     // werden UND wenn alle begin ... end weggelassen werden,
7
      end;       // dann ändert sich die Aneisungsausführung ==> redundant
8
9
 if bedingung
10
  then begin  // kein Block/eine(1) Anweisung
11
       anw_a; // begin ... end kann weg, aber
12
       end;   // wenn alle begin ... end entfernt werden
13
 begin        // ändert sich nichts ==> nicht redundant, 
14
 anw_b;       // sondern nur überflüssig OHNE Information
15
 end;
Praxistest redundante Seilbahn:
a) Kabine hängt an 3 Seilen. 2 fallen aus: Kabine vom Boden entfernt. 
3 Ausfälle: Kabine erreicht Bodenkontakt
b) Kabine ist bereits abgestürzt. 3 Seile sind gespannt. 3 Ausfälle: 
Kabine behält Bodenkontakt: für die
betroffene Kabine waren die 3 Seile vor dem Ausfall nicht einmal 
redundant

im Beispiel aus der Füllstoffindustrie "Aus aktuellem 
Anlass:"(Ausschnitt)
1
 if rtc.month = $03 then begin  // 'fasst' if (rts.sum)... zusammen
2
    if (rtc.summ and Rsumm) = 0 then begin // 'fasst' if (rtc.hh)... zusammen
3
       if rtc.hh = $02 then begin  // Information:
4
          rtc.hh := $03;           // hh / sum sollen 
5
          rtc.summ := Rsauto or Rsumm;//zusammen  ausgeführt werden
6
       end;
7
    end;
8
  end;
9
10
 if rtc.month = $03 then
11
    if (rtc.summ and Rsumm) = 0 then
12
       if rtc.hh = $02 then begin
13
          rtc.hh := $03;
14
          rtc.summ := Rsauto or Rsumm;
15
       end;//keine Änderung
16
17
 1:1 Kopie Pascal (Füllstoff)==> C (Füllstoff):
18
 void rtc_checksummer();
19
 {if (rtc.summ and Rsauto != 0)  {  // prüfen auf Flag Auto
20
    if (rtc.wday == 7)  {  // wenn Sonntag
21
      if (rtc.day >= $25  {  // wenn letzter Sonntag im Monat
22
        if (rtc.month == $03) {  // wenn März, Umschalten auf Sommerzeit
23
          if ((rtc.summ and Rsumm) == 0 ) {  //wenn Flag nicht gesetzt
24
            if (rtc.hh == $02)  {
25
              rtc.hh = $03;
26
              rtc.summ = Rsauto or Rsumm;
27
              rtc_setsummer();  // Sommerzeit schreiben
28
            };
29
          };
30
        } else if (rtc.month == $10)  {  // wenn Oktober, Umschalten auf Winterzeit
31
          if ((rtc.summ and Rsumm) != 0)  {  //wenn Flag gesetzt
32
            if (rtc.hh == $03) {
33
              rtc.hh = $02;
34
              rtc.summ = Rsauto;
35
              rtc_setsummer();  // Winterzeit schreiben
36
            };
37
          };
38
        };
39
      };
40
    };
41
  };
42
};
==> für C Ungewohnte könnte die überflüssige und nicht redundante 
Klammerung noch(!) unleserlicher sein, aber unnötige Nichtinformation 
ist in beiden Sprachen ziemlich sicher unleserlicher

Häufig gibt es für exakt die gleiche Anweisung unterschiedliche 
Schreibweisen:
1
inc(x) <=> x=x+1 kann aber den Zweck verdeutlichen.
2
Bei C ist nach definiertem Standard:
3
 if ( bed_1 && bed_2)  Anweisung;
EXAKT, für die Ausführung, identisch mit
 if (bed_1)
    if (bed_2) Anweisung;
[/code]
Pascal hat keine offizielle Festlegung, aber praktisch alle Compiler 
benutzen SC als Vorgabe.
Mit
1
 If (tag=24)and (monat=24) then heiligabend:
 ist leslich: vom Prinzip könnte monat=24 zuerst geprüft werden, aber 
faktisch wird tag zuerst abgefragt. Falls sich herausstellen sollte, 
dass die Abfrage tag=24 sehr 'teuer' ist, dann lässt sich das leserliche 
Programm sehr einfach an die - in dem speziellen Fall wohl eher 
unwahrscheinlichen - Gegebenheiten anpassen in dem die
 beiden Bedingungen vertauscht werden.
 Beim umgekehrten Fall
1
 if pW<>nil then if pw^=flag_hlg then heiligabend:
 wird deutlich, dass pw<>nil erfüllt sein MUSS und nicht nur aus 
performance Gründen zuerst geprüft wird. Vom
 Prinzip ist ein bekanntes Pascal Konstrukt:
 if (pW<>nil) and ( pw^=flag_hlg) then heiligabend:
 ziemlich unleserlich und nur durch Gewohnheit, weil es vielfach so 
verwendet und beim Kontakt mit Fremdcode wohl unbewusst gelernt wird 
oder weil es kürzer ist, halbwegs lesbar.(genaue Ursache und ob 
überhaupt, ist nicht sicher),


Bei u.U. gewünschten Seiteneffekten ("function SE_toll:boolean") könnte 
es kompliziert werden:
 Pascal:
1
 if se_toll or true then ok; //u.U. könnte ein Compiler se_toll als Voreinstellung weg-optimieren
 C:
1
 if (se_toll or true) ok; //der Compiler darf ohne Optimierungsoption se_toll nicht vergessen, obwohl das Ergebnis mathematisch im Voraus [code]
2
berechnet werden könnte.

 Bei anderen Sprachen u.U. etwas vorsichtig sein: VB kennt zumindest 
and_then als schnellen Vergleich. VBA nicht (stammt aus einem nicht ganz 
freiwilligem  Experiment bzgl. Programmiersprache an dem ich mal 
teilgenommen hab und kann natürlich durch Erinnerungsfehler etwas anders 
sein)

 Tom's Code im Beitrag #5367644:
1
     static void (**p)(void) = (void(**)(void)) &F + 1;
2
     p[(mon==10 && wday==7 && day>24) - (mon==3 && wday==7 && day>24)]();
 > Sehr kompakt mit wenigen Zeilen und deshalb überaus lesbar.
 ist nicht unbedingt wegen der wenigen Zeilen lesbar und ob die 
komischen Zeichen lesbar sind hängt extrem am
 Lesenden, ABER dadurch dass die fehlende Uhrzeit
 Apollo M. schrieb:
 > ich vermisse hier noch die zeit/stunde, weil die uhr wird nicht um
 > mitternacht umgestellt.
nachweislich lesbar war ist ein sicherer Beweis für eine lesbare 
Fehlererkennung erbracht worden.
Der Fehler könnte in der Praxis je nach Implementierung sogar recht 
unterhaltsame Folgen bspw. wenn set_summer: byte hh;hh=hh+1 definiert 
ist und die Uhr am Tag der geplanten Umstellung mit maximaler 
Prozessorgeschwindigkeit die Uhr vorstellt, dann ist durch die 
Begrenzung auf ein Byte eine traditionelle Uhrzeit im Bereich 0..23 nur 
noch mit einer Wahrscheinlichkeit von unter 10% zu erwarten. Bei 
automatischer Tagumstellung nach 23:59 wäre die maximale Länge des 
letzten Sonntags nur durch die Prozessorgeschwindigkeit begrenzt 
gewesen.... :-)
vereinfacht: Fehler können Spass machen und wenn die nicht so bedrohlich 
wirken, dann können die wohl auch einfacher zu lesen sein. ==> Indiz für 
lesbaren Quelltext.

Ein netter möglicher Fehler ergäbe sich noch aus
Jobst Q. schrieb:
> Ist denn sichergestellt, dass eine wahre Bedingung +1 als Ergebnis hat?
> Soweit ich mich erinnere, ist nur Null und nicht-null festgelegt.
neuerdings ist mit C99 =>stdbool.h =>true=1 die Frage für eine seriöse 
Fehlerdarstellung wohl zu definiert, aber rein theoretisch falls Tom 
einen C95 Compiler /64bit mit true=-1 benutzt, dann
ließe sich sicher ein versuchter Sprung aus dem Programmspeicher 
vorhersagen--> praktisch optimaler Fehler

Durch die Analyse von Tom's lesbaren Code ... [gekürzt].... , aber wenn 
das SZ-flag als begründbar sicherer Bestandteil des Datums implementiert 
wird, bspw. bit 7 in hh, dann wären sowohl lesbarer Code als auch
performanterer Code praktisch unvermeidbar.
Begründung:
- lesbar:die zusammenhängenden Ereignisse 2_WZ-=>3_SZ bzw 3_SZ-=> 2_WZ 
ließen sich nur so auffällig trennen, sodass selbst ein unbewusster 
Vorsatz zu offensichtlich würde.
- Prozessorlast: der Spitzenwert an Vergleichen am Anfang der beiden 
Umstellungstage sinkt um %20
Falls das Programm an anderen Stellen ebenfalls Performance kritische 
Stellen hat, dann kann eine u.U.
aufwändige Bitmaskierung natürlich Probleme verursachen, aber ohne 
Kenntnis halbwegs definierter Problemstellen lässt sich keine 
Gesamtbelastungsbeurteilung erstellen.

Die Chance auf Realisierung für den Fall, dass der Betroffene eine 
Verbesserung eigentlich gern gesehen hätte
lässt sich mit
> Deswegen hätte ich ja gern mal die vom Compiler erzeugten
> Assemblerlistings zu obigen Konstrukten gesehen. Aber dafür reichts dann
> wieder nicht.
ziemlich sicher auszuschließen, da wenn es schon für die 
Anschaffung/Bedienung eines Compilers nicht reicht obwohl der 
realisierbare Wunsch besteht die von einem Compiler erzeugten 
Assemblerlistings zu obigen Konstrukten zu sehen, dann dürften weitere 
Barrieren bei der Umsetzung von Wünschen vorhanden sein.


Zwischenbilanz: der aktuelle Text ist ziemlich sicher zu lang und 
eigentlich wollte ich nur auf:
Jobst Q. schrieb:
> Redundanzarme Programmierung mit gemeinsamen Funktionen oder Makros für
> ähnliche Operationen bietet Fehlern weniger Möglichkeiten, sich zu
> verstecken.
>
> Die aufrufenden Funktionen werden kürzer und übersichtlicher, da kommt
> man Fehlern schneller auf die Spur. Sind sie in der aufgerufenen
> Funktion, werden sie schneller entdeckt, weil diese häufiger aufgerufen
> wird.
antworten, aber es gab da ein paar Zwischentexte.
Also die Kombination Aufrufhäufigkeit mit Parametern die eine 
automatische Fehlerentdeckung verursachen hat mit Sicherheit zu viele 
Seiteneffekte d.h. u.a. geplantes häufiges Aufrufen hat eine mögliche 
Performancerelevanz zur Folge, damit steigt die 
Untersuchungswahrscheinlichkeit und die Fehlerwahrscheinlichkeit sinkt, 
aber für zwei andere Hypothesen habe ich Indizien
a) ein nicht überprüfbares Experiment:  Kurzfassung in Stichpunkten
- Teilnahme an einer Fortbildung B.Eng u.a. Datenverarbeitung
- Versuchsteilnehmer: überwiegend Studenten die DV (in der Fortbildung 
nur eines von sehr unterschiedlichen
Modulen) eher kritisch distanziert verstehen
- ein TN der auch aus Unterhaltungsgründen ruhig in 3 Gruppen (einmal 
offiziell, 2*IM) teilnimmt und die Verwaltung des Programmcodes für alle 
3 Gruppen übernommen hat, aber - und das war das Interessante an dem 
Versuch - zweimal den persönlichen Programmierstil für den 
Prüfungsbeauftragten unleserlich im Sinne einer persönlichen 
Handschrifterkennung im Computerprogramm gestalten musste.
Resultat 1:
 - offizielle Gruppe hatte ca. 5 Seiten Programmcode (passte grob mit 
der Erwartungshaltung, keine Überraschung)
 - die beiden IM-Gruppen ca. 20 Seiten (leichte Überraschung: Zielgröße 
langes Listing ohne wirklich auffälligen Leerraum kann überraschend 
schwierig werden)
 - die nicht genau untersuchten Vergleichsgruppen müssen von einem 
Papierhersteller gesponsert gewesen sein (subjektiv gemessen)
Resultat 2: ein Ausfall in einer IM Gruppe (..manchmal gehen halt 
bestimmte Sachen nicht), aber ansonsten korrelierte sogar bei den 
individuellen Feedbackergebnissen vom unabhängigen Prüfungsbeauftragten 
der Performanceindikator mit der Papiermenge (weniger war in dem Fall 
bessere Note)
Für mich einerseits ein  guter Indikator, dass zumindest in Grenzen (gar 
kein Code ist sicher falsch ...bis ... deutlich unterdurchschnittlich 
lang) ein gewisses Optimum für unbekannt viele Möglichkeiten liegen 
könnte,
andererseits kenne ich natürlich den klassischen Messfehler:
Apollo M. schrieb:
> z.b. meine erfahrung mit gcc und avr ist, das bei umfangreichen
> bedingungen ich zwar regelmäßig switch/case bzgl. lesbarkeit mir wünsche
> aber if/else oft bessere code size liefert.
> ich bin ansonsten ein switch/case kind, weil da mache ich weniger fehler
> ...
> und habe irgendwie einen besseren überblick was sache ist.
auf deutsch: mit geeigneten Wahrnehmungen lassen sich sehr einfach die 
passenden Wünsche/Ergebnisse messen
ohne dass es auffällt.
Versuchsgruppen zu klein + undokumentiert + viele mögliche Messfehler 
==> eigentlich allenfalls als nette Story oder für interne Zwecke 
brauchbar, aber durch den zufälligen Fund eines mutmaßlich 
unveröffentlichten Freischaltcodes aktuell plausibler geworden (s.u.)

Bis hier bis auf den langen Text nichts wirklich belastbar 
interessantes, aber letztes WE ist mir eine Überraschung passiert, die 
mutmaßlich begründbar auch andere überraschen könnte:
- hobbymäßige µC Basteleien u.a. Lichtdeko.
- meine 'Standard' µC für alles Schaltbare waren 2313tiny 8k-14bit-pwm + 
8K digital
- geplantes Update 4312, weil sich rgb-kanäle als dekorativ 
herausgestellt haben und durch den verdoppelten Speicher 15PWM Kanäle 
relativ problemlos möglich sind bzw. sein sollten
- anderseits wenn schon so viel Speicher vorhanden ist, dann sollten 
mehrere lut uvm.  in den Speicher und dann wurde es wg. Interpolation 
etc. knapp mit Zeit. Sehr interessante Zusammenhänge.
vereinfacht: Situation in der viele Schritte viele kleine Verbesserungen 
bringen und alles komplizierter machen bis an anderen Stellen 
versehentlich Randbedingungen verletzt werden.

Eine Versuchsmöglichkeit: teilweise Problemübertragung auf Unbeteiligte 
d.h. eine Spekulation wenn das Problem bei einem simulierten Mega 
ähnlich ist, dann wurde entweder der problematische Code mit simuliert 
und lässt sich evtl. dort finden, ansonsten in der konkreten 
Implementation auf dem Ziel-Tiny (so etwas kann klappen, aber es ist 
keinerlei Wahrscheinlichkeit annähernd schätzbar)
Resultat: 16bit PWM mit 32Kanälen==>ca. 40% Auslastung, bei sauber 
strukturiertem hübsch sortierten Code, zig Registern etc.,--> sehr 
uneindeutig.

Nächster Versuch: aus Spass eine vom Prinzip nicht performance kritische 
Zielgröße probiert: Dateigröße bzw. Leserlichkeit: max. EINE Datei,EINE 
Textseite, keine Einzeloptimierungen
Resultat==>10%: irgendwie überraschend gut und extrem überraschend 
simpel. hmm.
Prüfliste für eine mögliche Verallgemeinerung:
Anz 8bit timer =1 : erfüllen alle mir bekannten AVR
Anz Register= 4 'hohe' d.h ein Indexregister wird 'verbraucht' + 2 
'vollwertige' aus  R16..31 + r0 r15 (tw. lässt sich das zu lasten 
anderer Kriterien optimieren) )
Speicher: 24byte sram/port
Flash=600b
Interrupts= 1, aber keine sonstigen erlaubt
ok
==> sicher induktiv beweisbar, dass für alle mir bekannten AVR eine 
Freischaltung aller 16bit-PWM Kanäle mit maximaler (untere Bits) bzw. 
F/4000 Frequenz (höhere bits) mit vertretbarem Aufwand möglich ist.
Extremfall Atmega (??) mit 5 Ports  ca. 15% Auslastung.
Max. F/2000 geht auch mit grob 3x% beim 5-port mega.
Also mit 20 MHZ *AtMega*: 34 *PWM-Kanäle* / *16*-*Bit* / 10KHz / 
ca.30% Auslastung* mEn. interessant.

Anhand einiger Beiträge aus dem Forum (bspw. einfache 12bit 16k pwm für 
3euro als Tipp bei einem vorhandenen 32K - praktisch wohl 31 - 
16bit-PWMM Mega mit deutlich reduzierter Flackerleistung) lässt sich 
plausibel begründen, dass der einfache Freischaltcode (Textdatatei 50*80 
Zeichen mit Zielgröße Lesbarkeit, also max. 3 Befehle/Zeile, sofern 
nicht bspw. nop nop nop nop o.ä. einfach lesbar wiederholt werden) 
ziemlich unbekannt ist.

Noch ein paar kurze Checks:
- ist eine absichtliche Geheimhaltung des Freischaltcodes wahrscheinlich 
plausibel?
Konzerne die 12pwm vermarkten: mutmaßlich genug anderen Spielkram im 
Portfolio--> eher nicht
Micochip bzw. Atmel (hätte genug Zeit zur Veröffentlichung gehabt): 
könnten u.U. mehr Chips insgesamt, aber weniger mit mehreren 
Hardware-PWM verkaufen.
==> albern
- kommerzielle Vermarktungsmöglichkeit?
==> eher Deko
kurz: ich glaube, dass es a) sehr wenige Menschen gibt die den 
Freischaltcode kennen, aber auch dass
b) dieses nur aus reinem Zufall so ist.

Da der Code vermutlich noch nicht öffentlich ist bietet sich mit diesem 
Beitrag bis in den April zur geplanten Veröffentlichung eine sehr 
seltene Gelegenheit sich ohne die Gefahr eines Selbstbetruges zu testen, 
was bei anderen unkontrollierten Situationen praktisch unvermeidbar ist.
Jeder Interessierte der etwas (AVR) Assembler kann und damit überhaupt 
die 'Codeanalysen' in den Beiträgen verstehen kann , kann daran 
teilnehmen. ALLE Versuche können ohne Hilfe mit eigenem heimischen PC am 
Simulator überprüft werden. ASM ist nicht so gefährlich wie C und nicht 
so geschwätzig wie Pascal, kann aber beliebig macrodiert werden, sodass 
keine Verzerrungen zu erwarten sind. Vermutlich (interessante Hypothese) 
könnten alle die den Zugang gefunden haben verschlüsselt miteinander 
kommunizieren, wüssten aber nicht wie und warum (etwas Spass, bestimmte 
Aussagen lassen sich vermutlich wirklich öffentlich austauschen, sodass 
die Beteiligten sich gegenseitig über die Kenntnis vergewissern könnten 
ohne dass 'Unwissende', also Späteinsteiger, Informationen zum 
Freischaltcode erhalten)

Im folgenden meine ich mit "die" Lösung nicht eine wirklich spezifische 
"die" Lösung, sondern eine die ungefähr mit dem Freischaltcode 
vergleichbar ist oder die aktuell am Freischaltcode ablesbar ist, formal 
ist eine Lösung ohne passendes Problem natürlich keine Lösung, aber ugs. 
wird es so verwendet und im Kontext sollte die etwas ungenaue Definition 
auch genug lesbar sein, um entscheidbare Kriterien zu haben.

An der Textversion lassen sich viele tw. mutmaßliche Eigenschaften 
ablesen:
- es ist sicher, dass es nicht die optimale Lösung ist, da im 
Freischaltcode sehr einfach Änderungsmöglichkeiten zu sehen sind, die 
aber sicher die Leserlichkeit stark beeinträchtigen würden und damit 
zumindest per Umfrage eine Situation 'beweisen' könnten die (zumindest 
tw. von diesen Beiträgen gelesen) eher ungewöhnlich ist: einfache 
Optimierung nur zu lasten der Leserlichkeit? zumindest interessant
- an anderen Stellen lässt sich ablesen, dass eine Verringerung der 
Latenz (max. Zyklenzahl im Interrupt) auf die Gesamtlast geht (u.U. eine 
Zielgröße die ansonsten gerne vergessen wird)
generell müssen bei der Lösung natürlich (?) andere Interrupts 
ausgeschlossen sein, aber max. Latenzen von <100T könnten in vielen 
Fällen das Performance-Problem relativeren.

nette Vorhersagen:
- Menschen die eine 14+bit BAM PWM implementiert haben, werden 
begründbar wahrscheinlich eine klassische Informatiker Problemsituation 
erlebt haben, aber das mutmaßliche Problem ist wahrscheinlich ein Teil 
der Lösung (getestet mit n=1)
- die Anzahl der Informationen im Sinne von  ... schwierig zu definieren 
praktisch Konzept für eine einfache  Änderung... damit die meisten ASM 
Programmieren den Freischaltcode selber schreiben könnte, dürfte ca. 4 
sein.
Vom Prinzip sehr einfach sobald  Punkte klar sind, nur vorher mutmaßlich 
schwierig.


Ich hoffe das ist ein interessantes Ostersuch Problem mit guter 
Unterhaltung, auch wenn der Text wohl seine zulässige Gesamtlänge 
maximal überschritten hat.
Viel Spass

Beitrag #5371336 wurde von einem Moderator gelöscht.
von Jobst Q. (joquis)


Lesenswert?

Karl schrieb:
> Ich bin ja immer wieder beeindruckt, wenn darauf hingewiesen wird, dass
> man mit C doch AUCH guten Code schreiben könnte.
>
> Warum wird es dann nicht gemacht?

Natürlich wird es gemacht. Der Anteil schlechten Codes an der gesamten 
Programmierung in C ist minimal, auch wenn du dich darauf spezialisiert 
hast, nur diese wahrzunehmen.

Aus der Tatsache, dass es Verkehrsunfälle gibt, kann nicht geschlossen 
werden, dass Unfälle generell nicht vermieden werden. Erst wenn die 
Anzahl Unfälle in Beziehung gesetzt wird zu den unfallfrei gefahrenen 
km, kann man eine realistische Aussage dazu treffen.

: Bearbeitet durch User
von Karl (Gast)


Lesenswert?

Dirk B. schrieb:
> Pascal hat keine offizielle Festlegung, aber praktisch alle Compiler
> benutzen SC als Vorgabe.
> Mit If (tag=24)and (monat=24) then heiligabend:
>  ist leslich

Mit der richtigen Typedef von monat würde Dir Pascal für die folgende 
Zeile sagen: Warning: unreachable code

Liebe Kinder, leider fällt die Bescherung für die nächsten Jahre aus.

Dirk B. schrieb:
> für C Ungewohnte könnte die überflüssige und nicht redundante
> Klammerung noch(!) unleserlicher sein, aber unnötige Nichtinformation
> ist in beiden Sprachen ziemlich sicher unleserlicher

Die Klammerung mag Dir überflüssig erscheinen, aber mit richtiger 
Klammerung wäre das hier: 
http://cdn3.spiegel.de/images/image-662536-860_poster_16x9-erjl-662536.jpg 
nicht passiert.

von Karl (Gast)


Lesenswert?

Jobst Q. schrieb:
> Der Anteil schlechten Codes an der gesamten
> Programmierung in C ist minimal, auch wenn du dich darauf spezialisiert
> hast, nur diese wahrzunehmen.

Nur weil Code größtenteils funktioniert heisst das noch nicht dass er 
nicht schlecht ist.

von A. S. (Gast)


Lesenswert?

Karl schrieb:
> Die Klammerung mag Dir überflüssig erscheinen, aber mit richtiger
> Klammerung wäre das hier:

so ein Knausern an Zeilen oder Zeichen ist echt nur von 1 oder 2 
Leerzeichen Einrückung zu toppen.

Bei uns hätte lint gemeckert.

Und wenn man so kleine Monitore hat, dass man den Scheiß nicht im 
Blocksatz machen kann (der dann auch viel schneller prüfbar ist), dann 
sind die selber schuld.

von Jobst Q. (joquis)


Lesenswert?

Achim S. schrieb:
> so ein Knausern an Zeilen oder Zeichen ist echt nur von 1 oder 2
> Leerzeichen Einrückung zu toppen.

Zwei Zeichen Einrückung ist doch optimal. Nicht zu übersehen, aber doch 
noch zu überblicken.

Ich stell mir gerade Karls Treppenmonstercode mit 4 oder 8 Zeichen 
Einrückung vor. Gruselig. Da bräuchte man einen halbrunden Bildschirm 
und Fischaugen, um es zu überblicken.

von N. G. (newgeneration) Benutzerseite


Lesenswert?

Ich werfe dazu einfach mal einen Buchtitel ein:

Clean Code: A Handbook of Agile Software Craftsmanship (Robert C. 
Martin)
(gibt es meines Wissens nach auch als PDF)

Sehr lesenswert. Gerade wegen diesen riesen if-Monstern über mehr als 
fünf Ebenen. Dann erübrigt sich auch die Eingangsfrage.

Mit freundlichen Grüßen,
N.G.

von Jemand (Gast)


Lesenswert?

Sind hier auch Anhänger der umgekehrten Quadrateinrückung?
Am Beispiel des bereits geposteten Codeschnipsels:
1
                                                  void rtc_checksummer();
2
                                                  {
3
                                     if (rtc.summ and Rsauto != 0) {  // prüfen auf Flag Auto
4
                          if (rtc.wday == 7) {  // wenn Sonntag
5
                 if (rtc.day >= $25) {  // wenn letzter Sonntag im Monat
6
          if (rtc.month == $03) {  // wenn März, Umschalten auf Sommerzeit
7
     if ((rtc.summ and Rsumm) == 0 ) {  //wenn Flag nicht gesetzt
8
  if (rtc.hh == $02) {
9
 rtc.hh = $03;
10
 rtc.summ = Rsauto or Rsumm;
11
 rtc_setsummer();  // Sommerzeit schreiben
12
  };
13
     };
14
          } else if (rtc.month == $10) {  // wenn Oktober, Umschalten auf Winterzeit
15
     if ((rtc.summ and Rsumm) != 0) {  //wenn Flag gesetzt
16
  if (rtc.hh == $03) {
17
 rtc.hh = $02;
18
 rtc.summ = Rsauto;
19
 rtc_setsummer();  // Winterzeit schreiben
20
  };
21
     };
22
          };
23
                 };
24
                          };
25
                                     };
26
                                                  };

von Karl (Gast)


Lesenswert?

N. G. schrieb:
> Clean Code: A Handbook of Agile Software Craftsmanship (Robert C.
> Martin)
> (gibt es meines Wissens nach auch als PDF)
> Sehr lesenswert.

Echt jetzt? Ich hab mal die als pdf verfügbaren "free sample chapters" 
überflogen: Ein Haufen Geschwätz, witzige Bildchen und zusammengetragene 
Zitate von diversen "Software-Gurus".

Nicht ein einziges Beispiel, wo man mal sieht worum es dem Typen geht. 
Wenn ich Computer-Belletristik will, hole ich mir ein Buch von Elsberg 
oder Suarez.

von Drrrtrr (Gast)


Lesenswert?

Karl schrieb:
> N. G. schrieb:
> Clean Code: A Handbook of Agile Software Craftsmanship (Robert C.
> Martin)
> (gibt es meines Wissens nach auch als PDF)
> Sehr lesenswert.
>
> Echt jetzt? Ich hab mal die als pdf verfügbaren "free sample chapters"
> überflogen: Ein Haufen Geschwätz, witzige Bildchen und zusammengetragene
> Zitate von diversen "Software-Gurus".
>
> Nicht ein einziges Beispiel, wo man mal sieht worum es dem Typen geht.
> Wenn ich Computer-Belletristik will, hole ich mir ein Buch von Elsberg
> oder Suarez.

Signalwort agile sollte da doch schon alles sagen, spätestens bei 
Craftsmanship hättest das Lesen aufhören können.

von Jay W. (jayway)


Lesenswert?

Jemand schrieb:
> Sind hier auch Anhänger der umgekehrten Quadrateinrückung?

Das nicht, aber Code sollte schon ästhetischen Ansprüchen genügen. ;-)
1
#!/usr/bin/perl -w                                      
2
use strict;
3
4
5
                                           $_='ev
6
                                       al("seek\040D
7
           ATA,0,                  0;");foreach(1..3)
8
       {<DATA>;}my               @camel1hump;my$camel;
9
  my$Camel  ;while(             <DATA>){$_=sprintf("%-6
10
9s",$_);my@dromedary           1=split(//);if(defined($
11
_=<DATA>)){@camel1hum        p=split(//);}while(@dromeda
12
 ry1){my$camel1hump=0      ;my$CAMEL=3;if(defined($_=shif
13
        t(@dromedary1    ))&&/\S/){$camel1hump+=1<<$CAMEL;}
14
       $CAMEL--;if(d   efined($_=shift(@dromedary1))&&/\S/){
15
      $camel1hump+=1  <<$CAMEL;}$CAMEL--;if(defined($_=shift(
16
     @camel1hump))&&/\S/){$camel1hump+=1<<$CAMEL;}$CAMEL--;if(
17
     defined($_=shift(@camel1hump))&&/\S/){$camel1hump+=1<<$CAME
18
     L;;}$camel.=(split(//,"\040..m`{/J\047\134}L^7FX"))[$camel1h
19
      ump];}$camel.="\n";}@camel1hump=split(/\n/,$camel);foreach(@
20
      camel1hump){chomp;$Camel=$_;y/LJF7\173\175`\047/\061\062\063\
21
      064\065\066\067\070/;y/12345678/JL7F\175\173\047`/;$_=reverse;
22
       print"$_\040$Camel\n";}foreach(@camel1hump){chomp;$Camel=$_;y
23
        /LJF7\173\175`\047/12345678/;y/12345678/JL7F\175\173\0 47`/;
24
         $_=reverse;print"\040$_$Camel\n";}';;s/\s*//g;;eval;   eval
25
           ("seek\040DATA,0,0;");undef$/;$_=<DATA>;s/\s*//g;(   );;s
26
             ;^.*_;;;map{eval"print\"$_\"";}/.{4}/g; __DATA__   \124
27
               \1   50\145\040\165\163\145\040\157\1 46\040\1  41\0
28
                    40\143\141  \155\145\1 54\040\1   51\155\  141
29
                    \147\145\0  40\151\156 \040\141    \163\16 3\
30
                     157\143\   151\141\16  4\151\1     57\156
31
                     \040\167  \151\164\1   50\040\      120\1
32
                     45\162\   154\040\15    1\163\      040\14
33
                     1\040\1   64\162\1      41\144       \145\
34
                     155\14    1\162\       153\04        0\157
35
                      \146\     040\11     7\047\         122\1
36
                      45\15      1\154\1  54\171          \040
37
                      \046\         012\101\16            3\16
38
                      3\15           7\143\15             1\14
39
                      1\16            4\145\163           \054
40
                     \040            \111\156\14         3\056
41
                    \040\         125\163\145\14         4\040\
42
                    167\1        51\164\1  50\0         40\160\
43
                  145\162                              \155\151
44
                \163\163                                \151\1
45
              57\156\056

Beitrag #5371859 wurde von einem Moderator gelöscht.
Beitrag #5371866 wurde von einem Moderator gelöscht.
Beitrag #5371886 wurde von einem Moderator gelöscht.
von Possetitjel (Gast)


Lesenswert?

Karl schrieb:

> Ich hab mal die als pdf verfügbaren "free sample chapters"
> überflogen: Ein Haufen Geschwätz, witzige Bildchen und
> zusammengetragene Zitate von diversen "Software-Gurus".

Vielleicht hättest Du mehr als das Vorwort lesen sollen.

Wobei: Niemand soll gezwungen werden dazuzulernen, auch Du
nicht.

Beitrag #5371912 wurde von einem Moderator gelöscht.
Beitrag #5371985 wurde von einem Moderator gelöscht.
Beitrag #5372033 wurde von einem Moderator gelöscht.
von Großer Zeh (Gast)


Lesenswert?

Possetitjel schrieb:

> Wobei: Niemand soll gezwungen werden dazuzulernen, auch Du
> nicht.


Die Frage ist: WILL man sich mit einer Sprache und einer solch häßlichen 
Syntax und Regeln für diese, die einem Würfelspiel gleichen, wirklich 
den Kopf vollstopfen? Normalerweise will man doch einen Algorithmus in 
einen Quelltext überseztzen und sich nicht mit einer Diva von 
Programmiersprache um jedes Sonderzeichen streiten.

Beitrag #5372148 wurde von einem Moderator gelöscht.
Beitrag #5372248 wurde von einem Moderator gelöscht.
Beitrag #5372304 wurde von einem Moderator gelöscht.
von Possetitjel (Gast)


Lesenswert?

Großer Zeh schrieb:

> Possetitjel schrieb:
>
>> Wobei: Niemand soll gezwungen werden dazuzulernen,
>> auch Du nicht.
>
> Die Frage ist: WILL man sich mit einer Sprache und
> einer solch häßlichen Syntax und Regeln für diese,
> die einem Würfelspiel gleichen, wirklich den Kopf
> vollstopfen?

Nun ja, was heisst "wollen"?

C hat ein paar Alleinstellungsmerkmale, und wenn man
die benötigt, hat man keine Wahl. Das ist ja inzwischen
oft genug durchdiskutiert worden.

Ich akzeptiere das auch ohne Groll, denn ich lerne
allmählich unterscheiden, was echte Ausdruckskraft
ist und was unnützer syntaktischer Zucker (wobei die
Zuschreibung "unnütz" natürlich subjektiv ist.)


> Normalerweise will man doch einen Algorithmus in
> einen Quelltext überseztzen und sich nicht mit einer
> Diva von Programmiersprache um jedes Sonderzeichen
> streiten.

Sicher -- nur wenn man das Können der Diva braucht,
muss man die Gage halt bezahlen.
Wenn nicht, dann nicht.

von Karl (Gast)


Lesenswert?

Großer Zeh schrieb im Beitrag #5372248:
> Soll man wirklich eine Programmiersprache erlernen, die die Fähigkeit,
> sich normal auszudrücken, so sehr zerstört?

Wieso gehst Da davon aus, dass er überhaupt eine Programmiersprache 
kann? Das ist ein Philosophiestudent im 20. Semester, der aus Versehen* 
über die Feiertage in der Unibibliothek eingeschlossen wurde.

*) über das "aus Versehen" können wir noch diskutieren.

von Karl (Gast)


Lesenswert?

Possetitjel schrieb:
> C hat ein paar Alleinstellungsmerkmale, und wenn man
> die benötigt, hat man keine Wahl.

Als da wären?

Typecast geht sogar unter Ada, wenn auch nicht so locker flockig ohne 
Prüfung aus der Hand.

von Teo D. (teoderix)


Lesenswert?

Sorry Leute,
hätt ich gewusst das mein Post ein Lawine von Geschwafel lostritt, hätt 
ichs gelassen. :D

von Possetitjel (Gast)


Lesenswert?

Karl schrieb:

> Possetitjel schrieb:
>> C hat ein paar Alleinstellungsmerkmale, und wenn man
>> die benötigt, hat man keine Wahl.
>
> Als da wären?

Das Zusammentreffen von
- Normung,
- Trennung von Sprachkern und Standardbibliothek,
- "kleinem" Sprachkern und
- "unvollständiger" Abstraktion.

Das prädestiniert C für den unübersehbaren Zoo von
mittelgroßen, kleinen und kleinsten Maschinen.

All das wurde hier aber schon x Mal durchgekaut.

Beitrag #5372644 wurde von einem Moderator gelöscht.
von Carl D. (jcw2)


Lesenswert?

Wichtig ist, Programme so zu schreiben, daß der Leser versteht, was man 
dem Compiler sagen wollte.
Das gilt auch für Texte, deren Inhalt nicht an Compiler übergeben werden 
soll. Manche "Schriftsteller" schreiben aber so, daß es keiner lesen 
will/kann und sollten sich dann nicht wundern, wenn ihre Gedanken nicht 
verstanden werden.
Kurz: wer klare Programmiersprachen fordert, sollte keine "Dirk"-Texte 
schreiben.

: Bearbeitet durch User
von Karl (Gast)


Lesenswert?

Possetitjel schrieb:
> Das Zusammentreffen von...

Ja und? Ist doch in anderen Sprachen auch möglich. Das ist kein 
Alleinstellungsmerkmal.

Du glaubst nur, dass das nur in C geht, weil Du es nur in C kennst.

von (prx) A. K. (prx)


Lesenswert?

Die Syntax einer Programmiersprache prägt das Denken m.E. nicht so sehr. 
Das wird erkennbar, wenn man sich von Details löst und einen etwas 
breiteren Hintergrund sucht. Ob man Sonderzeichen schüttelt oder 
Keywords ist wenig relevant, solange das Paradigma der Sprache sich 
nicht ändert. Und da fallen imperative Sprachen wie C, Pascal und auch 
Fortran alle in die gleiche Kategorie.

Diese Ähnlichkeit von Sprachen, über deren Unterschiede sich viele Leute 
streiten wie die Kesselflicker, fällt besonders dann auf, wenn man in 
Sprachen reinschnuppert, die nicht dazu gehören. Ein krasser Fall aus 
der Vorzeit ist beispielsweise Prolog. Da kommt man mit prozeduralem 
Denken nicht weit, weil gibts nicht, der Quelltext beschreibt keine 
Abläufe. Erfahrungen mit imperativen Sprachen nützen hier nichts.

Wer es weniger krass und etwas aktueller haben will, der schaue sich 
funktionale Sprachen an.

Beitrag #5372737 wurde von einem Moderator gelöscht.
Beitrag #5372755 wurde von einem Moderator gelöscht.
von W.S. (Gast)


Lesenswert?

A. K. schrieb:
> Die Syntax einer Programmiersprache prägt das Denken m.E. nicht so sehr.
> Das wird erkennbar, wenn man..

..alles in einen so großen Rahmen stellt, daß sämtliche Details verloren 
gehen.

Nee, wie sehr gerade C das Denken und die Denkfähigkeit beeinträchtigt, 
kann man hier in diesem Forum sehr gut beobachten.

Das eigentlich Erstaunliche dabei ist, wie sehr sich gerade C 
ausgebreitet hat. Ausgerechnet diese Krücke, und das bei Anwesenheit von 
besseren Alternativen.

Jetzt könnte man darüber räsonnieren, ob es der Drang nach der Heldentat 
ist, der die Programmierer zu C getrieben hat, oder ob es der Wunsch 
war, etwas möglichst kryptisches zu betreiben, wo der Chef und alle 
anderen möglichst wie das berühmte Schwein ins Uhrwerk schauen. 
Schlichtweg der Drang nach Geheimniskrämerei zwecks persönlicher 
Vorteile.

W.S.

Beitrag #5372806 wurde von einem Moderator gelöscht.
von W.S. (Gast)


Lesenswert?

Possetitjel schrieb:
> Was ist upn?

97
3+
4*
ergibt 400

Zu jung, um einen HP-Taschenrechner gekannt zu haben?

Ich benutze sowas in leicht abgeänderter Weise gern für Steuersequenzen 
zwischen PC und µC per seriell oder USB, weil man damit atomare 
Operationen hat, die sich gut erweitern lassen und weil das Ganze im µC 
leicht dekodierbar ist. Zum Beispiel beim Wobbler:

3500000A1000S300N2W
Das wobbelt das 80M Band mit 1kHz Schritten 2x durch.

klaro?

W.S.

von (prx) A. K. (prx)


Lesenswert?

W.S. schrieb:
> ..alles in einen so großen Rahmen stellt, daß sämtliche Details verloren
> gehen.

Das Prinzip dahinter nennt sich Abstraktion. Liegt nicht jedem.

Mir ist es für die Denkweise in einer Sprache ziemlich egal, ob
 if (...) { ... }
oder
 if ... then ... endif
verwendet wird.

Zur Erinnerung: Ich schrieb ausdrücklich über die Syntax.

: Bearbeitet durch User
von Einer K. (Gast)


Lesenswert?

A. K. schrieb:
> if ... then ... endif

Das UPN Forth if geht in etwa so:
1
true / flag auf den Stack legen
2
3
if
4
  / tue dieses hier, wenn flag = true
5
else
6
  / tue dieses hier, wenn flag = false
7
then
8
/ hier gehts normal weiter
Hat auch seinen Reiz, finde ich....

von (prx) A. K. (prx)


Lesenswert?

Arduino F. schrieb:
> Hat auch seinen Reiz, finde ich....

Ist aber in diesem Fall genauso imperativ, sieht also nur anders aus, 
arbeitet aber gleich. Interessant wird es bei FORTH, wenn man die 
Dynamik der Sprache in Rechnung stellt, d.h. sich damit inkrementell 
eine eigene Sprache schafft.

Immerhin können FORTH Worte bereits zur Zeitpunkt der Übersetzung aktiv 
werden, nicht erst mit der Ausführung, was von Worten wie IF/THEN zur 
Umsetzung in den internen Code genutzt wird. Dann schafft sich das 
Programm quasi eine eigene anwendungsbezogene Syntax.

: Bearbeitet durch User
von Possetitjel (Gast)


Lesenswert?

W.S. schrieb:

> Possetitjel schrieb:
>> Was ist upn?
>
> 97
> 3+
> 4*
> ergibt 400

Klar. Kann mich nur nicht entsinnen, mal mit Karl über
"Umgekehrte polnische Notation" diskutiert zu haben;
seine Referenz auf "goto" dagegen war mir verständlich.


> Zu jung, um einen HP-Taschenrechner gekannt zu haben?

Nein -- auf der falschen Seite der Mauer gelebt.

von Einer K. (Gast)


Lesenswert?

A. K. schrieb:
> , d.h. sich damit inkrementell
> quasi eine eigene Sprache schafft.

In C muss man das Problem solange durchkauen, bis man es in der Sprache 
abhandeln kann.

In Forth wird die Sprache solange modifiziert, bis sie das Problem 
optimal widerspiegelt.

Meinst du das so, in etwa?

Beitrag #5372848 wurde von einem Moderator gelöscht.
von (prx) A. K. (prx)


Lesenswert?

Arduino F. schrieb:
> Meinst du das so, in etwa?

Schau dir beispielsweise mal an, wie Strings in FORTH übersetzt werden. 
Da steht beispielsweise
 ." Text"
im Quelltext. Aber es gibt keine zentral in einem Übersetzer fixierte 
Syntax dazu, denn das Word
 ."
wird bei der Übersetzung sofort ausgeführt, liest den Quelltext bis zum 
" ein, und macht daraus die interne Darstellung. Es wird also Teil des 
Übersetzungsprozesses.

Da der Quelltext hinter ." ausschliesslich vom Code in diesem ." 
bestimmt wird, und ein Programm beliebig andere solcher Worte definieren 
kann, kann also die Syntax des Quelltextes in hohem Mass von Programm 
selbst definiert werden.

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


Lesenswert?

W.S. schrieb:
> Das eigentlich Erstaunliche dabei ist, wie sehr sich gerade C
> ausgebreitet hat. Ausgerechnet diese Krücke, und das bei Anwesenheit von
> besseren Alternativen.

Dann zeige doch mal eine einzige, die auf µC, hardwarenah oder einfach 
(Beschreibung/Umsetzung/zuverlässigkeit) auch nur annähernd heranreicht. 
Bis dahin fürchte ich, hat Frau Merkel recht mit ihrem Alternativlos.


Ein gutes Argument wäre z.B., wenn die Sprache in ihrem Kern sich im 
Wesentlichen selber übersetzt, und nicht per C-Compiler Assembler-Code 
erzeugt.

von (prx) A. K. (prx)


Lesenswert?

Dirk B. schrieb im Beitrag #5372848:
> Programmierte die noch nicht so
> eine eigene Denkfähigkeit haben die Denkfähigkeit beeinträchtigt,

Zwar sind Parser natürlicher Sprachen wesentlich flexibler als die von 
Programmiersprachen, kommen aber trotzdem manchmal an ihre Grenzen.

von (prx) A. K. (prx)


Lesenswert?

Achim S. schrieb:
> Ein gutes Argument wäre z.B., wenn die Sprache in ihrem Kern sich im
> Wesentlichen selber übersetzt, und nicht per C-Compiler Assembler-Code
> erzeugt.

Womit wir wieder bei FORTH wären. Denn genau das geschieht da.

von Rolf M. (rmagnus)


Lesenswert?

Jobst Q. schrieb:
> Achim S. schrieb:
>> so ein Knausern an Zeilen oder Zeichen ist echt nur von 1 oder 2
>> Leerzeichen Einrückung zu toppen.
>
> Zwei Zeichen Einrückung ist doch optimal. Nicht zu übersehen, aber doch
> noch zu überblicken.
>
> Ich stell mir gerade Karls Treppenmonstercode mit 4 oder 8 Zeichen
> Einrückung vor. Gruselig. Da bräuchte man einen halbrunden Bildschirm
> und Fischaugen, um es zu überblicken.

Wenn du das Problem hast, sind deine Funktionen zu kompliziert. Wobei 8 
Zeichen wirklich übertrieben ist. Zwei sind mir aber dann doch zu wenig. 
Ich hab für mich 4 als Optimum gefunden.

Beitrag #5372885 wurde von einem Moderator gelöscht.
von Einer K. (Gast)


Lesenswert?

A. K. schrieb:
> Aber es gibt keine fixierte Syntax dazu,

Naja, es ist schon etwas vermessen, bei Forth von einer Syntax zu reden.

Mit fallen da nur wenige Regeln ein...
1. Forth Worte werden durch WhiteSpaces voneinander getrennt.
2. Es können nur Worte genutzt werden welche vorher definiert wurden.

Zu 2 gibt es die 1/2 Ausnahme, z.B. dass das Wort RECURSIVE, üblicher 
weise, das Attribut IMMEDIATE hat, also zur Kompilezeit ausgeführt wird. 
RECURSIVE kompiliert einen Aufruf auf das Wort, welches gerade 
kompiliert wird in den Code.

A. K. schrieb:
> Schau dir beispielsweise mal an, wie Strings in FORTH übersetzt
> werden.

." ist ein kompilierendes Wort.
Es hat auch das Attribut IMMEDIATE

Mein Decompiler sagt, dass ." bei mir so definiert ist:
1
: ."    COMPILE (.") ," ; IMMEDIATE


Mein Decompiler sagt zu if:
1
: IF    ?COMP HERE 2 CELLS - @ DUP lit COMPILE = SWAP lit LIT 
2
                = OR 0= HERE CELL - @ lit DUP = AND 
3
                IF      CELL NEGATE ALLOT COMPILE -?BRANCH 
4
                        
5
                ELSE    COMPILE ?BRANCH 
6
                THEN    >MARK 2 ; IMMEDIATE


Ich sage es gerne so....
Forth Worte haben Attribute:
1. Sie haben einen Namen (darüber werden sie im Vocabulary gefunden)
2. Sie haben ein Laufzeitverhalten
3. Sie haben ein Kompilezeitverhalten.
4. Sie tragen Werte/Parameter in sich

1 ist immer der Fall
2 bis 4 muss nicht unbedingt zum tragen kommen

-------

A. K. schrieb:
> Achim S. schrieb:
>> Ein gutes Argument wäre z.B., wenn die Sprache in ihrem Kern sich im
>> Wesentlichen selber übersetzt, und nicht per C-Compiler Assembler-Code
>> erzeugt.
>
> Womit wir wieder bei FORTH wären. Denn genau das geschieht da.
Sehe ich auch so!

von Jobst Q. (joquis)


Lesenswert?

Rolf M. schrieb:
> Wenn du das Problem hast, sind deine Funktionen zu kompliziert. Wobei 8
> Zeichen wirklich übertrieben ist. Zwei sind mir aber dann doch zu wenig.


Ich bin kein Freund von langen Treppen und weiß auch wie sie zu 
vermeiden sind. Siehe: 
Beitrag "Re: Art der if-Auswahl nur Geschmackssache?"

Aber manchmal hab ich längere Funktionen, die nur mit großem Aufwand 
aufzuteilen wären. In denen sind dann bis zu 6 Einrückungen, da wär mir 
mit 4 Zeichen Einrückung der Text schon manchmal unangenehm breit,zumal 
ich in dem Alter bin, wo man große Schriften bevorzugt.

> Ich hab für mich 4 als Optimum gefunden.
Ist ja auch ok.Ich will mein Optimum ja auch nicht anderen vorschreiben.

von Dirk B. (Gast)


Lesenswert?

Possetitjel schrieb:
>Warum wird dann -- teils erbittert -- über die Formatie-
>rung von Quelltexten gestritten?
eine plausible Erklärung wäre: weil es für Formatierungen keine (in 
Grenzen) keine entscheidbar besser/schlechter/richtig/falsch geben kann 
und somit jeder Recht hat und alle anderen die falsche Formatierung 
benutzen (ungefähr) und vor allem ist das Risiko erkennbar falsch zu 
liegen sicher 0.

Mein Beitrag mit dem Vorschlag 32 16bit zum Suchspiel zu nutzen wurde 
wohl völlig anders verstanden als es gemeint war (und ist wohl auch sehr 
unglücklich formuliert) aber wenn die Erbitterung es verbietet einfach 
den Autor in ganzen Sätzen anzuschreiben, dann fallen solche Fehler 
nicht auf und erzeugen wohl zig Geschwafel Lawinen.

von W.S. (Gast)


Lesenswert?

Dirk B. schrieb im Beitrag #5372848:
> In diesem Forum können...
..schwafel...schwafel...
> beeinträchtigt,

Mein lieber Junge, es kommt nicht drauf an, wieviele sinnlose Wörter man 
aneinanderreihen kann, um irgend etwas zu ignorieren.

Ich bevorzuge klare und prägnante Sätze.

Aber um sowas zu können, mußt du noch sehr üben.

Da fällt mir der gute Herbert ein (Wehner), über den sich mal ein 
Kabarettist etwa so ausgelassen hat:

"Meine Damen und Herren,

mir wird hier des Öfteren vorgeworfen, ich übte Polemik.

Nein.

ICH habe das nicht nötig.

Sie, meine Damen und Herren, müssen das noch üben."

W.S.

von W.S. (Gast)


Lesenswert?

Achim S. schrieb:
> Dann zeige doch mal eine einzige, die auf µC, hardwarenah oder einfach
> (Beschreibung/Umsetzung/zuverlässigkeit) auch nur annähernd heranreicht.

Dazu ist es so etwa 30 Jahre zu spät. Man hätte Pascal oder noch früher 
Algol nehmen können. Aber dafür ist es JETZT zu spät.

Vergleiche doch mal sowas:
1
#define FIOPIN (*((volatile unsigned long *) 0x3FFFC014))
2
3
var
4
 FIOPIN : dword absolute $3FFFC014;

Merkst du, was für eine elende Cast-Hampelei das in C ist?
Aber mir ist hier nicht danach, wieder das alte Gezänk 
heraufzubeschwören. Also merken wir uns eines: Daß nämlich 
Fehlentscheidungen irgendwann nicht mehr korrigierbar sind.

W.S.

von A. S. (Gast)


Lesenswert?

W.S. schrieb:
> Fehlentscheidungen

Ja, ne, is klar.

von Karl (Gast)


Lesenswert?

W.S. schrieb:
> Man hätte Pascal oder noch früher
> Algol nehmen können. Aber dafür ist es JETZT zu spät.

Warum? Mit Ada ging und geht das durchaus. Aber in Ada kann man halt 
nicht so schön wild rumcasten und rumpointern.

Und für Pascal gibt es durchaus brauchbare Compiler für Mikrocontroller.

Hab gerade ein komplettes Projekt für AVR von C nach Pascal 
transferiert. Das macht richtig Spass.

Und auf ARM und ARM Embedded läuft zur Zeit auch Einiges in Pascal. Das 
muss sich hinter C keineswegs verstecken.

Das Einzige, was die Leute an C bindet ist ihr eigener begrenzter 
Horizont.

von Karl (Gast)


Lesenswert?

Achim S. schrieb:
> Ja, ne, is klar.

Jap, oder wie mein Info-Prof zu sagen pflegte: Millionen Fliegen können 
nicht irren...

Beitrag #5375036 wurde von einem Moderator gelöscht.
Beitrag #5375047 wurde von einem Moderator gelöscht.
von W.S. (Gast)


Lesenswert?

Karl schrieb:
> Und für Pascal gibt es durchaus brauchbare Compiler für Mikrocontroller.

Meinst du Mikroe oder was anderes?

W.S.

Beitrag #5375161 wurde von einem Moderator gelöscht.
Beitrag #5375167 wurde von einem Moderator gelöscht.
von Teo D. (teoderix)


Lesenswert?

Könnt Ihr Bitte damit aufhören, Danke!
Ich dachte nämlich, ich lerne hier noch ein paar Eigenheiten von C 
kennen. :(

von Dirk B. (Gast)


Lesenswert?

Teo D. schrieb:
> Ich dachte nämlich, ich lerne hier noch ein paar Eigenheiten von C
> kennen. :(
Die paar Eigenheiten von C die du schon kennst wären da für einige 
Menschen eine  interessante Information, da C häufig zur Programmierung 
genutzt wird und etwas mit eigenen Eigenheiten sich nicht dafür eignet.

von Karl (Gast)


Lesenswert?

Teo D. schrieb:
> Ich dachte nämlich, ich lerne hier noch ein paar Eigenheiten von C
> kennen.

Du meinst so lustige Sachen wie:
1
char c1;
2
c1 = ~0x80;
3
if (c1 == ~0x80)
4
// True oder False?

Na, wie gehts nach dem if weiter? Ist der Vergleich True oder False?

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Karl schrieb:
> Du meinst so lustige Sachen wie:

Du meinst so falsche Sachen wie...

Karl schrieb:
> Na, wie gehts nach dem if weiter? Ist der Vergleich True oder False?

Die Frage stellt sich nicht, eben weil der Code fehlerhaft ist. Und 
jeder Compiler wird dir das sagen. Hör doch auf mit den konstruierten 
beispielen...

von Karl (Gast)


Lesenswert?

Michael R. schrieb:
> Die Frage stellt sich nicht, eben weil der Code fehlerhaft ist. Und
> jeder Compiler wird dir das sagen.

Der Compiler-Explorer meldet keinen Fehler, der bringt nur eine Warnung. 
Und witzigerweise bringt der die Warnung nur bei char, uint8_t oder 
int8_t. Und nur bei der Zuweisung, nicht beim Vergleich.

int16_t c2; c2 = ~0x8000; wird klaglos akzeptiert, obwohl es genauso 
einen Overflow erzeugen würde.

> Hör doch auf mit den konstruierten
> beispielen...

Wenn Du C auf AVR programmieren würdest, wüsstest Du, dass eine 
Zuweisung mit ~(1 << 7) zum Setzen aller Bits ausser Bit 7, oder zum 
Löschen von Bit 7 per AND völlig normal und gängige Praxis ist. Und 
genau das ist ~0x80.

von Sepp (Gast)


Lesenswert?

Karl schrieb:
> Der Compiler-Explorer meldet keinen Fehler, der bringt nur eine Warnung.
> Und witzigerweise bringt der die Warnung nur bei char, uint8_t oder
> int8_t. Und nur bei der Zuweisung, nicht beim Vergleich.
>
> int16_t c2; c2 = ~0x8000; wird klaglos akzeptiert, obwohl es genauso
> einen Overflow erzeugen würde.

Das was du Zuweisung nennst ist eine Operation mit nachfolgender 
Zuweisung. Da du den type von 0x80 nicht definiert hast nimmt der 
Compiler einfach eine Standardeinstellung her (in deinem Fall uint16_t).

Somit ist dein ~0x80 nicht 0x7F sondern 0xFF7F, und das parst der 
compiler dir nicht sauber in uint8_t, deswegen die Warnung.

Karl schrieb:
> Wenn Du C auf AVR programmieren würdest, wüsstest Du, dass eine
> Zuweisung mit ~(1 << 7) zum Setzen aller Bits ausser Bit 7, oder zum
> Löschen von Bit 7 per AND völlig normal und gängige Praxis ist. Und
> genau das ist ~0x80.

Dein Bitshift wird anders behandelt als die Zahl, der bekommt vom 
compiler den minimal möglichen Datentyp, und das ist nun mal uint8_t in 
deinem Fall, würdest du um 9 bits shiften, dann wäre es ein uint16_t.

von Karl (Gast)


Lesenswert?

Sepp schrieb:
> Das was du Zuweisung nennst ist eine Operation mit nachfolgender
> Zuweisung.

Ach,
  ldi r24,lo8(127)
  std Y+1,r24
ist keine Zuweisung?

Sepp schrieb:
> Dein Bitshift wird anders behandelt als die Zahl, der bekommt vom
> compiler den minimal möglichen Datentyp

Wird er nicht: c1 = ~(1<<7); liefert genau die gleiche Warnung und im 
Compilat genau das gleiche falsche Ergebnis.

Bei
1
  uint16_t c1;
2
  c1 = ~(1<<15);
3
  if (c1 == ~(1<<15)) {
4
    is_true();
5
  } else {
6
    is_false(); 
7
  }
8
  uint8_t c2;
9
  c2 = ~(1<<7);
10
  if (c2 == ~(1<<7)) {
11
    is_true();
12
  } else {
13
    is_false(); 
14
  }

im GCC liefert der erste Fall true, der zweite Fall false.

von Sepp (Gast)


Lesenswert?

Karl schrieb:
> Ach,
>   ldi r24,lo8(127)
>   std Y+1,r24
> ist keine Zuweisung?

>lo8(x)
ist die Operation (das Parsen) vor der Zuweisung und entspricht 
((x)&0xff)
>lo8(127)
zeigt aber, dass das nicht alles ist, denn
>~0x80
ergibt in dem von dir erwähnten Fall nie
>127
sondern 65407.

Ich denke du mischt 8 und 16-Bit AVRs.
Denn den Fehler bekommst du so bei einem 16bit AVR und dein Compilat in 
der Form bei einem 8bit AVR.

Karl schrieb:
> Wenn Du C auf AVR programmieren würdest, wüsstest Du, dass eine
> Zuweisung mit ~(1 << 7) zum Setzen aller Bits ausser Bit 7, oder zum
> Löschen von Bit 7 per AND völlig normal und gängige Praxis ist. Und
> genau das ist ~0x80.

Karl schrieb:
> Wird er nicht: c1 = ~(1<<7); liefert genau die gleiche Warnung und im
> Compilat genau das gleiche falsche Ergebnis.

Also ist ~(1<<7) doch nicht ~0x80 wie vorher behauptet?

von (prx) A. K. (prx)


Lesenswert?

Sepp schrieb:
> Dein Bitshift wird anders behandelt als die Zahl

Nein. (1<<7) ist vom Type her stets identisch mit 0x80. Aber (1<<15) ist 
nicht notwendigerweise identisch mit 0x8000.

Sepp schrieb:
> Also ist ~(1<<7) doch nicht ~0x80 wie vorher behauptet?

Doch. Aber (int)(uint8_t)~0x80 ist nicht gleich ~0x80.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Karl schrieb:
> Der Compiler-Explorer meldet keinen Fehler, der bringt nur eine Warnung.

Der Code ist ja auch nicht illegal, sondern höchstens unsinnig.

> Und witzigerweise bringt der die Warnung nur bei char, uint8_t oder
> int8_t. Und nur bei der Zuweisung, nicht beim Vergleich.

Der GCC meckert mit -Wextra auch den Vergleich an:

1
 warning: comparison is always false due to limited range of data type [-Wtype-limits]

> int16_t c2; c2 = ~0x8000; wird klaglos akzeptiert, obwohl es genauso
> einen Overflow erzeugen würde.

Auf dem PC liefert der GCC dabei die gleichen Warnungen wie oben. Auf
dem AVR natürlich nicht, weil dort int16_t = int ist und somit keine
Konvertierung erfolgt, die einen Überlauf auslösen könnte.

von Karl (Gast)


Lesenswert?

Yalu X. schrieb:
> Auf dem PC liefert der GCC dabei die gleichen Warnungen wie oben. Auf
> dem AVR natürlich nicht, weil dort int16_t = int ist und somit keine
> Konvertierung erfolgt, die einen Überlauf auslösen könnte.

Ja, der gleiche Schmonz funktioniert aber auch auf 32bit CPUs mit int32 
vs. int16, oder auf 64bit CPUs. Das ist kein AVR-spezifisches Problem.

Yalu X. schrieb:
> Der Code ist ja auch nicht illegal, sondern höchstens unsinnig.

Warum ist es unsinnig, um beim AVR-Beispiel zu bleiben, Bits auf diese 
Weise zu selektieren, oder auf ein Bitmuster zu prüfen?

Der Code ist hier vereinfacht, aber c1 könnte seine Daten aus einem 
low-aktiven Tastenfeld haben und dann mit ~auf gedrückte Tasten prüfen. 
Die Werte könnten auch per #define irgendwo anders festgelegt werden.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Karl schrieb:
> Yalu X. schrieb:
>> Der Code ist ja auch nicht illegal, sondern höchstens unsinnig.
>
> Warum ist es unsinnig, um beim AVR-Beispiel zu bleiben, Bits auf diese
> Weise zu selektieren, oder auf ein Bitmuster zu prüfen?

Ich bezog mich auf das da:

Karl schrieb:
> char c1;
> c1 = ~0x80;
> if (c1 == ~0x80)
> // True oder False?

> Der Code ist hier vereinfacht, aber c1 könnte seine Daten aus einem
> low-aktiven Tastenfeld haben und dann mit ~auf gedrückte Tasten prüfen.

Ok, aber was erwartest du bei einem Vergleich zwischen zwei verschieden
breiten Integertypen?

Es gibt im Wesentlichen drei Möglichkeiten, mit diesem Fall umzugehen:

1. Einer oder beide der beiden Typen werden zu einem gemeinsamen Typ
   erweitert, so dass sie vergleichbar werden. Im konkreten Fall wird
   das char unter Beibehaltung des numerischen Werts zu einem int
   erweitert. So wird das in C bei der impliziten Typkonvertierung
   gehandhabt.

2. Einer oder beide der beiden Typen werden zu einem gemeinsamen Typ
   beschnitten, so dass sie vergleichbar werden. So hättest du es in
   deinem Beispiel wohl gerne. Im konkreten Fall c1==~0x80 würde ~0x80
   in ein char konvertiert, wobei sich der Wert von -129 in +127 ändert
   und der Vergleich deswegen wahr wird, da c1 ebenfalls gleich +127
   ist. Implizites Beschneiden und damit u.U. verbundene Änderungen des
   Werts ist aber in den allermeisten Fällen unerwünscht, da es eine
   Fehlerquelle bei numerischen Berechnungen darstellt.

3. Der Vergleich wird wegen der verschiedenen Datentypen gar nicht erst
   zugelassen. Im konkreten Fall würde der Compiler mit einem Fehler
   abbrechen, weil char und int nicht vergleichbar sind. Das wäre die
   sauberste Alternative, es gibt aber nur wenige Programmiersprachen,
   deren Typprüfung so streng ist (z.B. Haskell).

Man muss sich in C (wie auch in neueren Pascals und vielen anderen
Sprachen) bewusst sein, dass implizite Typkonvertierungen manchmal zu
Problemen führen. Solche Probleme lassen sich im Zweifelsfall meist
durch explizite Typkonvertierungen umgehen. Schreibt man im konkreten
Fall c1==(char)~0x80 statt c1==~0x80, ist das Ergebnis das von dir
erwartete.

von Rolf M. (rmagnus)


Lesenswert?

Yalu X. schrieb:
> 3. Der Vergleich wird wegen der verschiedenen Datentypen gar nicht erst
>    zugelassen. Im konkreten Fall würde der Compiler mit einem Fehler
>    abbrechen, weil char und int nicht vergleichbar sind. Das wäre die
>    sauberste Alternative, es gibt aber nur wenige Programmiersprachen,
>    deren Typprüfung so streng ist (z.B. Haskell).

Und C++ seit der 2011er Version, wenn man die Initialisierung mit {} 
nutzt.
Da sagt z.B. der gcc bei diesem Programm
1
int main()
2
{
3
    char c { ~0x80 };
4
}
folgendes:
1
0x80.cpp: In function ‘int main()’:
2
0x80.cpp:3:20: error: narrowing conversion of ‘-129’ from ‘int’ to ‘char’ inside { } [-Wnarrowing]

von Karl (Gast)


Lesenswert?

Yalu X. schrieb:
> Ok, aber was erwartest du bei einem Vergleich zwischen zwei verschieden
> breiten Integertypen?

Sowas?
1
# [47] c1 := not($80);
2
  ldi  r18,127
3
# [48] if c1 = not($80) then begin
4
  cpi  r18,127
5
  brne  .Lj16

Die Typen sind nicht verschieden breit. Die sind genau ein Byte. Und ein 
Not eines Bytes ist wiederum genau ein Byte. Ein Byte wird durch ein Not 
nicht zu zwei Byte.

Das ist nur eine Eigenheit von C, welches erstmal versucht alles in 16 
Bit zu quetschen.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Rolf M. schrieb:
> Und C++ seit der 2011er Version, wenn man die Initialisierung mit {}
> nutzt.
> Da sagt z.B. der gcc bei diesem Programmint main()
> {
>     char c { ~0x80 };
> }
> folgendes:0x80.cpp: In function ‘int main()’:
> 0x80.cpp:3:20: error: narrowing conversion of ‘-129’ from ‘int’ to
> ‘char’ inside { } [-Wnarrowing]

Ähnliches sagt ja auch der C-Compiler bei der Initialisierung, nur dass
der lediglich eine Warnung ausspricht.

Karl schrieb:
> Yalu X. schrieb:
>> Ok, aber was erwartest du bei einem Vergleich zwischen zwei verschieden
>> breiten Integertypen?
>
> Sowas?# [47] c1 := not($80);
>   ldi  r18,127
> # [48] if c1 = not($80) then begin
>   cpi  r18,127
>   brne  .Lj16

Das sieht nach Pascal aus, aber welcher Dialekt bzw.Compiler? (W.S. hat
diese Frage weiter oben auch schon gestellt)

Da gibt es nämlich große Unterschiede. Mit deinem Compiler wird der
Ausdruck not($80) offensichtlich zu 127, mit Free Pascal (PC-Version)
aber zu -129 ausgewertet. Dort würde der Vergleich false liefern.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Karl schrieb:
> Die Typen sind nicht verschieden breit. Die sind genau ein Byte. Und ein
> Not eines Bytes ist wiederum genau ein Byte. Ein Byte wird durch ein Not
> nicht zu zwei Byte.

wo steht das 0x80 ein Byte ist? Ist es nämlich nicht, es ist ein Integer 
(und ein integer heißt nicht 16 bit)

aber selbst wenn du aus 0x80 ein Byte machst, ist das "not" davon wieder 
ein Integer. Blöd, eh?

> Das ist nur eine Eigenheit von C, welches erstmal versucht alles in 16
> Bit zu quetschen.

16 Bit ist falsch, Integer wäre richtig.

Für dich mag es eine Eigenheit sein, C-Programmierer haben das Kapitel 
"Integral Promotion" in ihrem C-Buch gelesen ;-)

von Bär Luskoni (Gast)


Lesenswert?

Michael R. schrieb:

> Für dich mag es eine Eigenheit sein, C-Programmierer haben das Kapitel
> "Integral Promotion" in ihrem C-Buch gelesen ;-)

Das nützt aber Nichts, wenn in dem "guten C-Buch" des Anderen wieder 
etwas Anderes steht. Eine Sprache, bei der eine 50:50 Chance besteht, 
daß das richtige Ergebnis einer Operation erzielt wird...
Ich weiß nicht so recht...

von Einer K. (Gast)


Lesenswert?

Bär Luskoni schrieb:
> Ich weiß nicht so recht...

Aber ich!

Denn:
Den individuellen Irrtum eines Buchautors, einer Sprache anzulasten, ist 
mindestens ungerecht.

von Jobst Q. (joquis)


Lesenswert?

Karl schrieb:
> Yalu X. schrieb:
>> Der Code ist ja auch nicht illegal, sondern höchstens unsinnig.
>
> Warum ist es unsinnig, um beim AVR-Beispiel zu bleiben, Bits auf diese
> Weise zu selektieren, oder auf ein Bitmuster zu prüfen?

Zahlen vergleicht man mit == ,Bitmuster prüft man mit &.

if (! (c & 0x80))...

gibt immer das richtige Ergebnis,unabhängig von der Bitbreite der 
Variablen und der Implementierung.

von ADA_ULTIMA (Gast)


Lesenswert?

Ja,
die Geschmäcker unterscheiden sich.

Ich mag diese Geschmacksrichtung (auf PC, rPI, STM32, ..):
1
package Energie is
2
   type Restzeit_Type is new Integer range 0 .. 1234;
3
   Energie_Schwelle_Oben  : Restzeit_Type := 100;
4
   Energie_Schwelle_Unten : Restzeit_Type :=  10;
5
6
   procedure Led_Energie_Gruen_On;
7
   procedure Led_Energie_Rot_On;
8
   procedure Led_Energie_Gelb_On;
9
10
   procedure Energie_Rest_Ampel (Restzeit : Restzeit_Type);
11
end Energie;
12
13
package body Energie is
14
   procedure Led_Energie_Gruen_On is
15
   begin
16
      null;
17
   end;
18
19
20
   procedure Energie_Rest_Ampel (Restzeit : Restzeit_Type) is
21
   begin
22
      case Restzeit is
23
         when Energie_Schwelle_Oben .. Restzeit_Type'Last =>
24
            Led_Energie_Gruen_On;
25
         when Restzeit_Type'First .. Energie_Schwelle_Oben =>
26
            Led_Energie_Rot_On;
27
         when others =>
28
            Led_Energie_Gelb_On;
29
      end case;
30
   end Energie_Rest_Ampel;
31
end Energie;


oder so ähnlich..

von Karl (Gast)


Lesenswert?

Michael R. schrieb:
> aber selbst wenn du aus 0x80 ein Byte machst, ist das "not" davon wieder
> ein Integer. Blöd, eh?

Ach, wirklich? https://en.wikipedia.org/wiki/Bitwise_operation#NOT

Jobst Q. schrieb:
> Zahlen vergleicht man mit == ,Bitmuster prüft man mit &.
> if (! (c & 0x80))...

Autsch!
1
  c1 = 0x81;
2
  if (c1 & 0x80) {
3
    is_true();
4
  } else {
5
    is_false(); 
6
  }

Das Bitmuster 0x81 ist also gleich dem Bitmuster 0x80?

Da muss man sich nicht wundern, dass Software in C so schlecht ist, wenn 
C-Programmierer nicht mal die einfachsten Vergleiche hinbekommen.

Leute, ist es denn echt so schwer einfach zuzugeben: Ja, der C-Compiler 
baut hier Scheisse, das ist leider so, weil das irgendwann mal so 
angefangen wurde und wir das jetzt wegen der Abwärtskombatibilität so 
beibehalten müssen.

Stattdessen Verrenkungen um Verrenkungen, um zu erklären, warum die 
Scheisse in C jetzt doch die Sachertorte ist.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Karl schrieb:
>> aber selbst wenn du aus 0x80 ein Byte machst, ist das "not" davon wieder
>> ein Integer. Blöd, eh?
>
> Ach, wirklich? https://en.wikipedia.org/wiki/Bitwise_operation#NOT

Schön, dass du dir die Mühe gemacht hast, 'Integral Promotion' zu 
recherchieren, und versucht hast, das auch nur ansatzweise zu verstehen 
;-)

Karl schrieb:
> Ja, der C-Compiler
> baut hier Scheisse, das ist leider so, weil das irgendwann mal so
> angefangen wurde und wir das jetzt wegen der Abwärtskombatibilität so
> beibehalten müssen.

Wenn du die Promotion verstanden hättest, wüsstest du auch wo deren 
Vorteile liegen, dass das weder Sch**sse noch Abwärtskompatibilität ist.

So aber wirst du wohl bei deiner $FavoriteProgrammingLanguage (welche 
eigentlich?) bleiben müssen... das ist aber ok, damit hat niemand ein 
Problem, eher im Gegenteil ^^

von Karl (Gast)


Lesenswert?

Michael R. schrieb:
> 'Integral Promotion'

Scheisse wird nicht dadurch schmackhafter, dass man ihr einen tollen 
Namen gibt.

von Jobst Q. (joquis)


Lesenswert?

Karl schrieb:
> Autsch!
>   c1 = 0x81;
>   if (c1 & 0x80) {
>     is_true();
>   } else {
>     is_false();
>   }
> Das Bitmuster 0x81 ist also gleich dem Bitmuster 0x80?

if (c1 & 0x80) fragt ab, ob Bit7 gesetzt ist. Das ist bei c = 0x81 der 
Fall. Also richtig.

Wenn du abfragen willst ob Bit7 und Bit0 gesetzt ist, geht das so:

 if ((c1 & 0x81)==0x81)...

Deinen Mangel an Kenntnissen von Bitoperationen kannst du nicht C 
anlasten.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Karl schrieb:
> Scheisse wird nicht dadurch schmackhafter, dass man ihr einen tollen
> Namen gibt.

Hmmm... ich finde Gabelstapler total Scheisse. Ich kann mit denen 
überhaupt nicht umgehen...

Jobst Q. schrieb:
> Deinen Mangel an Kenntnissen von Bitoperationen kannst du nicht C
> anlasten.

Das gilt aber definitiv nicht für Gabelstapler ;-)

von Karl (Gast)


Lesenswert?

Jobst Q. schrieb:
> if (c1 & 0x80) fragt ab, ob Bit7 gesetzt ist. Das ist bei c = 0x81 der
> Fall. Also richtig.

Das ist nicht das, was Du oben behauptet hast.

von Jobst Q. (joquis)


Lesenswert?

Karl schrieb:
> Jobst Q. schrieb:
>> if (c1 & 0x80) fragt ab, ob Bit7 gesetzt ist. Das ist bei c = 0x81 der
>> Fall. Also richtig.
>
> Das ist nicht das, was Du oben behauptet hast.

"Bitmuster prüft man mit &." hatte ich gesagt. Und 0x80 ist nunmal als 
Bitmuster in 0x81 enthalten.

von Rolf M. (rmagnus)


Lesenswert?

Karl schrieb:
> Michael R. schrieb:
>> aber selbst wenn du aus 0x80 ein Byte machst, ist das "not" davon wieder
>> ein Integer. Blöd, eh?
>
> Ach, wirklich?

Ja, wirklich.

> https://en.wikipedia.org/wiki/Bitwise_operation#NOT

Schön. Du hast rausgefunden, wie eine boolesche Invertierung 
funtkioniert. Was hat das jetzt damit zu tun, von welchem Datentyp die 
Konstante 0x80 und damit auch ~0x80 in C ist?

> Da muss man sich nicht wundern, dass Software in C so schlecht ist, wenn
> C-Programmierer nicht mal die einfachsten Vergleiche hinbekommen.

Schließe doch bitte nicht von dir auf andere.

von Jobst Q. (joquis)


Lesenswert?

Karl schrieb:
> if (c1 == ~0x80)
> // True oder False?

Weder noch, sondern einfach Unsinn. Die einzig sinnvolle Verwendung des 
bitweisen NOT '~' ist das Zurücksetzen von Bits zusammen mit dem 
bitweisen AND '&'.

flags &= (~0x80);

Dabei ist es nicht von Nachteil, wenn die vom Compiler gewählte Größe 
der '~'-Konstruktion größer ist als die betroffene Variable. Alle 
zusätzlichen Bits sind gesetzt, haben also mit dem '&' keine Wirkung.

von Karl (Gast)


Lesenswert?

Rolf M. schrieb:
> Was hat das jetzt damit zu tun, von welchem Datentyp die
> Konstante 0x80 und damit auch ~0x80 in C ist?

Die Konstante hat sinnvoller Weise die Länge, die angegeben ist. 0x80 
sind ein Byte, 0x0080 sind 2 Byte. Gerade bei Hex ist das doch total 
simple und seit Jahrhunderten bewährt.

Ich sag doch: Die Crux bei C ist, dass es erstmal alles auf seine 
mindestens 16 Bit presst, auch wenn das für 8-Bit-Systeme nicht sinnvoll 
ist.

Jobst Q. schrieb:
> Die einzig sinnvolle Verwendung des
> bitweisen NOT '~'...

... die Du Dir vorstellen kannst. Für Deinen beschränkten Horizont kann 
ja sonst keiner was.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Karl schrieb:
> Die Konstante hat sinnvoller Weise die Länge, die angegeben ist. 0x80
> sind ein Byte, 0x0080 sind 2 Byte. Gerade bei Hex ist das doch total
> simple und seit Jahrhunderten bewährt.

Ach ist das süß ;-) Hex ist also ein Sonderfall... wie macht man das 
dezimal, oktal?

> [...] dass es erstmal alles auf seine mindestens 16 Bit presst [...]

ah, mindestens... er lernt^^

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Karl schrieb:
> Die Konstante hat sinnvoller Weise die Länge, die angegeben ist. 0x80
> sind ein Byte, 0x0080 sind 2 Byte.

In C hängt die Länge vom Wert und von der Basis ab.

Michael R. schrieb:
> Ach ist das süß ;-) Hex ist also ein Sonderfall... wie macht man das
> dezimal, oktal?

Stimmt aber. Der Typ einer lexikalischen Konstanten hängt zwar vom Wert 
ab, nicht von der Länge im Quelltext, aber das bestimmt sich oktal/hex 
tatsächlich anders als dezimal. Bei dezimaler Angabe werden die 
vorzeichenlosen Typen übergangen, weshalb 32768 in einer 16-Bit Umgebung 
"long" ist, 0x8000 aber "unsigned".

: Bearbeitet durch User
von Einer K. (Gast)


Lesenswert?

Ach, wenn unser Karl sich wenigstens darüber beschweren würde, dass ein 
Byte auch mal 9 Bit breit sein darf, dann hätte ich ja halbes 
Verständnis.

Aber so....
Denn das Verhalten von C ist eigentlich recht klar definiert.
Sicher kann man diese Definitionen in Zweifel ziehen, oder sich fragen 
ob sie geschickt definiert sind.

Es macht allerdings keinen Sinn darüber zu schimpfen, denn die 
Definition kann man nicht ändern. Das ist, wie darüber jammern, dass man 
nass wird, wenn es regnet.


Man/Karl könnte sich eine andere Sprache suchen, wenn man/Karl C nicht 
mag. Aber deswegen anderen die Sprache madig machen...?
Neee...

Ich selber bin auch kein C Fan.
Habs eher mit OOP, also wenn, dann C++ auf meinen AVR Zwergen.

von Karl (Gast)


Lesenswert?

Michael R. schrieb:
> Hex ist also ein Sonderfall... wie macht man das
> dezimal, oktal?

Naja, oktal in C ist ja noch bescheuerter: 010 ist ein Oktalzahl. Was 
haben die geraucht, um auf sowas zu kommen?

Michael R. schrieb:
> ah, mindestens...

Falls Du damit wieder auf die vergötterte Integer-Promotion ansprichst: 
Wenn ein NOT aus einem Byte zwei Byte macht, ist das keine 
Integer-Promotion, sondern einfach nur bescheuert. Das Ergebnis von 
einem NOT auf ein Byte ist wiederum nur ein Byte. Genauso wie das 
Ergebnis ein Byte AND ein Byte wieder nur ein Byte ist. Bitweise 
Operatoren. Wie soll sich da die Anzahl der Bits erhöhen?

Nein, was Du für Integer Promotion hältst ist einfach nur die Eigenheit, 
erstmal alles in 16 Bit zu pressen, weil 16 Bit damals(TM) halt hipp 
waren. Das ist schlichtweg ein Designfehler von C, den man immer weiter 
mitschleifen muss.

von Einer K. (Gast)


Lesenswert?

> Kritik an Anderen schützt vor eigener Leistung nicht!

Zeige mir bitte deinen eigenen "besseren" Kompiler.

von Karl (Gast)


Lesenswert?

Arduino F. schrieb:
> Man/Karl könnte sich eine andere Sprache suchen, wenn man/Karl C nicht
> mag. Aber deswegen anderen die Sprache madig machen...?

Done!

Allerdings ist es im Gegenteil eher so, dass die C-Fans anderen "ihre" 
Sprachen madig machen wollen, weil C und dessen Derivate das einzig 
Wahre sind und man nur in C richtig programmieren könne.

Witzig zu sehen ist allerdings, wie krampfhaft die "Eigenheiten" sprich 
Designfehler von C verteidigt werden, anstatt einfach mal zuzugeben: Ja 
ok, das war damals eine Fehlentscheidung, mit der wir heute leider leben 
müssen.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Karl schrieb:
> Nein, was Du für Integer Promotion hältst ist einfach nur die Eigenheit,
> erstmal alles in 16 Bit zu pressen, weil 16 Bit damals(TM) halt hipp
> waren. Das ist schlichtweg ein Designfehler von C, den man immer weiter
> mitschleifen muss.

wir müssen damals im selben Kommunikationstraining gewesen sein... 
"Blamiere dich täglich" war der Wahlspruch, aber du übertreibst es 
etwas... dabei warst du schon beim "mindestens".

es wird mitnichten auf 16 bit, sondern auf die native Wortbreite (aber 
mindestens 16 bit) erweitert. Und das nicht weil es hipp ist oder war, 
sondern weil es in der Mehrzahl der Fälle sinnvoll ist. Deswegen ist es 
auch kein Designfehler, sondern wurde absichtlich so definiert, weil es 
eben Vorteile bietet. Das damit ein paar Fallstricke für Anfänger 
verbunden sind, damit hast du recht. Aber C hatte (im Gegensatz zu zB 
Pascal) niemals den Anspruch, eine Anfänger-Sprache zu sein.

von (prx) A. K. (prx)


Lesenswert?

Karl schrieb:
> Naja, oktal in C ist ja noch bescheuerter: 010 ist ein Oktalzahl. Was
> haben die geraucht, um auf sowas zu kommen?

Oktale Darstellung war in der Phase ziemlich gebräuchlich, in der C 
erfunden wurde. Wenn man die binäre Codierung der Befehle der PDP11 und 
anderer Maschinen dieser Ära ansieht, dann weiss man auch warum.

> Falls Du damit wieder auf die vergötterte Integer-Promotion ansprichst:
> Wenn ein NOT aus einem Byte zwei Byte macht,

Tut es nicht. ~0x80 macht aus einer "int" eine "int", denn 0x80 ist 
schon eine "int". ~ ändert also nicht den Typ. Auch in
  uint8_t c = 0x80;
  ... ~c ...
erweitert nicht speziell der ~ Operator die variable "c" auf "int", 
sondern der Umstand, dass "c" im Kontext einer Expression verwendet 
wird. Aber auch da wird "c" vor der Berechung erweitert, also nicht 
durch den Operator.

> Nein, was Du für Integer Promotion hältst ist einfach nur die Eigenheit,
> erstmal alles in 16 Bit zu pressen,

Da wird nichts gepresst. Konstanten, die nicht in 16 Bits passen, werden 
nicht reduziert. Der Typ passt sich an, mindestens aber "int", was per 
Definition mindestens 16 Bits sein muss.

C ist definitiv nicht die bestmögliche aller Welten. Muss man nicht 
mögen, ist aber so und wird so verwendet. Deutsch ist auch nicht die 
einfachste aller Sprachen, wird aber von uns trotzdem verwendet.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Michael R. schrieb:
> KaUnd das nicht weil es hipp ist oder war,
> sondern weil es in der Mehrzahl der Fälle sinnvoll ist.

Es war damals nicht unüblich, Bytes seitens der Maschine automatisch zu 
einem Wort zu erweitern, wenn sie aus dem Speicher geladen wurden - mal 
vorausgesetzt, es gab überhaupt Byteadressierung. So auch die PDP11, die 
in der Entstehungsphase eine nicht unmassgebliche Rolle spielte.

von Jobst Q. (joquis)


Lesenswert?

Karl schrieb:
> Jobst Q. schrieb:
>> Die einzig sinnvolle Verwendung des
>> bitweisen NOT '~'...
>
> ... die Du Dir vorstellen kannst. Für Deinen beschränkten Horizont kann
> ja sonst keiner was.

Natürlich kann ich mir mehr vorstellen, aber eben nichts sinnvolles. 
Invertieren kann man auch mit XOR '^', sogar ganz beliebige Bits.

c^=0xFF;

Invertiert genau ein Byte, ohne dass die Typlänge eine Rolle spielt.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

A. K. schrieb:
> C ist definitiv nicht die bestmögliche aller Welten. Muss man nicht
> mögen, ist aber so und wird so verwendet.

Ein wahres Wort!

Um nochmal bei Karl zu bleiben: Karl mag C nicht. Ich mag Python nicht. 
Karl wird seine Gründe haben, zwischen mir und Python steht (vor allem) 
die off-side rule (Einrückung). Damit kann ich nicht.

Ich würde aber nie so weit gehen, das als Design-Fehler zu bezeichnen. 
Es ist nur ein (legitimes) Design welches mir nicht gefällt.

Weil ich Python nicht mag, verwende ich es nicht. Weil ich es nicht 
verwende, weiß ich nichts (oder nur sehr wenig) drüber. Weil ich nichts 
drüber weiß, schreibe ich nicht in Python-Threads mit (und schon gar 
nicht mokiere ich mich dort über Design-Fehler).

Bei Karl scheint da irgendwas anders zu sein ;-)

: Bearbeitet durch User
von Karl (Gast)


Lesenswert?

Nun, ich mag Python auch nicht, genau aus genanntem Grund.

Im Gegensatz zum Einrücken bei Python, welches offensichtlich ist, ist 
obiger Fehler in C eben nicht offensichtlich.

Der Compiler macht hier etwas, was zu einer der Intention - nicht zu 
Verwechseln mit Indention - des Codes widersprechenden Aussage führt, 
aufgrund einer Typwandlung, die im Verborgenen erfolgt.

Da weiter oben der FPC erwähnt wurde: Der FPC auf dem PC kompiliert das 
gar nicht erst, man müsste ihn durch einen expliziten Typecast dazu 
zwingen. Und der FPC für Embedded AVR kompiliert das plattformkonform 
mit 8 Bit, und das Ergebnis ist korrekt.

Geht also durchaus.

von Einer K. (Gast)


Lesenswert?

Mutter Teresa sagte mal:
> Wenn du die Menschen verurteilst,
> hast du keine Zeit, sie zu lieben.

Das gilt natürlich auch, im übertragenen sinne, für vieles andere.
z.B. für Computersprachen.
(auch wenn sie das nicht direkt damit gemeint haben sollte)

von Yalu X. (yalu) (Moderator)


Lesenswert?

Michael R. schrieb:
> Karl schrieb:
>> Nein, was Du für Integer Promotion hältst ist einfach nur die Eigenheit,
>> erstmal alles in 16 Bit zu pressen, ...
>
> ...
> es wird mitnichten auf 16 bit, sondern auf die native Wortbreite (aber
> mindestens 16 bit) erweitert.

Genau dieses "aber mindestens 16 bit" ist jedoch ein Punkt, wo ich Karl
teilweise recht gebe.

Der ursprüngliche Grund, die Größe von int und damit den defaultmäßigen
Wertebereich für Integer-Berechnungen an die Wortbreite des verwendeten
Prozessors anzugleichen, liegt schlicht im Wunsch nach maximaler
Performanz. Von diesem Standpunkt aus gesehen müsste bei einem
AVR-C-Compiler int 8 bit breit sein, denn ein AVR rechnet in 8-Bit-
Arithmetik deutlich schneller als in 16 Bit.

Der Grund, warum für int trotzdem mindestens 16 Bit vorgeschrieben sind,
liegt IMHO vor allem darin, dass einige als int deklarierte Funktionen
der Standardbibliothek (bspw. fgetc) einen Wertebereich von mehr als
0..255 voraussetzen. Die Signatur dieser Funktionen wurden bereits
festgelegt lange bevor der erste C-Compiler für einen 8-Bit-Prozessor
geschrieben wurde.

Dieses Problem wurde dadurch umgangen, dass 16 Bit als minimale Größe
von int festgesetzt wurde. Das führte bei den ersten C-Compilern für
8-Bit-Prozessoren tatsächlich zu Performanzeinschränkungen, die aber in
neueren, stark optimierenden Compilern weitgehend behoben sind.

Dennoch bleiben ein paar wenige Fälle, wo auf 8-Bit-Prozessoren die
Integer-Promotion lästig ist. Da diese Fälle selten sind und i.Allg.
durch Umschrieben des betreffenden Ausdrucks oder Einfügen von Casts
leicht abgehandelt werden können, besteht für mich keinerlei Grund, auf
8-Bit-Prozessoren in einer anderen Sprache als C (oder allenfalls noch
Assembler) zu programmieren.

An dem von Karl angeprangerten Fall

1
  irgendwas1 == ~irgendwas2

störe ich mich überhaupt nicht, da ich einen solchen Ausdruck noch nie
gebraucht habe, obwohl schon seit vielen Jahren Mikrocontroller aus
vielen unterschiedlichen 8-, 16- und 32-Bit-Familien in C programmiere.

Übrigens kann man beim AVR-GCC die int-Größe mit -mint8 auf nicht 8 bit
einstellen. Dann verhält er sich wie von Karl gewünscht, allerdings ist
er damit nicht mehr ISO-konform, und die Standardbibliothek kann nicht
mehr verwendet werden bzw. müsste angepasst und neu gebaut werden.

von Yalu X. (yalu) (Moderator)


Lesenswert?

@Karl:

Bisher hast du dich ja um die Beantwortung der bereits mehrfach
gestellten Frage nach dem von dir verwendeten Compiler erfolgreich
herumgedrückt:

W.S. schrieb:
> Meinst du Mikroe oder was anderes?

Yalu X. schrieb:
> Das sieht nach Pascal aus, aber welcher Dialekt bzw.Compiler?

Arduino F. schrieb:
> Zeige mir bitte deinen eigenen "besseren" Kompiler.

Aber so langsam scheint Licht ins Dunkel zu kommen:

Karl schrieb:
> Da weiter oben der FPC erwähnt wurde: Der FPC auf dem PC kompiliert das
> gar nicht erst, man müsste ihn durch einen expliziten Typecast dazu
> zwingen. Und der FPC für Embedded AVR kompiliert das plattformkonform
> mit 8 Bit, und das Ergebnis ist korrekt.

Du arbeitest also mit dem AVR-FPC?

: Bearbeitet durch Moderator
von Walter T. (nicolas)


Lesenswert?

"Die Länge einer Diskussion ist invers proportional zur Wichtigkeit des 
Themas." So verlangt es das Gesetz.

von Possetitjel (Gast)


Lesenswert?

Michael R. schrieb:

> Weil ich Python nicht mag, verwende ich es nicht. Weil
> ich es nicht verwende, weiß ich nichts (oder nur sehr
> wenig) drüber. Weil ich nichts drüber weiß, schreibe
> ich nicht in Python-Threads mit (und schon gar nicht
> mokiere ich mich dort über Design-Fehler).

Das ist menschlich gesehen zwar nobel, sachlich aber
nicht zielführend: Der Fortschritt wird durch die
Unzufriedenen erzwungen :)

von Stefan F. (Gast)


Lesenswert?

>  Der Fortschritt wird durch die Unzufriedenen erzwungen

Ich vertrete ja eher die Ansicht, dass Fortschritt meisten zufällig 
passiert und der Versuch, ihn zu erzwingen, meistens scheitert.

von Possetitjel (Gast)


Lesenswert?

Yalu X. schrieb:

> Der ursprüngliche Grund, die Größe von int und damit den
> defaultmäßigen Wertebereich für Integer-Berechnungen an
> die Wortbreite des verwendeten Prozessors anzugleichen,
> liegt schlicht im Wunsch nach maximaler Performanz.

Der Gedanke lässt sich weiterspinnen:

Wenn die Performanz allein das entscheidende Argument
(gewesen) wäre, hätte sich C nicht so weit verbreiten
dürfen -- dann hätte man nämlich bei Assembler bleiben
müssen.
Es ist daher nicht unsinnig zu vermuten, dass auch die
anderen Eigenschaften von C -- wie die im Vergleich zum
Assembler höhere Abstraktion und die bessere Portabili-
tät -- eine wichtige Rolle gespielt haben.

Spätestens hier sollte aber auffallen, dass logisch
widersprüchliche Ziele vorliegen: Man kann nicht
gleichzeitig maximale Performance (die optimale
Ausnutzung maschinenspezifischer Besonderheiten
erfordert) und maximale Portabilität (die maximale
Unabhängigkeit von maschinenspezifischen Besonder-
heiten notwendig macht) haben -- wenigstens nicht ohne
zusätzlichen Aufwand.

von Possetitjel (Gast)


Lesenswert?

Stefan U. schrieb:

>>  Der Fortschritt wird durch die Unzufriedenen erzwungen
>
> Ich vertrete ja eher die Ansicht, dass Fortschritt
> meisten zufällig passiert und der Versuch, ihn zu
> erzwingen, meistens scheitert.

Unsere Aussagen sind vollständig kompatibel.

Von den 100 Unzufriedenen, die versuchen, den Fortschritt
zu erzwingen, scheitern 99, und nur einer hat Erfolg.
Trotzdem wird der Fortschritt von den Unzufriedenen
getragen -- die anderen haben nämlich kein Motiv, etwas
zu ändern :)

von W.S. (Gast)


Lesenswert?

Yalu X. schrieb:
> Der ursprüngliche Grund, die Größe von int und damit den defaultmäßigen
> Wertebereich für Integer-Berechnungen an die Wortbreite des verwendeten
> Prozessors anzugleichen, liegt schlicht im Wunsch nach maximaler
> Performanz.

Nö.

Ich verbuche sowas unter Geburtsfehler, von denen C eine Menge hat. 
Immerhin versemmelt man sich genau damit aus Sicht der zu lösenden 
Probleme jegliche Sicherheit, Allgemeingültigkeit und Portierbarkeit. 
Und um die "Performanz" sollte sich der Compiler bzw. dessen Architekt 
kümmern.

Die Sicherheit, daß ein Datentyp eben genau definiert ist, sollte man 
nicht unterschätzen. Das grundsätzliche Ziel einer Programmiersprache 
oberhalb des Assemblers ist es ja gerade, dem Programmierer eine 
Sicherhait für seine problemabhängigen Algorithmen zu geben und nicht 
etwa, daß er sich mit maschinenabhängigen Besonderheiten herumschlagen 
muß. Das ist Obliegenheit der Tools.

Die Sichtweise in C ist da mal wieder nicht die Sicht aus 
Anwenderperspektive, sondern die Sicht des Compiler-Erfinders.

Nochwas: Die Maschinen, die ich damals programmiert hatte, konnten 
überhaupt keinen byteweisen Zugriff. Der Speicher bestand kompletissimo 
aus 16 Bit WORD's und wenn man Text (7 Bit) speichern wollte, dann mußte 
man das selbst per Swap in diese 16 Bits einpassen.

So ging das damals - mit Kernspeicher-Maschinen.

W.S.

von Egon N. (egon2321)


Lesenswert?

Walter T. schrieb:
> "Die Länge einer Diskussion ist invers proportional zur
> Wichtigkeit des
> Themas." So verlangt es das Gesetz.

Quatsch, der Nazivergleich kam noch nicht.

https://de.wikipedia.org/wiki/Godwin%E2%80%99s_law

von Yalu X. (yalu) (Moderator)


Lesenswert?

W.S. schrieb:
> Yalu X. schrieb:
>> Der ursprüngliche Grund, die Größe von int und damit den defaultmäßigen
>> Wertebereich für Integer-Berechnungen an die Wortbreite des verwendeten
>> Prozessors anzugleichen, liegt schlicht im Wunsch nach maximaler
>> Performanz.
>
> Nö.

Gibt es deiner Ansicht nach denn einen anderen möglichen Grund für die
maschinenabhängige Größe von int? Oder haben dei C-Entwickler diese
Entscheidung etwa völlig grundlos getroffen?

> Ich verbuche sowas unter Geburtsfehler, von denen C eine Menge hat.

C hat zwar tatsächlich einige Geburtsfehler, aber die maschinenabhängige
int-Größe gehört definitiv nicht dazu.

> Immerhin versemmelt man sich genau damit aus Sicht der zu lösenden
> Probleme jegliche Sicherheit, Allgemeingültigkeit und Portierbarkeit.

Sprachen mit guter Portabilität (bspw. Fortran) oder guter Performanz
(Assembler) gab es bereits. C sollte ein guter Kompromiss aus beiden
Kriterien werden. Das ist es IMHO auch geworden, aber es ist und bleibt
eben ein Kompromiss, der in keinem der Kriterien das absolute Optimum
erreicht.

Wem heute bei High-Level-Anwendungen Portabilität über alles geht,
programmiert nicht in C, sondern bspw. in Java. Maschinennahe Software,
hingegen (bspw. ein Betriebssystem) ist per se nur eingeschränkt
portabel, so dass andere Kriterien in den Vordergrund treten, in denen
Java wiederum ganz schlecht abschneidet.

Die Programmiersprache, die für alle Anwendungen vom PID-Regler auf
einem 8-Bit-Controller bis hin zum hochkomplexen KI-System gleichermaßen
optimal geeignet ist, gibt es leider noch nicht und wird es vermutlich
auch nie geben.

> Und um die "Performanz" sollte sich der Compiler bzw. dessen Architekt
> kümmern.

Werden die Basisdatentypen erst einmal ungünstig festgelegt, kann das
auch der beste Optimierer nicht mehr richten. Man muss auch sehen, dass
in den 70ern die Compilertechnik noch nicht so weit fortgeschritten war
wie heute, und starke Optimierer, wie wir sie heute gewohnt sind, die
RAM-Kapazität der damaligen Rechner um Größenordnungen sprengen würden.

von Karl (Gast)


Lesenswert?

Yalu X. schrieb:
> störe ich mich überhaupt nicht, da ich einen solchen Ausdruck noch nie
> gebraucht habe

Ich auch nicht. Das ist ein Beispiel, an dem das Problem kurz und 
prägnant deutlich wird.

Die "Zutaten" zu diesem Beispiel allerdings gibt es durchaus: Ein NOT, 
um eingelesene low-aktive Pins zu wandeln, eine Konstante, die irgendwo 
definiert eine Maske der verwendeten Pins enthält, Prüfen ob keiner der 
Pins gesetzt ist, oder alle, oder...

Das ist auch nicht mein Beispiel, das findest Du auf diversen Seiten, 
und das ist nicht 8-Bit spezifisch, das funktioniert unter anderem 
Systemen ebenso.

Yalu X. schrieb:
> Übrigens kann man beim AVR-GCC die int-Größe mit -mint8 auf nicht 8 bit
> einstellen.

Wovon mir, als das Thema vor einigen Monaten mal aufkam, vehement 
abgeraten wurde, es würde zu vielen Problemen führen.

Damals ging es darum, dass C viele Operationen unnötigerweise auf 16 Bit 
aufbläht, wenn 8 Bit auch reichen würden.

von Possetitjel (Gast)


Lesenswert?

Karl schrieb:

> Damals ging es darum, dass C viele Operationen
> unnötigerweise auf 16 Bit aufbläht, wenn 8 Bit
> auch reichen würden.

Das Problem hat FreePascal eine Etage höher (32bit
vs. 64bit) aber auch.

Ich bin grundsätzlich ein Freund starker Typisierung,
aber das Problem ist mMn damit nicht lösbar.

von (prx) A. K. (prx)


Lesenswert?

Karl schrieb:
> Damals ging es darum, dass C viele Operationen unnötigerweise auf 16 Bit
> aufbläht, wenn 8 Bit auch reichen würden.

Als C definiert wurde kam niemand auf die Idee, es wäre etwas für 8 Bit 
Mikroprozessoren. Es gab keine. Die kleinste Klasse von Rechnern, auf 
denen ein C Compiler sinnvoll schien, waren 16-Bit Minicomputer. Es 
dauerte Jahrzehnte, bis sich C als Crosscompiler-Sprache für 8-Bit 
Mikrocomputer verbreitete.

: Bearbeitet durch User
von Possetitjel (Gast)


Lesenswert?

A. K. schrieb:

> Karl schrieb:
>> Damals ging es darum, dass C viele Operationen
>> unnötigerweise auf 16 Bit aufbläht, wenn 8 Bit
>> auch reichen würden.
>
> Als C definiert wurde kam niemand auf die Idee,
> es wäre etwas für 8 Bit Mikroprozessoren. Es gab
> keine.

Kopf-an-Kopf-Rennen:
C     - 1972 erschienen
i8008 - 1972 erschienen
i8080 - 1974 erschienen
Z80   - 1976 erschienen

> Die kleinste Klasse von Rechnern, auf denen ein C
> Compiler sinnvoll schien, waren 16-Bit Minicomputer.

Ja - wegen der klassischen Bindung von C an Unix.

> Es dauerte Jahrzehnte, bis sich C als Crosscompiler-
> Sprache für 8-Bit Mikrocomputer verbreitete.

... was man als Beweis dafür ansehen kann, dass frühe
Fehlentscheidungen NICHT ewigen Bestand haben müssen,
sondern auch korrigiert werden können.

Beitrag #5379147 wurde von einem Moderator gelöscht.
von Yalu X. (yalu) (Moderator)


Lesenswert?

Karl schrieb:
> Yalu X. schrieb:
>> Übrigens kann man beim AVR-GCC die int-Größe mit -mint8 auf nicht 8 bit
>> einstellen.
>
> Wovon mir, als das Thema vor einigen Monaten mal aufkam, vehement
> abgeraten wurde, es würde zu vielen Problemen führen.

Ich habe zwar nicht viel Erfahrung mit -mint8, aber solange du in deinen
Programmen keinen Fremdcode benutzt, der nicht für eine int-Größe von 8
bit geschrieben ist (also auch nicht die AVR Libc), dürften IMHO keine
größeren Probleme entstehen.

Hier ist übrigens noch ein kleines Beispiel für den AVR-FPC, dessen
Verhalten auch nicht immer ganz der menschlichen Intuition entspricht:

1
procedure test;
2
  var
3
    w: word;
4
    b: byte;
5
6
  begin
7
8
    { Wegen $e0 + $40 > $ff wird die not-Operation 16-bit-breit ausgeführt }
9
    w := not ($e0 + $40);   { -> $fedf }
10
    do_something_with(w);
11
12
    { Wird $e0 durch eine Byte-Variable mit gleichem Inhalt ersetzt, wird
13
      die not-Operation nur noch 8-bit-breit ausgeführt }
14
    b := $e0;
15
    w := not ( b  + $40);   { -> $00df }
16
    do_something_with(w);
17
18
  end;

Vom FPC generierter Assemblercode (R1 hat den Inhalt 0):

1
PsTEST_ss_TEST:
2
  ldi  r24,-33
3
  ldi  r26,-2
4
  mov  r25,r26
5
  call  PsTEST_ss_DO_SOMETHING_WITHsWORD
6
  ldi  r18,-32
7
  ldi  r24,-33
8
  mov  r25,r1
9
  jmp  PsTEST_ss_DO_SOMETHING_WITHsWORD

Leider konnte ich in der Dokumentation des FPC keine Erklärung für
dieses Verhalten finden.

In C liefern beide Ausdrücke das gleiche Ergebnis:

1
void test(void) {
2
  uint16_t w;
3
  uint8_t b;
4
5
  w = ~(0xe0 + 0x40);     // -> 0xfedf
6
  do_something_with(w);
7
8
  b = 0xe0;
9
  w = ~( b + 0x40);
10
  do_something_with(w);   // -> 0xfedf
11
}

Vom GCC generierter Assemblercode:

1
test:
2
  ldi r24,lo8(-33)
3
  ldi r25,lo8(-2)
4
  call do_something_with
5
  ldi r24,lo8(-33)
6
  ldi r25,lo8(-2)
7
  jmp do_something_with

: Bearbeitet durch Moderator
von Karl (Gast)


Lesenswert?

Possetitjel schrieb:
> Das Problem hat FreePascal eine Etage höher (32bit
> vs. 64bit) aber auch.

Auf dem PC sehe ich ja ein, dass es sinnvoller ist die Bitbreite der 
Matheunit voll auszunutzen, da geht eine Division mit 32 oder 64 Bit 
auch schnell.

Allerdings ist Pascal hier wenigstens konsequent und sagt: Wenn wir auf 
64 Bit Breite rechnen, dann alles, und wenn Du das anders willst musst 
Du das explizit casten. C wandelt halt einfach nach Belieben.

Auf dem AVR hab ich mir meine eigenen Matheroutinen geschrieben. Da 
Pascal Multiplikation oder Division immer auf gleicher Bitbreite macht, 
wird unnötig viel Overhead erzeugt, genau wie bei C. Wenn ich aber nur 
16bitx16bit=>32bit oder 32bit/8bit=>32bit brauche, spare ich enorm. Eine 
Division in 5usec statt in 200usec ist schon ein Nummer.

Aber wenigstens versucht Pascal nicht meinem 8-Bitter unbedingt 16 Bit 
aufzunötigen.

Was mir unter Pascal deutlich besser gefällt ist die Matheunterstützung. 
Letztens hab ich einen - aktuellen - Beitrag über C gelesen, wo als Tipp 
kam: Nehmen Sie mathematische Funktionen möglichst auseinander. Nur eine 
Operation pro Zeile. Da dachte ich so: Echt jetzt? Euer Ernst? Leider 
finde ich die Seite nicht mehr, vielleicht wurde sie aus Scham gelöscht. 
War das bei heise...?

von Yalu X. (yalu) (Moderator)


Lesenswert?

Karl schrieb:
> Letztens hab ich einen - aktuellen - Beitrag über C gelesen, wo als Tipp
> kam: Nehmen Sie mathematische Funktionen möglichst auseinander. Nur eine
> Operation pro Zeile. Da dachte ich so: Echt jetzt? Euer Ernst?

Das denke ich auch ;-)

Bist du sicher, dass es um C und nicht um Bascom ging?

Bei letzterem muss man sogar für jede Rechenoperation eine eigene
Anweisung schreiben.

> Leider
> finde ich die Seite nicht mehr, vielleicht wurde sie aus Scham gelöscht.

So wird es wohl sein ;-)

von Karl (Gast)


Lesenswert?

Yalu X. schrieb:
> Hier ist übrigens noch ein kleines Beispiel für den AVR-FPC, dessen
> Verhalten auch nicht immer ganz der menschlichen Intuition entspricht:

not($E0 + $40) gibt bei mir einen fetten range check error. Ich muss das 
explizit auf uint16 casten. Und dann isses klar, einmal wird das Not auf 
ein Word angewendet, einmal auf ein Byte, und dann erst auf ein Word 
erweitert.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Karl schrieb:
> Yalu X. schrieb:
>> Hier ist übrigens noch ein kleines Beispiel für den AVR-FPC, dessen
>> Verhalten auch nicht immer ganz der menschlichen Intuition entspricht:
>
> not($E0 + $40) gibt bei mir einen fetten range check error.

Stimmt, auch wenn "fett" etwas übertrieben ist, denn es erscheint
lediglich eine Warnung, die den Kompiliervorgang nicht abbricht.

Sie kommt daher, dass der Compiler das Ergebnis $fedf als negativ
ansieht, so dass es außerhalb des Wertebereichs von word liegt.

> Ich muss das explizit auf uint16 casten.

Die Warnung verschwindet auch ohne Cast, wenn man für w einen
passenderen Datentyp wählt. Ersetze also

1
    w: word;

durch

1
    w: smallint;

so dass w auch negative Werte annehmen kann. Die Ergebnisse sind aber
immer noch verschieden. Es ändert sich nicht einmal der generierte
Assemblercode.

Was nun?

von Possetitjel (Gast)


Lesenswert?

Yalu X. schrieb:

> Die Ergebnisse sind aber immer noch verschieden. Es
> ändert sich nicht einmal der generierte Assemblercode.
>
> Was nun?

Naja, schätzungsweise ist "not" einfach überladen.

Anders ausgedrückt: "$E0 + $40" liefert dieser
Vermutung nach im ersten Beispiel nicht deswegen ein
word, weil es größer als 255 ist, sondern weil als
Ergebnis ein Word erwartet wird. (Die Konstanten
selbst haben ja in Pascal, wenn ich mich recht
entsinne, keinen Typ.)
Das kannst Du ja leicht prüfen: Sieh' einfach, was
bei "$30 + $50" passiert, und berichte.

Analog im zweiten Beispiel: Byte plus Konstante gibt
erstmal ein Byte; der unäre Operator "not" macht ein
Byte aus dem Byte, und die Zuweisung expandiert auf
das Word. Genau das kommt heraus.

Ich habe da jetzt nicht tiefer drübernachgedacht,
aber ich finde das auch völlig logisch so.

von Possetitjel (Gast)


Lesenswert?

Possetitjel schrieb:

> Analog im zweiten Beispiel: Byte plus Konstante gibt
> erstmal ein Byte; der unäre Operator "not" macht ein
> Byte aus dem Byte, und die Zuweisung expandiert auf
> das Word. Genau das kommt heraus.

Ich bekomme hier noch eine Meise. Warum liefert...
1
program multest; 
2
3
var b1, b2, b3 : byte; 
4
             w : word; 
5
begin 
6
  b1:=100; 
7
  b2:=200; 
8
  b3:=1; 
9
  w:=not((b1*b2)+b3); 
10
  writeln('w=',w);
11
end.

... als Ergebnis "w=45534" (=0xB1DE)?

Meiner Theorie nach hätte Byte mal Byte plus Byte
wiederum Byte ergeben sollen, so dass ich ein
Ergebnis der Art "0x00??" erwartet hätte.

> Ich habe da jetzt nicht tiefer drübernachgedacht,
> aber ich finde das auch völlig logisch so.

Ich nehme das frustriert zurück.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Karl schrieb:
> Auf dem PC sehe ich ja ein, dass es sinnvoller ist die Bitbreite der
> Matheunit voll auszunutzen, da geht eine Division mit 32 oder 64 Bit
> auch schnell.

Dabei geht es nicht nur um (mathematische) Berechnungen; mWn profitieren 
alle Register- und Speicheroperationen von der nativen Wortbreite.

> C wandelt halt einfach nach Belieben.
"nach Belieben" wäre mir noch nicht aufgefallen.

> Auf dem AVR hab ich mir meine eigenen Matheroutinen geschrieben.
Das kann aber nicht Sinn der Übung sein, oder?

> Was mir unter Pascal deutlich besser gefällt ist die Matheunterstützung.
Inwiefern? Grad vorher schreibst du, du hättest das alles selbst 
geschrieben?

> Damals ging es darum, dass C viele Operationen unnötigerweise auf 16 Bit
> aufbläht, wenn 8 Bit auch reichen würden.
ich mach jetzt doch schon einige Jahre in C, etwas kürzer auf dem AVR, 
aber ich kann mich nicht erinnnern da auf signifikante Probleme gestoßen 
zu sein (was nicht heißt dass solche nicht existieren).

Hast du mal ein wirklich konkretes Beispiel? Also nicht so ein 
konstruiertes wie
1
irgendwas1 == ~irgendwas2
von dem du ja selbst schreibst dass du es noch nie gebraucht hast.

von Rolf M. (rmagnus)


Lesenswert?

Karl schrieb:
> Allerdings ist Pascal hier wenigstens konsequent und sagt: Wenn wir auf
> 64 Bit Breite rechnen, dann alles, und wenn Du das anders willst musst
> Du das explizit casten. C wandelt halt einfach nach Belieben.

Nein, C macht das gleiche. Gerade aus diesem Grund gibt es die 
Integer-Promotion, die dich so stört, doch. Berechnungen werden immer in 
int durchgeführt, es sei denn, die Eingangsdatentypen sind größer. 
Gleiches gilt entsprechend für den Datentyp von Integerkonstanten.
Deshalb sollte int möglichst die native Breite der CPU sein. Es gibt 
allerdings eben die Einschränkung, dass es mindestens 16 Bit breit sein 
muss, was für den Spezialfall einer 8-Bit-CPU nicht ideal ist. Die 
Compiler-Optimierungen sorgen aber dafür, dass der Impact auf Laufzeit 
und Codegröße minimal ist.

von A. S. (Gast)


Lesenswert?

Rolf M. schrieb:
> Es gibt allerdings eben die Einschränkung, dass es mindestens 16 Bit
> breit sein muss, was für den Spezialfall einer 8-Bit-CPU nicht ideal
> ist.

...und deshalb dort meist abgestellt werden kann. Für optimierten 
8-bit-code.

Wer portable(r) sein will, lässt es.

von (prx) A. K. (prx)


Lesenswert?

Possetitjel schrieb:
> Kopf-an-Kopf-Rennen:
> C     - 1972 erschienen
> i8008 - 1972 erschienen

1972 erschien das Buch, Sprache und Compiler entstanden logischerweise 
vorher. Dass der Dennis Ritchie mit Intel unter einer Decke steckte darf 
man wohl ausschliessen.

Aber ein C Compiler, der auf einem 8008 läuft, das wär schon eine 
Vorstellung für 1972 ;-). Immerhin entstand C als Sprache eines 
laufenden Systems, nicht als Crosscompiler-Sprache für Drittsysteme, wie 
es heute bei Mikrocontrollern üblich ist.

: Bearbeitet durch User
von Jobst Q. (joquis)


Lesenswert?

Possetitjel schrieb:
> Ich bekomme hier noch eine Meise. Warum liefert...
> program multest;
>
> var b1, b2, b3 : byte;
>              w : word;
> begin
>   b1:=100;
>   b2:=200;
>   b3:=1;
>   w:=not((b1*b2)+b3);
>   writeln('w=',w);
> end.
>
> ... als Ergebnis "w=45534" (=0xB1DE)?
>
> Meiner Theorie nach hätte Byte mal Byte plus Byte
> wiederum Byte ergeben sollen, so dass ich ein
> Ergebnis der Art "0x00??" erwartet hätte.

Das zeigt doch schön, das dass grundsätzliche Problem weder bei C noch 
bei Pascal liegt, sondern beim unären Operator 'not' bzw '~'. Sein 
Verhalten ist naturgemäß typgebunden.

Wie ich schon weiter oben geschrieben habe, ist seine einzig sinnvolle 
Anwendung das Zurücksetzen von Bits zusammen mit 'and' bzw '&', wodurch 
die Typgebundenheit aufgehoben wird.

Wenn es um Invertierung geht, ist der binäre Operator 'xor' bzw '^' 
immer die bessere und sicherere Wahl.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Possetitjel schrieb:
>> Die kleinste Klasse von Rechnern, auf denen ein C
>> Compiler sinnvoll schien, waren 16-Bit Minicomputer.
>
> Ja - wegen der klassischen Bindung von C an Unix.

Weniger. Sondern weil man oft nur den einen Rechner hatte, nämlich den 
Zielrechner. Auf dem sollte der Compiler laufen, Unix oder nicht. So 
wars bei mir Ende der 70er auch, Compiler (PL/65) und Assembler liefen 
auf dem 6502 Rechner selbst. Einen anderen hatte ich nicht.

Mikrocontroller mit Entwicklungssystem auf einem PC gab es sinngemäss in 
den 70ern zwar auch. Minicomputer mit Assembler und ggf. Compiler als 
Entwicklungssystem für Mikrocomputer und Mikrocontroller. Aber in dieser 
Szene war C völlig unbekannt.

: Bearbeitet durch User
von Yalu X. (yalu) (Moderator)


Lesenswert?

Jobst Q. schrieb:
> Das zeigt doch schön, das dass grundsätzliche Problem weder bei C noch
> bei Pascal liegt, sondern beim unären Operator 'not' bzw '~'. Sein
> Verhalten ist naturgemäß typgebunden.

Hier noch ein Beispiel ohne Bitoperationen, sondern nur mit gewöhnlichen
Additionen:

1
procedure test;
2
  const
3
    b1 = 100;
4
5
  var
6
    b2: byte;
7
    i: smallint;
8
9
  begin
10
    i := b1 + 200;         { -> 300 }
11
    do_something_with(i);
12
13
    b2 := 100;
14
    i := b2 + 200;         { ->  44 }
15
    do_something_with(i);
16
  end;

Wie es scheint, liegt dieses (zumindest für C-Programmierer seltsam
anmutende) Verhalten an der Auswertung konstanter Ausdrücke:

Solche Ausdrücke werden durch den Compiler ausgewertet, wobei ein
Wertebereich von -2⁶³..+2⁶⁴-1 (das ist die Vereinigung von int64 und
uint64) abgedeckt wird. Wird dieser Wertebereich überschritten, meldet
der Compiler einen Overflow-Error, ansonsten ist das Ergebnis immer
mathematisch korrekt. Anders als in C wird das Ergebnis also nicht auf
eine bestimmte Bitbreite beschnitten.

Beispiel:

1
Ausdruck: 1000 * 1000
2
AVR-FPC:  -> 1000000
3
AVR-GCC:  ->   16960  (= 1000000 mod 2¹⁶)

Erst nach der Auswertung des konstanten Ausdrucks wird sein Typ
bestimmt, und zwar abhängig vom berechneten Wert.

Beispiel:

1
Ausdruck          Wert    Typ
2
——————————————————————————————————————————————
3
10 + 10             20    uint8 bzw. byte
4
10 - 30            -20    int8
5
1000 * 1000    1000000    uint32 bzw. longword
6
usw.
7
——————————————————————————————————————————————

In einem Ausdruck, der auch Variablen enthält, werden zuerst die
konstanten Teilausdrücke ausgewertet und typisiert, dann kommen die in
der FPC-Dokumentation beschriebenen impliziten Typkonvertierungen zur
Anwendung (auf der Seite nach "type conversion" suchen):

  https://www.freepascal.org/docs-html/current/ref/refsu4.html#x26-250003.1.1

In C wird für jede Teiloperation erst der Typ des oder der Operanden
bestimmt und danach mit einer von den Operandentypen abhängigen
Bitbreite das Ergebnis berechnet. Der Ablauf ist also genau andersherum
als in Free Pascal. Dieselbe Methode wird auch für variable Ausdrücke
angewandt, die erst zur Laufzeit berechnet werden.

Das führt letztendlich zu den folgenden Eigenschaften der beiden
Methoden:

Free-Pascal:

- Der Wert konstanter Ausdrücke ist immer richtig (oder der Compiler
  bricht mit einer Fehlermeldung ab, wenn der Wert nicht mit 64 Bit
  darstellbar ist, was aber selten passieren dürfte).

- Ausdrücke mit Variablen können zu abweichenden Ergebnissen führen, da
  dort andere Auswertungsregeln gelten.

C:

- Der Wert wird bei Überläufen beschnitten und ist deswegen nicht immer
  mathematisch korrekt.

- Das Ergebnis eines Ausdrucks hängt nur von dessen Operandenwerten und
  -typen ab, ist aber unabhängig davon, ob es sich bei den Operanden um
  Konstanten oder Variablen handelt.

Beide Sprachen haben also ein paar Überraschungen parat. Wenn man die
genannten Regeln genau kennt, kann man mit beiden Methoden gut leben.
Deswegen sollte man sich das Regelwerk einmal genau durchlesen, die
Frage ist nur, wo:

- Bei Free Pascal habe ich in der Dokumentation immer noch keine
  Beschreibung der Auswertung und Typisierung konstanter Ausdrücke
  gefunden, aber vielleicht habe ich auch einfach nur Tomaten auf den
  Augen :)
  Die ISO-Normen für Pascal und Extended Pascal stellen leider
  keine Hilfe bei Fragen zu aktuellen Pascal-Implementationen dar.

- Für C gibt es viel mehr Informationsquellen (sowohl auf Papier als
  auch im Netz). Im Zweifelsfall kann man die ISO-Norm zu Rate ziehen,
  die solche (und andere) Dinge naturgemäß mit absoluter Präzision
  beschreibt.

: Bearbeitet durch Moderator
von Possetitjel (Gast)


Lesenswert?

Jobst Q. schrieb:

>> Meiner Theorie nach hätte Byte mal Byte plus Byte
>> wiederum Byte ergeben sollen, so dass ich ein
>> Ergebnis der Art "0x00??" erwartet hätte.
>
> Das zeigt doch schön, das dass grundsätzliche Problem
> weder bei C noch bei Pascal liegt, sondern beim unären
> Operator 'not' bzw '~'.

Hmm. Weiss nicht. Vielleicht ist das Problem nur mein
schlechtes Gedächtnis. Ich war GANZ sicher, das TurboPascal
bei
1
 
2
  b1:=100; 
3
  b2:=200; 
4
  w:=b1*b2;

als Ergebnis "32" geliefert hätte. FreePascal liefert jedoch
20'000.


> Sein Verhalten ist naturgemäß typgebunden.

Sicher -- aber lt. Online-Hilfe ist "not" nur für Bytes (!)
definiert.

Im dargestellten Beispiel
1
 
2
  b1:=100; 
3
  b2:=200; 
4
  b3:=1; 
5
  w:=not((b1*b2)+b3);

kommt lt. Test "w=45534" heraus, was das Negat von 20001 ist.

Wie kann es sein, dass in einem Ausdruck, der angeblich nur
aus Byte-Operanden und Byte-Operatoren besteht, bereits die
einzelnen Faktoren der Multiplikation (!!) auf 16bit erweitert
werden, obwohl in dem arithmetischen Ausdruck nichts steht,
das auch nur entfernt nach 16Bit aussieht?

von (prx) A. K. (prx)


Lesenswert?

Wer die integer promotion von C nicht mag - ist der Umgang von Free 
Pascal mit ganzzahligen Rechnungen tatsächlich einfacher?
https://www.freepascal.org/docs-html/current/ref/refsu4.html#x26-260003.1.1

Possetitjel schrieb:
> Wie kann es sein, dass in einem Ausdruck, der angeblich nur
> aus Byte-Operanden und Byte-Operatoren besteht, bereits die
> einzelnen Faktoren der Multiplikation (!!) auf 16bit erweitert
> werden, obwohl in dem arithmetischen Ausdruck nichts steht,
> das auch nur entfernt nach 16Bit aussieht?

Das geschieht gemäss obiger Referenz, wenn die "native integer size" 16 
Bits beträgt:

`Every platform has a ”native” integer size, depending on whether the 
platform is 8-bit, 16-bit, 32-bit or 64-bit. e.g. On AVR this is 8-bit.

Every integer smaller than the ”native” size is promoted to a signed 
version of the ”native” size. Integers equal to the ”native” size keep 
their signedness.´

von (prx) A. K. (prx)


Lesenswert?

Wer das alles zu unintuitiv ist, der kann sich bei PL/I umsehen. Da legt 
man bei der Deklaration von Variablen die Anzahl Bits vor und nach dem 
Komma fest, "fixed binary (15,0)" ist eine 16 Bit Integer in binärer 
Darstellung - das Vorzeichen geht extra, 15 Bits insgesamt, davon 0 nach 
dem Komma.

Bei den Operatoren darf man dann kopfrechnen. Wenn man fixed(P,Q) und 
fixed(R,S) addiert oder subtrahiert, dann kommt ein 
fixed(MIN(N,1+MAX(P-Q,R-S)+MAX(Q,S)),MAX(Q,S)) raus, mit N als 
implementiertem Maximum. Multiplikation ist natürlich einfacher: 
fixed(MIN(N,P+R+1),Q+S). Also solange es in die Grenzen der 
Implementierung passt gibts keinen Überlauf.

http://documentation.microfocus.com/help/topic/com.microfocus.eclipse.infocenter.studee60ux/BKPFPFEXPRMATHOPS.html

Bei Bitoperation war man ebenfalls sehr korrekt. Das macht man nicht mit 
Zahlen, sondern mit Bitstrings deklarierter Länge. Und weil das eben 
Strings sind, erweitern Operatoren auf Operanden unterschiedlicher Länge 
den kürzeren String linksbündig. '10000'B | '01'B ergibt '11000'B

http://documentation.microfocus.com/help/topic/com.microfocus.eclipse.infocenter.studee60ux/BKPFPFEXPRS011.html

: Bearbeitet durch User
von Possetitjel (Gast)


Lesenswert?

A. K. schrieb:

> Das geschieht gemäss obiger Referenz,

Vielen Dank für den Link.


> wenn die "native integer size" 16 Bits beträgt:
>
> `Every platform has a ”native” integer size, [...]

Oh Gott. Nicht auch Pascal... !

von (prx) A. K. (prx)


Lesenswert?

Possetitjel schrieb:
>> `Every platform has a ”native” integer size, [...]
>
> Oh Gott. Nicht auch Pascal... !

;-)

Nur dass Free Pascal auch 8 Bits als native integer zulässt.

Aber es ist schon ein wenig anders als in C. Komplizierter nämlich. 
Beispielsweise weil es Ausnahmen gibt, etwa indem die Differenz zweier 
vorzeichenloser Typen ein Vorzeichen hat. Und wenn man Typen mit und 
ohne Vorzeichen mischt, kann es je nach Grösse auf ein Ergebnis 
rauslaufen, das doppelt so gross ist. Beides ist nicht unlogisch, aber 
kompliziert. Und natürlich ist das abhängig vom konkreten Pascal, Delphi 
anders als Turbo Pascal.

: Bearbeitet durch User
von Karl (Gast)


Lesenswert?

Jobst Q. schrieb:
> Wie ich schon weiter oben geschrieben habe, ist seine einzig sinnvolle
> Anwendung das Zurücksetzen von Bits zusammen mit 'and' bzw '&', wodurch
> die Typgebundenheit aufgehoben wird.

Ja klar, alle blöd ausser Du...

Die wohl berühmteste Routine des Forums: 
https://www.mikrocontroller.net/articles/Entprellung#Timer-Verfahren_.28nach_Peter_Dannegger.29

Siehe unten bei C-Code.

Jobst Q. schrieb:
> Wenn es um Invertierung geht, ist der binäre Operator 'xor' bzw '^'
> immer die bessere und sicherere Wahl.

Schon deswegen nicht, weil xor immer eine Hilfsvariable braucht.

von Karl (Gast)


Lesenswert?

Michael R. schrieb:
> Hast du mal ein wirklich konkretes Beispiel? Also nicht so ein
> konstruiertes wie

Ich weiss ja nicht, ob Dir das konstruiert ist:

[code]void test(void) {
  char a;
  a = a * 4;
  a = a << 2;
}

  ldd r24,Y+1
  lsl r24
  lsl r24
  std Y+1,r24
  ldd r24,Y+1
  clr r25
  sbrc r24,7
  com r25
  lsl r24
  rol r25
  lsl r24
  rol r25
  std Y+1,r24[code]

Ich hab das damals mit uint8_t gemacht, aber das nimmt der GCC im 
Codeexplorer nicht an, und AvrStudio 7 habe ich wegen exzessiver 
Platzverschwendung von der Platte gehauen.

von Karl (Gast)


Lesenswert?

Yalu X. schrieb:
> Erst nach der Auswertung des konstanten Ausdrucks wird sein Typ
> bestimmt, und zwar abhängig vom berechneten Wert.

Und das ist gut so:
1
const
2
  Cfcpu = 16000000;  // 16MHz Quarz
3
...
4
const
5
  fmul = 1 * Cfcpu div 1000000;

Bin ich ganz froh, das fmul hier nur ein Byte ist und nicht 4 Bytes.

A. K. schrieb:
> Wer die integer promotion von C nicht mag - ist der Umgang von Free
> Pascal mit ganzzahligen Rechnungen tatsächlich einfacher?

Ähm nein, da ist auch historisch viel Mist gewachsen. Deswegen verwende 
ich für den AVR prinzipiell nur uint8, uint16, int8, int16. Auf dem PC 
allerdings habe ich mir angewöhnt, dass ein Zähler von 1 bis 10 durchaus 
integer sein darf, und damit je nach Maschine 32 oder 64 Bits, weil es 
eh keinen Unterschied macht.

A. K. schrieb:
> Da legt
> man bei der Deklaration von Variablen die Anzahl Bits vor und nach dem
> Komma fest, "fixed binary (15,0)" ist eine 16 Bit Integer in binärer
> Darstellung - das Vorzeichen geht extra, 15 Bits insgesamt, davon 0 nach
> dem Komma.

Ja und, Festkommazahl, geht bei Ada auch und für FreePascal gibts glaub 
ich eine Lib für. Der AVR kann das ja sogar bedingt, allerdings scheint 
es so ein Hirnkrampf für den Programmierer zu sein, dass es kaum jemand 
nimmt.

von Possetitjel (Gast)


Lesenswert?

Karl schrieb:

> const
>   fmul = 1 * Cfcpu div 1000000;
>
> Bin ich ganz froh, das fmul hier nur ein Byte ist und
> nicht 4 Bytes.

Verstehe ich nicht. Würde Dir die Hand abfaulen, wenn Du
1
 
2
fmul = byte(1 * Cfcpu div 1000000);

schreiben müsstest?

von (prx) A. K. (prx)


Lesenswert?

Karl schrieb:
> Ja und, Festkommazahl, geht bei Ada auch und für FreePascal gibts glaub
> ich eine Lib für.

Es ging mir dabei weniger um die Nachkommastellen. Sondern darum, dass 
das Ergebnis einer Addition von zwei 16-Bit Operanden ein Typ mit 17 
Bits war. Das war die normale Integer-Rechnung, eine andere gab es 
nicht.

: Bearbeitet durch User
von Yalu X. (yalu) (Moderator)


Lesenswert?

Karl schrieb:
> Und das ist gut so:
> const
>   Cfcpu = 16000000;  // 16MHz Quarz
> ...
> const
>   fmul = 1 * Cfcpu div 1000000;
> Bin ich ganz froh, das fmul hier nur ein Byte ist und nicht 4 Bytes.

fmul ist eine Compilezeitkonstante. Die belegt auf dem Zielsystem kein
einziges Byte.

von Karl (Gast)


Lesenswert?

Michael R. schrieb:
> Inwiefern? Grad vorher schreibst du, du hättest das alles selbst
> geschrieben?

Beides.

Ich habe ein Projekt mit mehreren umfangreichen Berechnungen von C auf 
Pascal übertragen und meiner Beobachtung nach produziert hier Pascal 
besseren Code bzw. nutzt Register besser aus für die gleichen 
Berechnungen.

Allerdings setzt Pascal genau wie C einige Berechnungen zu aufwändig um. 
Für a32 = b16 x c16 muss zwingend b und c auf 32 Bit erweitert werden 
und eine 32 Bit Multiplikation ausgeführt werden. Mit einer 16 Bit 
Multiplikation ist a auch nur 16 Bit.

In Assembler kann ich aber b16 und c16 problemlos zu a mit 32 Bit 
multiplizieren.

Nun müssen bei 32x32 Bit natürlich selbst unter Verwendung des 
Hardware-Multiplizierers deutlich mehr Register rumgeschubst werden als 
bei 16 x 16 Bit, einschließlich des pushens und poppens dieser Register, 
der längeren Laufzeit und des größeren Speicherbedarfs. Da dauert die 
Berechnung durchaus 20mal so lange. Bei Division ist es noch extremer, 
da alles in Software.

Und nun kommts drauf an: Muss ich nur ab und zu einen Wert berechnen, 
nehm ich die fertigen Routinen. Muss ich was in Echtzeit berechnen, über 
viele Werte, sprich es muss einfach schnell sein, nehm ich die 
optimierten Assembler-Routinen.

von Jobst Q. (joquis)


Lesenswert?

Karl schrieb:
> Jobst Q. schrieb:
>> Wenn es um Invertierung geht, ist der binäre Operator 'xor' bzw '^'
>> immer die bessere und sicherere Wahl.
>
> Schon deswegen nicht, weil xor immer eine Hilfsvariable braucht.

Meistens keine Variable, sondern eine Konstante. Und mit dieser 
Konstante kann man genau bestimmen, wieviele und welche Bits invertiert 
werden. Unabhängig vom Compiler, vom Zielprozessor und von irgendwo 
festgelegten Typen. Also nur Vorteile.

von Karl (Gast)


Lesenswert?

Jobst Q. schrieb:
> Meistens keine Variable, sondern eine Konstante.

Es gibt kein xor mit Konstante auf dem AVR. Du musst eine Konstante 
immer erst in ein Register laden und dann kannst Du das Register mit 
Deinem Wert ver-xor-en.

von Jobst Q. (joquis)


Lesenswert?

Es geht hier um C oder Pascal, nicht um Assembler. Was der Compiler 
daraus macht ist seine Sache.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Karl schrieb:
> Allerdings setzt Pascal genau wie C einige Berechnungen zu aufwändig um.
> Für a32 = b16 x c16 muss zwingend b und c auf 32 Bit erweitert werden
> und eine 32 Bit Multiplikation ausgeführt werden.

Der AVR-GCC macht das schon seit geraumer Zeit besser, zumindest bei
Controllern mit Hardwaremultiplizierer. Johann, der auch hier im Forum
aktiv ist, hat's gerichtet:

  https://gcc.gnu.org/ml/gcc-patches/2011-06/msg02114.html

Die selbstgeschriebene 16-Bit-Multiplikation mit 32-Bit-Ergebnis
benötigst du nur noch für Pascalprogramme.

von Possetitjel (Gast)


Lesenswert?

Jobst Q. schrieb:

> Und mit dieser Konstante kann man genau bestimmen,
> wieviele und welche Bits invertiert werden.
> Unabhängig vom Compiler, vom Zielprozessor und von
> irgendwo festgelegten Typen. Also nur Vorteile.

Mit Verlaub -- aber da widerspreche ich.

Von einer Hochsprache, die den Namen verdient, erwarte
ich schon, dass ich die üblichen booleschen Operationen
einfach im Klartext (egal, ob Schlüsselwort oder
Sonderzeichen-Operator) hinschreiben kann -- und nicht
erst den Karnaugh-Plan im Kopf aufstellen muss. Als
"üblich" würde ich mal NOT, AND, OR, XOR auffassen.

(Auf der Siemens-S7-SPS geht das in AWL nicht so einfach;
es ist ein ziemlicher Krampf, dort einen Flankendetektor
zu formulieren, denn (NOT(alt) AND neu) funktioniert nicht.)

Vielleicht übersehe ich ja etwas, aber mir erschließt
sich das fundamentale Problem mit "NOT" nicht. Das ist
ein unärer Operator, der Datentyp des Ergebnisses kann
(=sollte) also derselbe wie der des Operanden sein.

von Possetitjel (Gast)


Lesenswert?

A. K. schrieb:

> Possetitjel schrieb:
>>> `Every platform has a ”native” integer size, [...]
>>
>> Oh Gott. Nicht auch Pascal... !
>
> ;-)
>
> Nur dass Free Pascal auch 8 Bits als native integer
> zulässt.

Naja, ich halte, drastisch formuliert, native integers
generell für Schwachsinn und einen Irrweg.

Hochsprachen sollten die Portabilität fördern -- und
nicht noch dazu einladen, die Plattformabhängigkeiten
gleichmäßig über den Quelltext zu verteilen.

> Beides ist nicht unlogisch, aber kompliziert. Und natürlich
> ist das abhängig vom konkreten Pascal, Delphi anders als
> Turbo Pascal.

Furchtbar.

Aber an die heilige Kuh, die seit Jahrzehnten tradierte
implizite Modulo-Arithmetik, hat sich wieder einmal niemand
herangetraut. Typisch.

von (prx) A. K. (prx)


Lesenswert?

Possetitjel schrieb:
> Naja, ich halte, drastisch formuliert, native integers
> generell für Schwachsinn und einen Irrweg.

Viele Prozessoren favorisieren bestimmte Datentypen. 32-Bit RISCs tun 
sich bei Rechnungen mit 32-Bit Integers leichter als mit kleineren 
Typen. Wenn eine Sprache also der Implementierung die Möglichkeit gibt, 
in der favorisierten Breite zu rechnen ohne bei jedem Schritt auf 
Einhaltung einer kleineren Breite zu bestehen, dann ist das schlicht ein 
Tribut an die Effizienz.

von Possetitjel (Gast)


Lesenswert?

A. K. schrieb:

> Viele Prozessoren favorisieren bestimmte Datentypen. 32-Bit
> RISCs tun sich bei Rechnungen mit 32-Bit Integers leichter
> als mit kleineren Typen.

Ja, ich weiss. Das ist mir klar.


> Wenn eine Sprache also der Implementierung die Möglichkeit
> gibt, in der favorisierten Breite zu rechnen ohne bei jedem
> Schritt auf Einhaltung einer kleineren Breite zu bestehen,
> dann ist das schlicht ein Tribut an die Effizienz.

Sicher -- aber dazu ist kein "native integer" notwendig.

Es wäre völlig ausreichend, wenn man beim Definieren
der Variablen keinen "Typ", sondern außer dem Hinweis
"integer" noch den notwendigen Wertebereich angeben könnte:
1
 
2
var 
3
  menuepunkt : integer range(1..20);
Das würde genau denselben Effekt erzielen; der Compiler
wäre völlig frei in seiner Entscheidung, auf welchen
plattform-eigenen Typ er diese Variable abbildet -- aber
es wäre dennoch vollständig portabel.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Possetitjel schrieb:
> Hochsprachen sollten die Portabilität fördern -- und
> nicht noch dazu einladen, die Plattformabhängigkeiten
> gleichmäßig über den Quelltext zu verteilen.

in Ergänzung zu A.K.s richtigem Kommentar meine ich, dass "native 
integers" genau das tun - Portabilität fördern.

Wenn ich schreibe
1
for (int i = 42; i> 0; i--) {
2
   magic_smoke(i)
3
}
dann möchte ich dass auf einer 64-bit-CPU i 64 bit hat, detto mit 32, 16 
(mal abgesehen davon dass der hochoptimierende Compiler noch eingreift)

von Jobst Q. (joquis)


Lesenswert?

Possetitjel schrieb:
> Von einer Hochsprache, die den Namen verdient, erwarte
> ich schon, dass ich die üblichen booleschen Operationen
> einfach im Klartext (egal, ob Schlüsselwort oder
> Sonderzeichen-Operator) hinschreiben kann -- und nicht
> erst den Karnaugh-Plan im Kopf aufstellen muss. Als
> "üblich" würde ich mal NOT, AND, OR, XOR auffassen.

Die Probleme treten ja nicht bei den boolschen (logischen) Operatoren 
auf, sondern bei den Bitoperatoren. C unterscheidet das aus gutem Grund 
mit unterschiedlichen Zeichen. ! && || für die Logik und ~ & | ^ für die 
Bitmanipulationen.

Bei den Bitoperatoren ist das NOT ~ als unärer ein typabhängiger 
Sonderfall. Es hängt vom Typ ab, wieviele Bits betroffen sind. Bei den 
binären Operatoren AND, OR und XOR wird das von den Operanden bestimmt.

Deshalb würde ich das ~ vermeiden, so gut es geht, aus Gründen der 
Vorhersehbarkeit und der Portabilität, die du ja auch sehr schätzt.

Possetitjel schrieb:
> (Auf der Siemens-S7-SPS geht das in AWL nicht so einfach;
> es ist ein ziemlicher Krampf, dort einen Flankendetektor
> zu formulieren, denn (NOT(alt) AND neu) funktioniert nicht.)

Wieso?

U Neu; UN Alt; = PosFlanke;
UN Neu; U Alt; = NegFlanke;
U Neu; X Alt; = Aenderung;
...
U Neu; = Alt;

von Possetitjel (Gast)


Lesenswert?

Michael R. schrieb:

> Possetitjel schrieb:
>> Hochsprachen sollten die Portabilität fördern -- und
>> nicht noch dazu einladen, die Plattformabhängigkeiten
>> gleichmäßig über den Quelltext zu verteilen.
>
> in Ergänzung zu A.K.s richtigem Kommentar meine ich,
> dass "native integers" genau das tun - Portabilität
> fördern.

Nur in speziellen Fällen.

> Wenn ich schreibe
> for (int i = 42; i> 0; i--) {
>    magic_smoke(i)
> }

Das darf man m.W. in Pascal nicht schreiben; diese
"inline-Deklaration" war zumindest bisher nicht erlaubt.


> dann möchte ich dass auf einer 64-bit-CPU i 64 bit hat,
> detto mit 32, 16 (mal abgesehen davon dass der
> hochoptimierende Compiler noch eingreift)

Bei allem gebotenen Respekt glaube ich, dass Du EIGENTLICH
etwas anderes willst: Den Integer-Datentyp, der
1. maximale Rechengeschwindigkeit ermöglicht und
2. den notwendigen Zahlbereich abdeckt.

Oder möchtest Du im (leicht modifizierten) Beispiel:
1
 
2
for (int i = 300; i> 0; i--) {
3
   magic_smoke(i)
4
}
auf einer 8-bit-Maschine einen Compilerfehler bekommen?

Ich würde das nicht wollen.

von (prx) A. K. (prx)


Lesenswert?

Possetitjel schrieb:
> var
>   menuepunkt : integer range(1..20);

> Das würde genau denselben Effekt erzielen; der Compiler
> wäre völlig frei in seiner Entscheidung, auf welchen
> plattform-eigenen Typ er diese Variable abbildet -- aber
> es wäre dennoch vollständig portabel.

Eine solche Spezifikation geht am Problem vorbei. Denn das Problem 
besteht nicht in der Breite der Speicherung von Variablen. Sondern in 
der Breite der Berechnung von Ausdrücken, die Variablen verwenden. Wenn 
du dieses Regelwerk nur von solchen Deklarationen abhängig machst, dann 
landest du ungefähr beim erwähnten PL/I, also mit individuellen 
Rechenbreiten für jeden Rechenschritt. Nur eben in Wertebereichen, statt 
wie in PL/I in Bits oder Digits.

Die Multiplikation zweier [1..100] Variablen ergäbe also ein 
Zwischenergebnis [1..10000] und müsste somit mit mindestens 16 Bits 
stattfinden. Denn du wirst mit Variablen, die als [1..100] deklariert 
sind, wohl kaum meinen, dass dies auch auf das Produkt zutreffen sollte.

Weitere Rechenschritte führen dann zu weiteren möglichen Änderungen in 
der Rechenbreite. Wird dabei von der Implementierung vorgegebene 
Maximalbreite überschritten - und das ist bei Multiplikationen schnell 
der Fall - dann dürfte nicht implizit abgeschnitten werden, denn das 
wäre ja nicht portabel. Es liefe also darauf hinaus, bei vielen 
Rechenausdrücken für einzelne Zwischenschritte jeweils explizit die 
Rechenbreite im Programm anzugeben. Also sowas wie
 x *[0..50000] y
statt
 x * y
um dem Compiler mitzuteilen, dass er nur mit diesem Wertebereich rechnen 
muss, auch wenn die Teilrechnungen hinter x und y eigentlich eher eine 
nicht vorhandene 256-Bit Multiplikation erzwängen.

Bei Operationen auf Bits gibts das ähnlich, nämlich bei Linksshifts, 
wenn die rechte Seite nicht konstant ist. Da lässt sich die 
erforderliche Rechenbreite im Programm nur schlecht statisch aus den 
Typen der Teilausdrücke ableiten, so dass auch hier der Programmierer 
bei jeder einzelnen solchen Operation explizit abgeben müsste, wie breit 
sie durchgeführt werden muss.

Alternativ wären ausschliesslich Berechnungen der Form
  a = b <op> c
und
  a = <op> c
mit Variablen a,b,c zulässig, weil sich dann aus dem Typ von a,b,c die 
Breite der Berechnung ableiten liesse. Komplexere Rechenausdrücke 
müssten entsprechend zerlegt werden. Also nix mit a = b * c - d.

Viel Spass.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Possetitjel schrieb:
> Oder möchtest Du im (leicht modifizierten) Beispiel:
>   for (int i = 300; i> 0; i--) {
>     magic_smoke(i)
>   }
> auf einer 8-bit-Maschine einen Compilerfehler bekommen?

In einer Sprache, in der sich der Typ von "i" aus dem Typ der 
Initialisierung und der Verwendung ergibt, liesse sich das in diesem 
Beispiel vermeiden. Allerdings müsste dann die interne Darstellung von 
"i" in
   for (integer i = 100; i > x; i--)
      magic_smoke(i)
eigentlich vom Typ von "x" (kann -1000000 sein) und vielleicht auch von 
der Parameterdeklaration von magic_smoke abhängen. In komplexeren 
Beispielen blickt dann aber schnell niemand mehr durch.

In einem C Statement
    for (int i = 300000; i > 0; i--)
würde ich es allerdings schon vorziehen, gewarnt zu werden, wenn int nur 
16 Bits haben sollte. Du wirklich nicht?

: Bearbeitet durch User
von Possetitjel (Gast)


Lesenswert?

A. K. schrieb:

> Possetitjel schrieb:
>> var
>>   menuepunkt : integer range(1..20);
>
>> Das würde genau denselben Effekt erzielen; der Compiler
>> wäre völlig frei in seiner Entscheidung, auf welchen
>> plattform-eigenen Typ er diese Variable abbildet -- aber
>> es wäre dennoch vollständig portabel.
>
> Eine solche Spezifikation geht am Problem vorbei. Denn
> das Problem besteht nicht in der Breite der Speicherung
> von Variablen. Sondern in der Breite der Berechnung von
> Ausdrücken, die Variablen verwenden.

Nee, Moment.

Ich kann Dir folgen -- aber das sind unterschiedliche
Teilprobleme.

Wir waren zwischendurch auf die "native Integers" abge-
schweift, darauf, dass ich sie für einen Irrweg halte, und
Deine Entgegnung, dass man sie aus Performancegründen
dennoch haben will.
Meine Erwiderung DARAUF ist: Die Typsysteme gängiger
Programmiersprachen spezifizieren z.T. das Falsche. Für
eine Laufvariable ist u.U. völlig wurscht, wievel Speicher
sie benötigt -- man will einfach, dass die Kiste schnell
und richtig rechnet. Man sollte daher eine Möglichkeit
schaffen, solche Variablen nach WERTEBEREICH und nicht
nach SPEICHERBEDARF zu deklarieren.

Das bringt nämlich die gegensätzlichen Forderungen nach
Performance und Portabilität unter einen Hut: Wenn den
Programmierer der Speicherbedarf nicht interessiert,
sondern nur ein bestimmter Wertebereich gefordert wird,
dann soll er gefälligst auch den Wertebereich angeben --
und nicht die Speichergröße!

Der Unterschied meines Vorschlages zum "native integer"
ist: Der Compiler kann prüfen, ob der Zahlbereich
ausreicht, und bei Portierung auf eine andere Plattform
mit anderer native-integer-Größe einen geeigenten
non-native-integer wählen!

Das hat aber mit der hauptsächlich diskutierten Frage
nach "integer promotion", impliziter Typkonvertierung
usw. nicht direkt zu tun, das war ein Seitenast.

von F. F. (foldi)


Lesenswert?

Thomas H. schrieb:
> Zitat von einem unserer Informatiker: "Code soll für
> einen selbst übersichtlich und lesbar sein. Optimierung macht der
> Compiler".

Sollte über (und unter) jedem Tutorial stehen.

von Possetitjel (Gast)


Lesenswert?

A. K. schrieb:

> In einer Sprache, in der sich der Typ von "i" aus
> dem Typ der Initialisierung und der Verwendung ergibt,
> liesse sich das in diesem Beispiel vermeiden.

Ich möchte nicht missverstanden werden: Es ging mir
nicht darum, die üblichen Typen generell abzuschaffen
und durch die Wertebereichs-Spezifikation oder irgend
eine Art von Heuristik zu ersetzen.

Der Vorschlag war nur, ZUSÄTZLICH eine Typspezifikation
über den Wertebereich zu haben, damit der Programmierer
die (plattformunabhängige) Kontrolle über den Wertebereich
behält, die Auswahl der internen Zahldarstellung aber dem
Compiler überlassen kann.

> Allerdings müsste dann die interne Darstellung von
> "i" in
>    for (integer i = 100; i > x; i--)
>       magic_smoke(i)
> eigentlich vom Typ von "x" (kann -1000000 sein) und
> vielleicht auch von der Parameterdeklaration von
> magic_smoke abhängen.

Naja, ich hatte eigentlich Pascal vor Augen, wo diese
on-the-fly-Deklarationen m.W. nicht erlaubt sind.

> In komplexeren Beispielen blickt dann aber schnell
> niemand mehr durch.

Klar -- deswegen ja mein primitiver Vorschlag, den
gewünschten Wertebereich explizit durch Konstanten
anzugeben.

> In einem C Statement
>     for (int i = 300000; i > 0; i--)
> würde ich es allerdings schon vorziehen, gewarnt zu
> werden, wenn int nur 16 Bits haben sollte. Du wirklich
> nicht?

Naja, in meinem Universum würde es das Problem nicht
geben, weil in
1
 
2
var 
3
  pixelcount : integer range(0..300000);
das "integer" nicht für einen spezifischen Datentyp
stehen sollte, sondern als Aufforderung an den Compiler,
aus den auf der jeweiligen Plattform zur Verfügung
stehenden Datentypen einen auszuwählen, der den geforderten
Zahlbereich -- im Beispiel also 1-300'000 --  abbilden kann.

Auf einer 32bit-Maschine und Optimierung auf Geschwindigkeit
wäre das ganz sicher ein 32bit-native-integer, auf einer
16-bit-Maschine sicherlich irgend etwas anderes.

Programme profitieren auf diese Art bei Portierung
"automatisch" von einer größeren Wortbreite, ohne dass bei
kleineren Prozessoren die Gefahr von unerkannten Überläufen
besteht.

von Possetitjel (Gast)


Lesenswert?

Jobst Q. schrieb:

> Possetitjel schrieb:
>> (Auf der Siemens-S7-SPS geht das in AWL nicht so einfach;
>> es ist ein ziemlicher Krampf, dort einen Flankendetektor
>> zu formulieren, denn (NOT(alt) AND neu) funktioniert nicht.)
>
> Wieso?

Das weiss ich leider nicht mehr genau. Kann sein, dass es
nicht auf der S7, sondern der S5 war; bin nicht sicher.

Der Kernpunkt war jedenfalls, das "not(x)" durch "x xor 1"
nachgebildet werden musste. Natürlich geht das -- aber es
ist doch irgendwie Krampf. Muss das im dritten Jahrtausend
WIRKLICH noch sein?

von Karl (Gast)


Lesenswert?

Yalu X. schrieb:
> Der AVR-GCC macht das schon seit geraumer Zeit besser, zumindest bei
> Controllern mit Hardwaremultiplizierer. Johann, der auch hier im Forum
> aktiv ist, hat's gerichtet:

Anscheinend 2011, der GCC 4.6.4 scheints nicht zu können, der ist von 
2013.
1
  ldd r24,Y+1
2
  ldd r25,Y+2
3
  ldd r18,Y+3
4
  ldd r19,Y+4
5
  mov r22,r18
6
  mov r23,r19
7
  rcall __mulhi3
8
  clr r26
9
  sbrc r25,7
10
  com r26
11
  mov r27,r26
12
  std Y+5,r24
13
  std Y+6,r25
14
  std Y+7,r26
15
  std Y+8,r27

von Karl (Gast)


Lesenswert?

Possetitjel schrieb:
> Klar -- deswegen ja mein primitiver Vorschlag, den
> gewünschten Wertebereich explizit durch Konstanten
> anzugeben.

Kannst Du machen, nimmst Du Ada.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Possetitjel schrieb:
> Man sollte daher eine Möglichkeit
> schaffen, solche Variablen nach WERTEBEREICH und nicht
> nach SPEICHERBEDARF zu deklarieren.

Ist das (speziell bei Integer) nicht das Selbe? Zumindest bei mir im 
Kopf ist das so... Die Wertebereiche von 8- und 16 bit signed/unsigned 
hab ich eingebrannt, bei 32 Bit wirds schon eng ("irgendwas mit 4 
vorne") und in 99.999% der Fälle muss ich nicht nachdenken ob ich 0..255 
oder 0..65535 oder mehr brauche.

Aber wie oben schon richtig festgestellt wurde, Variablen sind nicht so 
schwierig, komplexer sind Ausdrücke. Aber auch hier habe zumindest ich 
keine Probleme mit der Art wie C das angeht. Die Fälle wo ich hier 
Fehler suchen und durch einen cast beheben musste, kann ich vermutlich 
an einer Hand abzählen.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Possetitjel schrieb:
> Aber an die heilige Kuh, die seit Jahrzehnten tradierte
> implizite Modulo-Arithmetik, hat sich wieder einmal niemand
> herangetraut. Typisch.

Erklärst du mir was du hier meinst? Bezieht sich das auf pascal? (dann 
muss ichs nicht verstehen, siehe ich & Python)

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Karl schrieb:
>> Hast du mal ein wirklich konkretes Beispiel? Also nicht so ein
>> konstruiertes wie
>
> Ich weiss ja nicht, ob Dir das konstruiert ist:
>
>
1
> void test(void) {
2
>   char a;
3
>   a = a * 4;
4
>   a = a << 2;
5
> }
6
>
>
>
1
>   ldd r24,Y+1
2
>   lsl r24
3
>   lsl r24
4
>   std Y+1,r24
5
>   ldd r24,Y+1
6
>   clr r25
7
>   sbrc r24,7
8
>   com r25
9
>   lsl r24
10
>   rol r25
11
>   lsl r24
12
>   rol r25
13
>   std Y+1,r24[code]
14
>

Wenn ich dein Beispiel so kompiliere, kommt nix raus, a wird nicht 
verwendet und deshalb komplett rausoptimiert.

ich habs aber mal leicht modifiziert:
1
char Karl (char a)
2
{
3
  a = a * 4;
4
  a = a << 2;
5
  return a;
6
}

Daraus wird bei mir:
1
Karl:
2
swap r24
3
andi r24,lo8(-16)
4
ret
5
.size  Karl, .-Karl
keine 16 bit, und optimaler code (ich denke das lässt sich auch von Hand 
nicht mehr verbessern)

Das von dir beschriebene Ergebnis kriege ich, wenn ich mit -O0 (disable 
all optimizations) kompiliere. Warum machst du das?

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Karl schrieb:
>> Der AVR-GCC macht das schon seit geraumer Zeit besser, zumindest bei
>> Controllern mit Hardwaremultiplizierer. Johann, der auch hier im Forum
>> aktiv ist, hat's gerichtet:
>
> Anscheinend 2011, der GCC 4.6.4 scheints nicht zu können, der ist von
> 2013.

Auch -O0 ? Damit löst mein avr-gcc 4.8.1 (auch 2013) das auch noch nach 
__mulhi3 auch, mit -Os (was eigentlich Standard auf AVR sein sollte) 
aber sehr wohl nach __umulhisi3 (der 32=16*16 widening multiplication)

von (prx) A. K. (prx)


Lesenswert?

Possetitjel schrieb:
> Der Vorschlag war nur, ZUSÄTZLICH eine Typspezifikation
> über den Wertebereich zu haben, damit der Programmierer
> die (plattformunabhängige) Kontrolle über den Wertebereich
> behält, die Auswahl der internen Zahldarstellung aber dem
> Compiler überlassen kann.

Das hatte ich schon verstanden. Aber du stelltest diese Datentypen 
direkt in den Kontext deiner Ablehnung von native integers und eine 
solche (sinnvolle) Typdeklaration ändert nichts daran, dass man Regeln 
benötigt, in welcher Breite sowohl damit als auch mit normalen Integers 
gerechnet werden soll.

Pascals native Integers sind kein Aspekt der Speicherung von Daten, 
sondern betreffen nur die Breite der Berechnung in Ausdrücken. Mit einer 
möglichen Wertebereichsdefinition von Variablen wirst du sie nicht los.

> Naja, ich hatte eigentlich Pascal vor Augen, wo diese
> on-the-fly-Deklarationen m.W. nicht erlaubt sind.

Solche Details sind für Grundfragen zur Typisierung von Integers 
irrelevant. In C geht das auch erst seit C99.

> stehenden Datentypen einen auszuwählen, der den geforderten
> Zahlbereich -- im Beispiel also 1-300'000 --  abbilden kann.

Damit beschreibst du wieder die Festlegung von Variablen ...

> Auf einer 32bit-Maschine und Optimierung auf Geschwindigkeit
> wäre das ganz sicher ein 32bit-native-integer, auf einer
> 16-bit-Maschine sicherlich irgend etwas anderes.

... um sofort zur Breite der Berechnung von Ausdrücken umzuschwenken. 
Denn eine 32-Bit Maschine kann (heute) Daten im RAM stets auch kleiner 
speichern, tut sich aber im Umgang mit diesen Daten evtl leichter, wenn 
sie dafür auf native integer erweitert werden.

Die sinnvolle Möglichkeit, [1..100] im Array als Byte zu speichern, in 
einer explizit definierten lokalen Variable, die im Register liegt, aber 
in voller Wortbreite, erspart nicht die Festlegung, in welche Breite man 
mit diesen Daten umgehen sollte. Und da kommen die native integers in 
Spiel.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

PS: Da war doch was... Deine Wertebereichstypen gibts in Pascal doch 
sowieso schon seit Anbeginn der Zeit.

https://www.freepascal.org/docs-html/current/ref/refsu4.html#x26-290003.1.1

von Yalu X. (yalu) (Moderator)


Lesenswert?

A. K. schrieb:
> PS: Da war doch was... Deine Wertebereichstypen gibts in Pascal doch
> sowieso schon seit Anbeginn der Zeit.

Genau.

Und auch in C gibt es mit [u]int_fast<n>_t und [u]int_least<n>_t
flexible Datentypen, mit denen auf portable Weise Wertebereiche für
Variablen festgelegt werden können, zwar nicht so feingranular wie in
Pascal, dafür kann man mit "fast" oder "least" angeben, ob man lieber
Rechenzeit oder Speicherplatz sparen möchte.

Possetitjel schrieb:
> A. K. schrieb:
>> Eine solche Spezifikation geht am Problem vorbei. Denn
>> das Problem besteht nicht in der Breite der Speicherung
>> von Variablen. Sondern in der Breite der Berechnung von
>> Ausdrücken, die Variablen verwenden.
>
> Nee, Moment.
>
> Ich kann Dir folgen -- aber das sind unterschiedliche
> Teilprobleme.

Das erste Teilproblem, um das es dir schwerpunktmäßig geht, sehe ich in
Pascal und C als weitgehend gelöst an.

Das zweite, viel schwierigere Teilproblem hat A. K. diesem Beitrag sehr
gut umrissen:

A. K. schrieb:
> Denn das Problem besteht nicht in der Breite der Speicherung von
> Variablen. Sondern in der Breite der Berechnung von Ausdrücken, die
> Variablen verwenden.
> ...

Ich kenne keine (auch keine exotische) Programmiersprache, in der dieses
Problem zufriedenstellen gelöst wäre.

Interessant ist, dass selbst eine extrem "maschinenferne" Sprache wie
Haskell neben Integer-Typen mit fester und dynamischer Bitbreite einen
plattformspezifischen Integer-Typ (Int) hat. Im Gegensatz zu C wird Int
für Rechenoperationen aber nur dann verwendet, wenn auch die Operanden
von diesem Typ sind.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Yalu X. schrieb:
> Ich kenne keine (auch keine exotische) Programmiersprache, in der dieses
> Problem zufriedenstellen gelöst wäre.

ich denke das liegt auch daran, dass dieses Problem von Der Sprache bzw. 
dem Compiler gar nicht gelöst werden kann; hier sehe ich den 
Programmierer in der Pflicht. Nur er (oder sie) kann die Wertebereiche 
abschätzen, und entsprechend reagieren.

Das zeigt sich schon bei einer einfachen Multiplikation: Die Bitbreite 
des Ergebnisses ergibt sich aus der Summe der Bitbreiten der Operanden. 
Wenn ich also zwei 16-bit-Werte multipliziere, müsste ich die Operation 
in 32 bit ausführen, um sicher keinen Überlauf zu erhalten.

Wenn ich aber weiß (woher auch immer), dass das Ergebnis immer in 16 
bit Platz finden wird, kann ich die Multiplikation in 16 bit ausführen, 
was (zB am AVR) schneller und kürzer ist.

Dieses Wissen kann mir aber keine Sprache und kein Compiler abnehmen.

von Jobst Q. (joquis)


Lesenswert?

Possetitjel schrieb:
> Jobst Q. schrieb:
>
>> Possetitjel schrieb:
>>> (Auf der Siemens-S7-SPS geht das in AWL nicht so einfach;
>>> es ist ein ziemlicher Krampf, dort einen Flankendetektor
>>> zu formulieren, denn (NOT(alt) AND neu) funktioniert nicht.)
>>
>> Wieso?
>
> Das weiss ich leider nicht mehr genau. Kann sein, dass es
> nicht auf der S7, sondern der S5 war; bin nicht sicher.
>
> Der Kernpunkt war jedenfalls, das "not(x)" durch "x xor 1"
> nachgebildet werden musste. Natürlich geht das -- aber es
> ist doch irgendwie Krampf. Muss das im dritten Jahrtausend
> WIRKLICH noch sein?

Die AWL-Anweisungen sind nicht für alle CPUs gleich, nichtmal für die 
Serien. Im Laufe der Zeit sind wohl einige dazugekommen.

Habe gerade gelesen, dass es zum Invertieren die Anweisungen INVI (16 
Bit) und INVD (32 Bit) gibt, aber evtl erst ab S7-1500.

XOW und XOD gibt es aber wohl schon länger.

von (prx) A. K. (prx)


Lesenswert?

Michael R. schrieb:
> ich denke das liegt auch daran, dass dieses Problem von Der Sprache bzw.
> dem Compiler gar nicht gelöst werden kann;

Der aus dem Unix-Universum stammende "bc" kommt dem recht nahe. ;-)
Geht halt als Interpreter einfacher.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Michael R. schrieb:
> Yalu X. schrieb:
>> Ich kenne keine (auch keine exotische) Programmiersprache, in der dieses
>> Problem zufriedenstellen gelöst wäre.
>
> ich denke das liegt auch daran, dass dieses Problem von Der Sprache bzw.
> dem Compiler gar nicht gelöst werden kann; hier sehe ich den
> Programmierer in der Pflicht.

Richtig. Man müsste dann für jede einzelne Rechenoperation den zu
erwartenden Wertebereich angeben, bspw. so:

A. K. schrieb:
> x *[0..50000] y
> statt
>  x * y

Aber das will man ja auch nicht wirklich.

A. K. schrieb:
> Der aus dem Unix-Universum stammende "bc" kommt dem recht nahe. ;-)
> Geht halt als Interpreter einfacher.

Das geht auch problemlos in kompilierten Sprachen (wird ja teilweise
tatsächlich auch gemacht), ist aber nicht unbedingt effizienzfördernd.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Yalu X. schrieb:
> Richtig. Man müsste dann für jede einzelne Rechenoperation den zu
> erwartenden Wertebereich angeben, bspw. so:
>
> A. K. schrieb:
>> x *[0..50000] y
>> statt
>>  x * y
>
> Aber das will man ja auch nicht wirklich.

Das reicht ja nicht mal!

ich konstruiere mal ein Beispiel: Länge eines Vektors der sich im 
Inneren des Einheitskreises bewegt, bei auf 255 skaliertem 
Einheitskreis. Basierend auf den Wertebereichen könnte der Vektor 360 
lang werden, praktisch ist er aber nie größer als 255.

Oder anders gesagt: Immer wenn die Variablen nicht unabhängig sind, 
können sich geringere Wertebereiche ergeben.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Yalu X. schrieb:
> Das geht auch problemlos in kompilierten Sprachen (wird ja teilweise
> tatsächlich auch gemacht),

Nur mit dynamischen Datentypen, deren Breite sich also zur Laufzeit 
entwickelt. Klar, kann man als C++ Klasse machen und tut man sicherlich 
auch. Angewandt auf alle normalen Integers der Sprache wäre das aber, 
verglichen mit dem was man hier im Forum üblicherweise als Compiler 
versteht, eher ein als Compiler getarnter Interpreter.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Michael R. schrieb:
> Das reicht ja nicht mal!
> ...
> Oder anders gesagt: Immer wenn die Variablen nicht unabhängig sind,
> können sich geringere Wertebereiche ergeben.

Genau deswegen braucht man ja die Wertebereichsangaben für die einzelnen
Rechenoperationen, für die A. K. die Syntax

  <op>[<lo>..<hi>]

vorgeschlagen hat. Für dein Beispiel würde das so aussehen:

1
x, y, length: 0..255;
2
3
length := isqrt(sqr(x) +[0..65025] sqr(y))

Damit weiß der Compiler, dass die Addition nur 16- und nicht etwa
17-bit-breit ausgeführt werden muss, wie es bei unabhängigen x und y der
Fall wäre.

Entsprechendes gilt auch für Funktionsaufrufe. Wenn der Compiler nicht
von sich aus erkennt, dass der Aufruf von sqr([0..255]) einen
Wertebereich von [0..65025] hat, müsste der obige Ausdruck
folgendermaßen ergänzt werden:

1
length := isqrt(sqr[0..65025](x) +[0..65025] sqr[0..65025](y))

Ja, irgendwann ist ein Ausdruck dann so sehr mit Wertebereichshinweisen
gespickt, dass überhaupt keiner mehr durchblickt ;-)

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Yalu X. schrieb:
> <op>[<lo>..<hi>]

ok, das hatte ich falsch verstanden.

Yalu X. schrieb:
> Ja, irgendwann ist ein Ausdruck dann so sehr mit Wertebereichshinweisen
> gespickt, dass überhaupt keiner mehr durchblickt ;-)

Richtig, und diese (fiktive) Programmiersprache würde ich dann nicht 
sooo gerne verwenden ;-)

Übrigens: Respekt, du hast mein (krudes) Beispiel besser verstanden als 
ich selbst... der Trick liegt in der Addition, für die 16 Bit 
ausreichend sind.

: Bearbeitet durch User
von Possetitjel (Gast)


Lesenswert?

Michael R. schrieb:

> Possetitjel schrieb:
>> Man sollte daher eine Möglichkeit schaffen, solche
>> Variablen nach WERTEBEREICH und nicht nach SPEICHERBEDARF
>> zu deklarieren.
>
> Ist das (speziell bei Integer) nicht das Selbe?

Für den Menschen: Fast.
Für den Compiler: Nein.

> Zumindest bei mir im Kopf ist das so...

Ich weiss... anerkannte Berufskrankheit bei Programmierern :)

> Die Wertebereiche von 8- und 16 bit signed/unsigned
> hab ich eingebrannt, bei 32 Bit wirds schon eng [...]

Nein, ich meine das anders: Wenn Du auf einer 8-bit-Maschine
einen kleinen endlichen Automaten mit - was weiss ich - 12
Zuständen programmierst, dann wirst Du für die Zustandsvariable
ein Byte wählen.
Wenn Du jetzt diesen Automaten auf einem ARM verwenden willst,
weil er super funktioniert, zwingst Du den Compiler, auch auf
dem ARM mit einem Byte zu operieren, obwohl ein 32bit-Wort viel
sinnvoller wäre.

Es gibt aber keine Möglichkeit, dem Compiler zu sagen: "Nimm
einen Dir passend scheinenden Integer-Typ, der (mindestens)
die Zahlen 1 bis 12 kennt".

Der Witz dieser Idee zeigt sich erst, wenn man sich einen
Zahlbereich 1..1000 und die Portierung in die umgekehrte
Richtung (vom ARM auf 8 bit) vorstellt: Theoretisch wäre
ein ja word (16bit) ausreichend.
Wenn es meine wertebereichsgesteuerte Typauswahl gäbe, würde
der Compiler auf dem ARM schätzungsweise ein longint wählen --
auf einer 8-bit-Maschine aber tatsächlich ein word, weil das
der kleinste Integer ist, der den Zahlbereich abdeckt.

Die Bereichsangabe 1..1000 ist vollständig portabel; dennoch
könnte vom Compiler immer der optimale Datentyp gewählt werden.

> Aber wie oben schon richtig festgestellt wurde, Variablen
> sind nicht so schwierig, komplexer sind Ausdrücke.

Kommt darauf an. Fließkomma kann außer Betracht bleiben, weil
dort die niederwertigsten Bits (und nicht die höchstwertigen)
wegfallen. Das fällt in die Zuständigkeit der Numerik.

Bitoperationen spielen auch keine Rolle, weil die keinen
Überlauf erzeugen, und wenn sie vernünftig implementiert bzw.
definiert sind, auch keine versteckte Typabhängigkeit haben.

Als Problemfälle bleiben nur die Ganzzahl-Ausdrücke.

> Aber auch hier habe zumindest ich keine Probleme mit der
> Art wie C das angeht. Die Fälle wo ich hier Fehler suchen
> und durch einen cast beheben musste, kann ich vermutlich
> an einer Hand abzählen.

Sicher eine Sache der Gewohnheit.
Ich bin der Meinung, dass
1. die Regeln EINFACH sein und
2. sich nicht dauernd ändern sollten.
Der Rest ist ziemlich wahlfrei.

von Possetitjel (Gast)


Lesenswert?

Michael R. schrieb:

> Possetitjel schrieb:
>> Aber an die heilige Kuh, die seit Jahrzehnten tradierte
>> implizite Modulo-Arithmetik, hat sich wieder einmal niemand
>> herangetraut. Typisch.
>
> Erklärst du mir was du hier meinst?

Klar.

> Bezieht sich das auf pascal?

Nee.

Jeder hält es für normal, dass bei...
1
 
2
var b : byte; 
3
... 
4
b:=255; 
5
incr(b);
... für b Null herauskommt.

Nicht nur, dass das mathematisch falsch und unter dem
Gesichtspunkt der Anwendungslogik häufig unsinnig ist, es
ist auch deshalb ärgerlich, weil der Prozessor intern den
Überlauf sehr wohl registriert, das Flag aber nicht an die
Hochsprache durchreicht.

Dass es auch anders geht, zeigen die diversen SIMD-Einheiten,
die (auch) eine Sättigungsarithmetik haben.

von Possetitjel (Gast)


Lesenswert?

A. K. schrieb:

> Das hatte ich schon verstanden. Aber du stelltest diese
> Datentypen direkt in den Kontext deiner Ablehnung von
> native integers und eine solche (sinnvolle) Typdeklaration
> ändert nichts daran, dass man Regeln benötigt, in welcher
> Breite sowohl damit als auch mit normalen Integers gerechnet
> werden soll.
>
> Pascals native Integers sind kein Aspekt der Speicherung von
> Daten, sondern betreffen nur die Breite der Berechnung in
> Ausdrücken. Mit einer möglichen Wertebereichsdefinition von
> Variablen wirst du sie nicht los.

Du hast Recht, ich zwei verschiedene Fragen vermischt.
Entschuldigung.

Mein zügelloses Wettern gegen "native integers" bezog sich
auf den "int"-Datentyp, wie er in C existiert. Während man
z.B. bei "uint8_t" gleichzeitig SOWOHL Speicherbedarf ALS
AUCH Wertebereich festlegt, legt man bei "int" außer der
Tatsache, dass es ganze Zahlen sein sollen, ÜBERHAUPT NICHTS
fest -- also Übertreibung in die andere Richtung.

Die Sachlage in Pascal ist anders; da geht es, wie Du richtig
bemerkst, um die Auswertung von Ausdrücken. Ich muss jetzt,
beim erneuten ruhigen Durchdenken, auch zugeben, dass die
implementierte Lösung einen gewissen Charme hat -- aber
ärgerlich ist sie andererseits doch, denn es entsteht eine
verdeckte Plattformabhängigkeit.

von Possetitjel (Gast)


Lesenswert?

A. K. schrieb:

> PS: Da war doch was... Deine Wertebereichstypen gibts
> in Pascal doch sowieso schon seit Anbeginn der Zeit.

Die haben natürlich bei meinem Vorschlag Pate gestanden.

Ich weiss aber nicht, ob sie genau das leisten, was ich
haben will: Ich will ja keinen NEUEN Datentyp erzeugen, der
dann Pascal-typisch wieder nur zu sich selbst zuweisungs-
kompatibel ist -- ich will, dass der Compiler aus den
EXISTIERENDEN Integertypen einen passenden auswählt.

Aus meinen antiken Pascal-Büchern kann ich nicht heraus-
lesen, wie die Teilbereichstypen im Detail funktionieren.

von Possetitjel (Gast)


Lesenswert?

Yalu X. schrieb:

> Und auch in C gibt es mit [u]int_fast<n>_t und
> [u]int_least<n>_t flexible Datentypen, mit denen
> auf portable Weise Wertebereiche für Variablen
> festgelegt werden können, zwar nicht so feingranular
> wie in Pascal, dafür kann man mit "fast" oder "least"
> angeben, ob man lieber Rechenzeit oder Speicherplatz
> sparen möchte.

Ahh... richtig, da war was. Danke für die Erinnerung;
das war mir komplett entfallen.


> A. K. schrieb:
>> Denn das Problem besteht nicht in der Breite der
>> Speicherung von Variablen. Sondern in der Breite
>> der Berechnung von Ausdrücken, die Variablen verwenden.
>> ...
>
> Ich kenne keine (auch keine exotische) Programmiersprache,
> in der dieses Problem zufriedenstellen gelöst wäre.

Ich weiss ja nicht, was für Dich "zufriedenstellend" ist.
Da Tcl überhaupt kein für den Programmierer zugängliches
Typkonzept kennt, ist in Tcl natürlich auch die Unterscheidung
verschieden langer Integers hinfällig.

von Possetitjel (Gast)


Lesenswert?

Yalu X. schrieb:

>> Oder anders gesagt: Immer wenn die Variablen nicht
>> unabhängig sind, können sich geringere Wertebereiche
>> ergeben.
>
> Genau deswegen braucht man ja die Wertebereichsangaben
> für die einzelnen Rechenoperationen, [...]

Nee... die braucht man nur für die µC.net-typische
Übertreibung... :)

Es gibt doch erstmal drei separate Probleme:
- Portabilität,
- Auswertungsregeln, die Überlauf verhindern,
- Effizienz.

Mein Ausgangspunkt war gar nicht, dass ich immer und
überall den Überlauf zuverlässig verhindern will --
es ging mir nur darum, dass nicht plattformabhängig
mal ein Überlauf entsteht und mal nicht!

Anders ausgedrückt: Ich wollte keine plattformabhängigen
Auswertungsregeln.

von Wilhelm M. (wimalopaan)


Lesenswert?

Sicher gibt es keine ideale Sprache, die diesem Problem in Gänze gerecht 
wird. Allerdings ist für mich C++ nah dran. Jeder C++-Programmierer 
lernt / sollte lernen ziemlich am Anfang, dass eine der Ideen der 
Sprache die (fast) Gleichbehandlung der primitive DT und UDT sind, mit 
dem Hintergrund, sich ein domänenspezifisches Typsystem zu erzeugen 
(nicht alles ist ein String oder ein int, es gibt Bytes, Meter, Volt, 
etc. und entspr. Operationen).

Insofern adressiere ich das Problem mit Typen wie

[c]
uint_ranged<23, 57> x;
uint_ranged_NaN<0, 19999> y;
uint_circular<0, 15> z;
[\c]

Die notwendigen unterliegenden primitiven DT werden hier bspw. aus dem 
Wertebereich bestimmt. Die Plattformabhängigkeit löst der Standard durch 
uint_fast8_t, etc.

Statische Überläufe erkennt der Compiler, Laufzeitüberläufe als 
Verlassen des Wertebereiches prüfen natürlich Assertionen (sind ja auch 
abschaltbar).

Natürlich kann man sich mehr wünschen, aber so bin ich bzgl. Sicherheit 
und Expressivität schon ein ziemliches Stück weiter als der "alles ist 
ein unsigned char" Ansatz ...

von F. F. (foldi)


Lesenswert?

Wilhelm M. schrieb:
> Allerdings ist für mich C++ nah dran.

Endlich! Ich kaufe gleich Popcorn ein, für heute Abend und die nächsten 
Wochen.
Wenn C++ ins Spiel kommt, wird es lustig.

von Wilhelm M. (wimalopaan)


Lesenswert?

F. F. schrieb:
> Wilhelm M. schrieb:
>> Allerdings ist für mich C++ nah dran.
>
> Endlich! Ich kaufe gleich Popcorn ein, für heute Abend und die nächsten
> Wochen.

Ja, mich hat es auch schon gewundert, dass das noch keiner ins Spiel 
gebracht hatte ... nun, ich muss ja meinem Ruf gerecht werden ;-)

von S. R. (svenska)


Lesenswert?

Possetitjel schrieb:
> Nein, ich meine das anders: Wenn Du auf einer 8-bit-Maschine
> einen kleinen endlichen Automaten mit - was weiss ich - 12
> Zuständen programmierst, dann wirst Du für die Zustandsvariable
> ein Byte wählen.

Nein. Für die Zustandsvariable wähle ich eine Enumeration. Die kann man 
auf einem AVR (allerdings per Compilerschalter) auch auf 8 Bit 
eindampfen und wird je nach Wertebereich automatisch vergrößert.

> Es gibt aber keine Möglichkeit, dem Compiler zu sagen: "Nimm
> einen Dir passend scheinenden Integer-Typ, der (mindestens)
> die Zahlen 1 bis 12 kennt".

Doch, und in C nennt der sich "enum" (mit benannten Werten).
Außerdem kennt C solche Datentypen wie uint_fast8_t und uint_least8_t, 
mit denen ich genau solch ein Verhalten ausdrücken kann.

> Die Bereichsangabe 1..1000 ist vollständig portabel; dennoch
> könnte vom Compiler immer der optimale Datentyp gewählt werden.

CPUs arbeiten auf Vielfachen von Bytes, und außerdem durchgängig im 
Binärsystem. Wenn ich einen Wertebereich von 1..1000 angebe, dann 
erwarte ich auch, dass dieser immer und ausnahmslos eingehalten wird.

Das kostet entweder Performance zur Laufzeit oder ich kann mit Über- 
oder Unterläufen ungültige Werte produzieren. Möchte ich nicht.

> Als Problemfälle bleiben nur die Ganzzahl-Ausdrücke.

Also genau das, wofür Computer gebaut werden - Rechenoperationen. :-)

Possetitjel schrieb:
> [Modulo-Arithmetik]
> Dass es auch anders geht, zeigen die diversen SIMD-Einheiten,
> die (auch) eine Sättigungsarithmetik haben.

Man kann dank der Modulo-Arithmetik bestimmte Dinge sehr effizient 
umsetzen, was mit sättigender Arithmetik nicht ginge. Zumal CPUs bei 
normaler Integer-Arithmetik nicht sättigen können.

Alles, was du möchtest, gibt es in höheren Sprachen, bei denen die 
Laufzeitkosten kein Problem darstellt. Im Extremfall steht dann sowas 
wie coq, bei dem jede Operation passend bewiesen werden muss (und wo die 
Laufzeitkosten wieder wegfallen).

Possetitjel schrieb:
> Mein zügelloses Wettern gegen "native integers" bezog sich
> auf den "int"-Datentyp, wie er in C existiert.

Wie es ihn auch in Pascal gibt, siehe oben.
Wie es ihn in so ziemlich jeder Programmiersprache gibt, denn wenn man 
darauf verzichtet, muss man mit arbitrary precision logic arbeiten und 
das kostet Performance und Speicher.

> Während man
> z.B. bei "uint8_t" gleichzeitig SOWOHL Speicherbedarf ALS
> AUCH Wertebereich festlegt, legt man bei "int" außer der
> Tatsache, dass es ganze Zahlen sein sollen, ÜBERHAUPT NICHTS
> fest -- also Übertreibung in die andere Richtung.

Das ist falsch. Ein "int" ist mindestens 16 Bit lang und auf der CPU 
effizient. Das sind grundsätzlich sinnvolle Randbedingungen und etwas 
völlig anderes als "nichts".

Und wie gesagt, du darfst auch mit den fastN- und leastN-Typen 
hantieren. Oder dich bei Java umschauen, wo Integer ein Objekt ist (dort 
gibt es "int" nur auf Performance-Gründen, nur signed und m.W. mit 
relativ undefinierter Breite).

Was du möchtest, ist an sich nicht verkehrt, aber es ist für 
maschinennahe/effiziente Programmierung (und für nichts anderes ist C 
entwickelt worden) aus den genannten Gründen nicht optimal. Da sind 
andere Kompromisse wichtiger.

von Possetitjel (Gast)


Lesenswert?

Wilhelm M. schrieb:

> Sicher gibt es keine ideale Sprache, die diesem
> Problem in Gänze gerecht wird. Allerdings ist für
> mich C++ nah dran.

Nun ja, ich bin dabei, C zu lernen. Zielpunkt sind
Mikrocontroller.

Wäre es tatsächlich hilfreich, statt C eine noch
wesentlich mächtigere und komplexere Sprache zu
wählen? Ich habe da Zweifel...

von F. F. (foldi)


Lesenswert?

Possetitjel schrieb:
> Wilhelm M. schrieb:
>
>> Sicher gibt es keine ideale Sprache, die diesem
>> Problem in Gänze gerecht wird. Allerdings ist für
>> mich C++ nah dran.
>
> Nun ja, ich bin dabei, C zu lernen. Zielpunkt sind
> Mikrocontroller.
>
> Wäre es tatsächlich hilfreich, statt C eine noch
> wesentlich mächtigere und komplexere Sprache zu
> wählen? Ich habe da Zweifel...

C++ ist schon klasse und ich wollte das nicht verunglimpfen. Es gab 
einmal einen Thread hier, da wurde ein Problem so über Wochen (glaube 
das ging eine Weile) zerrissen und es kamen quasi täglich neue "Götter 
der C++ Kunst" hinzu und der Thread wurde eher philosophisch (vielleicht 
übertreibe ich ein bisschen, aber deshalb das Popcorn) und war nicht 
mehr zu lesen.

von Wilhelm M. (wimalopaan)


Lesenswert?

Possetitjel schrieb:
> Wilhelm M. schrieb:
>
>> Sicher gibt es keine ideale Sprache, die diesem
>> Problem in Gänze gerecht wird. Allerdings ist für
>> mich C++ nah dran.
>
> Nun ja, ich bin dabei, C zu lernen. Zielpunkt sind
> Mikrocontroller.
>
> Wäre es tatsächlich hilfreich, statt C eine noch
> wesentlich mächtigere und komplexere Sprache zu
> wählen? Ich habe da Zweifel...

Die Sprache C++ ist zwar komplexer und wesentlich mächtiger als C, aber 
nicht notwendigerweise schwerer zu lernen. Das wird leider oft in einen 
Topf geworfen. Ja, C++ ist eine Multiparadigmensprache: imperativ / 
prozedural, objektorientiert, generisch / meta-programmatisch, 
funktional. Doch man muss nicht alles auf einmal benutzen, sondern man 
kann sich je nach Einsatzzweck das Richtige heraussuchen.

Sich auf C als Sprache oder auf den C-Anteil in C++ allein zu 
beschränken, macht m.E. keinen Sinn. In einer general-purpose Umgebung 
wie *nix/Win$$ greift man gerne auf OOP zurück, in einer eingeschränkten 
Umgebung wie bare-metal µC findet man schnell eine Kombination aus 
prozedural und meta-programmatisch hilfreich, ggf. mit einer Prise OOP.

Ich finde die Sichtweise "alles ist ein Integer oder ein hoffentlich 
null-terminiertes char-Array" sehr einengend. In meinen Augen sind 
solche SW-Konstrukte extrem schwer zu lesen, zu warten oder weiter zu 
entwickeln. Hingegen fördern die richtigen Abstraktionen die Lesbarkeit 
bzw. Expressivität und verhindern zur Compile-Zeit viele Fehler, die man 
sonst zu Laufzeit suchen muss. Zudem hilft ein reiches Typsystem dem 
Compiler bei der Optimierungsarbeit.

von (prx) A. K. (prx)


Lesenswert?

Wilhelm M. schrieb:
> Insofern adressiere ich das Problem mit Typen wie

Wobei Possetitjel damit auch den Platzbedarf optimieren wollte. Wie 
macht man das in C++? Also dass der Programmierer nicht doch wieder 
explizit den Grundtyp "uint" in "uint_ranged<23, 57>" angeben muss, egal 
ob wie hier hardcoded, oder als Parameter vom Template. Sondern der 
Compiler sich das aus dem angegeben Bereich selbst ableiten kann. Für 
die frühen C++ Versionen fällt mir da nichts sinnvolles ein.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

A. K. schrieb:
> Wilhelm M. schrieb:
>> Insofern adressiere ich das Problem mit Typen wie
>
> Wobei Possetitjel damit auch den Platzbedarf optimieren wollte. Wie
> macht man das in C++? Also dass der Programmierer nicht doch wieder
> explizit den Grundtyp "uint" in "uint_ranged<23, 57>" angeben muss, egal
> ob wie hier hardcoded, oder als Parameter vom Template. Sondern der
> Compiler sich das aus dem angegeben Bereich selbst ableiten kann. Für
> die frühen C++ Versionen fällt mir da nichts sinnvolles ein.

Mit einer Meta-Funktion, etwa so:
1
    template<uint64_t V>
2
    struct TypeForValue {
3
        using type = typename std::conditional<(V > std::numeric_limits<uint32_t>::max()), uint64_t, 
4
                              typename std::conditional<(V > std::numeric_limits<uint16_t>::max()), uint32_t,
5
                                typename std::conditional<(V > std::numeric_limits<uint8_t>::max()), uint16_t, uint8_t>::type>::type>::type;
6
    };

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