If Abfrage in C - Frage zu Verkürzung

Gast #3718273
Lesenswert?

Guten Nachmittag miteinander,
normalerweise programmiere ich meine AVRs in ASM. Irgendwo habe ich dann 
mal gelesen in C sei das alles viel bequemer. Bevor ich einen 
Programmiersprachenkrieg anzettel komme ich nun zu meiner Frage.
Es geht um folgenden Ausschnitt im GCC Tutorial hier auf der Seite:
1
i = PINA;
2
i = i & 0x01;
3
if ( i != 0 ) {test1();} 
4

5
if ( ( PINA & 0x01 ) != 0 ) { test1(); } // verkürzt 
6

7
if ( PINA & 0x01 ) { test1(); } // nochmals verkürzt

Also bei der ersten Variante der Verkürzung verzichtet man einfach auf 
die Variable i und checkt einfach ob das Byte <0 ist. Richtig?

Die zweite Variante verstehe ich jetzt nicht ganz.
PINA wird mit 0x01 geundet (Also logisch und genommen? Gemacht? Wie auch 
immer)
Woher weiß der uC jetzt in welchem Fall die Funktion ausgeführt werden 
soll? Immerhin steht nirgends, dass das Byte komplett 0 sein soll.
Gast #3718313
Lesenswert?

Das Problem dabei ist der völlig unmathematische Entwurf von C. C kennt 
keine boolean Variablen und somit muß eine Verfügung  im Stile eines 
Ordre de Mufti her: Null ist false und alles ungleich Null ist true, 
Basta.

Deine beiden Ausdrücke sind in ihrer Wirkung gleich. Der erste, 
ausdrückliche ist eigentlich der mathematisch korrekte, aber du wirst in 
deinem Leben massenweise auch den zweiten finden, weil der ein paar 
Zeichen weniger hat. Eingeschworene C-Leute mögen sowas bevorzugt, 
genauso wie sie auch den Fragezeichenoperator einem If..Else Konstrukt 
vorziehen. Nachdem der Compiler das alles durch seine Mangel geleiert 
hat, kommt in beiden Fällen der gleiche Code heraus. Die Version mit dem 
? ist bloß kryptischer und unleserlicher, weswegen sie bei vielen 
C-Programmierern den Vorzug hat.

Klaro?

W.S.
Moderator (Firma: Titel) Persönliche Seite #3718327
Lesenswert?

W.S. schrieb:
> Das Problem dabei ist der völlig unmathematische Entwurf von C.
Er ist aber sehr maschinennah:
1
if ( (PINA & 0x01) != 0 ) { test1(); } // verkürzt
ich muss nicht ein Register (PINA) mit 1 ver-unden und dann hinterher 
nochmal mit 0 vergleichen. Der Schon nach der Berechnung des Ausdrucks 
PINA&1 stehen die Flags im Prozessor richtig und können ausgewertet 
werden. Ein zusätzlicher Vergleich auf "ungleich Null" ist unnötig.

W.S. schrieb:
> Die Version mit dem ? ist bloß kryptischer und unleserlicher
Verilog kann das auch. Ich hätte da den Beitrag 
Beitrag "Re: Entscheidungsoperation IF/else oder ?" mit dem Link auf den 
Fragezeichenoperator...  ;-)
#3718335
Lesenswert?

Was ich hier einmal betonen möchte ist, dass in der Compiler hier in 
allen drei Fällen vermutlich den selben Code generieren würde (und 
Ausnahmen bestätigen die Regel).
Das bedeutet dass man in C den Code ruhig so schreiben kann, wie es am 
besten lesbar ist.
So würde z.B. wenn es die Architektur unterstützt z.B. ein 
Bittest-Befehl erzeugt werden (weiss nun nicht, ob ein AVR so was hat), 
wenn dieser Vorteile gegenüber einem "AND" Befehl hat.

(Randbedingungen:
1. der Optimizer muss aktiv sein (z.B. -O2)
2. Die Inhalt der Variable "i" wird später nicht für andere Dinge 
verwendet)

ZigZeg
Gast #3718382
Lesenswert?

Lothar Miller schrieb:
> Er ist aber sehr maschinennah:if ( (PINA & 0x01) != 0 ) { test1(); } //
> verkürzt
> ich muss nicht ein Register (PINA) mit 1 ver-unden und dann hinterher
> nochmal mit 0 vergleichen. Der Schon nach der Berechnung des Ausdrucks
> PINA&1 stehen die Flags im Prozessor richtig und können ausgewertet
> werden. Ein zusätzlicher Vergleich auf "ungleich Null" ist unnötig.

Nee, mein Lieber.
Du schreibst eben wie ein C-Programmierer, was einem mathematisch 
denkenden Zeitgenossen als schlichter Unsinn erscheint, weil man dazu 
eben die vielen "Ordres de Mufti" beachten muß, um den Hintersinn zu 
erfassen.

Ob auf einer konkreten Maschine das verunden einer Variablen mit einer 
Konstanten tatsächlich irgendwelche Flags setzt oder nicht, kann man 
allein im Manual zu der betreffenden Architektur nachlesen. In ganz 
vielen Fällen ist das so, aber aus mathematischer Sicht kommt als 
Ergebnis NUR eine Zahl heraus, die der Operation entspricht - und die 
muß man, wenn man ein logisches Ergebnis haben will, eben anschließend 
einem Vergleich unterziehen.


Und nochwas: C ist nicht "aber sehr maschinennah". Vergleiche doch mal 
die Ausdrucksmöglichkeiten von C mit der Hardware in einem kleinen 
PIC16. Dort findest du ausdrückliche Befehle zum Arbeiten mit einzelnen 
Bits. Man könnte dort schreiben

if (PortA.4) DoSomething();

( in Assembler
  BTFSC PortA,4
  CALL  DoSomeThing
)
wenn das in C so vorgesehen wäre. Ist es aber nicht. Stattdessen braucht 
man ellenlange Headerdateien, um mittels Präprozessor etwas 
vergleichbares hinzukriegen. Aber Präprozessor-Akrobatik ist was anderes 
als die Syntax von C.


W.S.
#3718402
Lesenswert?

W.S. schrieb:
> wenn das in C so vorgesehen wäre. Ist es aber nicht. Stattdessen braucht
> man ellenlange Headerdateien, um mittels Präprozessor etwas
> vergleichbares hinzukriegen. Aber Präprozessor-Akrobatik ist was anderes
> als die Syntax von C.

naja, die Header braucht es in erster Linie, damit ich statt "Bit 4" 
einen symbolischen Namen nehmen kann. Wenn ich "Bit 4" meine sage ich in 
C einfach
1
if (PORTA & (1<<4))

ZigZeg
Moderator Persönliche Seite #3718424
Lesenswert?

W.S. schrieb:
> Ob auf einer konkreten Maschine das verunden einer Variablen mit einer
> Konstanten tatsächlich irgendwelche Flags setzt oder nicht, kann man
> allein im Manual zu der betreffenden Architektur nachlesen.

Kennst du eine, bei der das nicht der Fall wäre?

C ist in erster Linie so entworfen worden, dass es die Fähigkeiten
der PDP-11-CPU bestmöglich in eine Sprache abbilden konnte, die mehr
abstrahiert als Assembler, ohne dass man dabei zwangsweise die
Effektivität einbüßen muss.  Das sieht man an den Bitoperationen
hier (zum Vergleich: Pascal als Lehrsprache hat sowas nicht¹), aber
auch an Konstrukten wie *cp++.  Hätte man derartige Konstrukte nicht
irgendwie in eine höhere Programmiersprache rübergerettet, wären die
Betriebssysteme weiter aufwändig (und fehleranfällig) in Assembler
gehackt worden.

Andererseits dürfte C natürlich heutzutage den umgekehrten Effekt auf
viele CPUs haben: man entwirft sie so, dass sich C möglichst effektiv
drauf implementieren lässt.  Das ist für den AVR dokumentiert, und
die Cortexe haben ja ebenfalls als Entwurfsziel, dass man sie komplett
ohne (expliziten) Assemblercode programmieren kann.

Sich darüber zu streiten, ob das nun gut ist oder nicht, ist ziemlich
müßig.

¹) Bevor die Programmiersprachenglaubenskrieger jetzt alle auffahren:
ja, aktuelle Pascal-Dialekte haben sowas.  Das Wirthsche Pascal, etwa
zur gleichen Zeit wie C entstanden, nur mit einem völlig anderen
Fokus (Lehrsprache vs. Betriebssystemimplementierung) hatte es jedoch
nicht.
#3718428
Lesenswert?

Lothar Miller schrieb:
> Schon nach der Berechnung des Ausdrucks
> PINA&1 stehen die Flags im Prozessor richtig und können ausgewertet
> werden.

Wenn es Flags gibt. Bei MIPS (wie PIC32) gibt es sie nicht...

> Ein zusätzlicher Vergleich auf "ungleich Null" ist unnötig.

...weshalb ein bedingter Sprungbefehl zwei Register vergleicht und 
abhängig davon springt.
Persönliche Seite #3718447
Lesenswert?

Jörg Wunsch schrieb:
> W.S. schrieb:
>> Ob auf einer konkreten Maschine das verunden einer Variablen mit einer
>> Konstanten tatsächlich irgendwelche Flags setzt oder nicht, kann man
>> allein im Manual zu der betreffenden Architektur nachlesen.
>
> Kennst du eine, bei der das nicht der Fall wäre?

Mal abgesehen davon, dass es ziemlich schnurz ist, die eine Maschine 
genau verundet:  Ja, es gibt auch Machinen, bei der ein AND keine 
Flags setzt; etwa auf Maschinen, die (effektiv) kein PSW haben.  Ein 
Beispiel ist Infineon TriCore.
Moderator (Firma: Titel) Persönliche Seite #3718455
Lesenswert?

A. K. schrieb:
> ...weshalb ein bedingter Sprungbefehl zwei Register vergleicht und
> abhängig davon springt.
Das weiß der Compiler und fügt diesen Vergleich dann eigenhändig ein.

W.S. schrieb:
> Man könnte dort schreiben
> if (PortA.4) DoSomething();
Solche prozessorabängigen Erweiterungen gabs schon für den 8051. Und der 
Lohn? Bei einem Prozessor- oder nur Compilerwechsel viel Handarbeit.

> ( in Assembler
>   BTFSC PortA,4
>   CALL  DoSomeThing
> )
> wenn das in C so vorgesehen wäre. Ist es aber nicht.
Ein brauchbarer C-Compiler macht aber genau das aus dem "Maskencode".

Welche Sprache ist deiner Meinung nach geeigneter und/oder orthogonaler?
#3718461
Lesenswert?

Jörg Wunsch schrieb:
> Kennst du eine, bei der das nicht der Fall wäre?

Klassische Statusregister mit Flags sind mitnichten selbstverständlich. 
Weil Flags singuläre Ressourcen sind, die im Highend-Bereich erheblichen 
Aufwand mit sich bringen.

Neben MIPS war da beispielsweise DEC Alpha eine Architektur ohne Flags. 
PowerPC wiederum hat statt dessen einen separaten Satz von 8 Registern 
für Status-Resultate, bei Intel IA64 sind es noch ein paar mehr. HPs PA 
übersprang den Folgebefehl anhängig vom Resultat eines Vergleichs.

Die x86 Implementierungen treiben grossen Aufwand, um trotz klassischem 
Statusregister nicht dauernd über die eigenen Füsse zu stolpern, dennoch 
entstehen nicht selten unerwünschte und bremsende Abhängigkeiten.

In der Anfangsphase der 8-Bit Mikroprozessoren gab es welche, die 
allenfalls ein Carry-Flag hatten, aber bei Null- oder Vorzeichentests im 
Sprung direkt den Akku überprüften (RCA 1802, NS SC/MP).
#3718931
Lesenswert?

W.S. schrieb:
> Die Version mit dem
> ? ist bloß kryptischer und unleserlicher, weswegen sie bei vielen
> C-Programmierern den Vorzug hat.

Du wirst keinen Programmierer finden, der völlig auf "if" verzichtet.
Ein guter Programmierer setzt immer jeweils den Operator ein, der für 
die konkrete Aufgabe am besten ist.

Der "?" Operator liefert im Gegensatz zum "if" einen Wert zurück. Das 
kann mit "if" dann wirklich sehr umständlich und kompliziert werden und 
auch der erzeugte Code wird größer und langsamer.

Sind dagegen nur verschiedene Anweisungen auszuführen, wird man dafür 
kaum den "?" Operator benutzen.
Moderator Persönliche Seite #3718934
Lesenswert?

Peter Dannegger schrieb:
>> ? ist bloß kryptischer und unleserlicher, weswegen sie bei vielen
>> C-Programmierern den Vorzug hat.
>
> Du wirst keinen Programmierer finden, der völlig auf "if" verzichtet.

Dafür sehr viele, die den ternären Operator meiden wie der Teufel das
Weihwasser und nur benutzen, wenn es gar nicht anders geht.  Aber das
passt halt nicht in W. S.' Weltbild.
Moderator Persönliche Seite #3718972
Lesenswert?

W.S. schrieb:
> Die Version mit dem
> ? ist bloß kryptischer und unleserlicher, weswegen sie bei vielen
> C-Programmierern den Vorzug hat.

Im Gegenteil: Die (richtige) Anwendung des ?-Operators fördert die 
Lesbarkeit von C-Programmen. Man kann in C manches kurz und knackig 
formulieren, wofür man in anderen Sprachen eine ganze Litanei 
runtersabbeln muss.

Natürlich kann man in C sehr kryptisch programmieren. Das ist aber 
nicht unbedingt das Ziel des gemeinen C-Programmierers.

Du ratterst hier daher lediglich Vorurteile runter, die man schon in den 
70er Jahren gehört hat, als in Deutschland noch kaum jemand C überhaupt 
kannte. Wie Du am Erfolg von C erkennen kannst (immerhin werden 
mittlerweile damit ganze Betriebssysteme geschrieben), bist Du mit 
Deinen Argumenten irgendwo hinter irgendeinem Planeten auf diesem 
Sonnensystem stehengeblieben.

Was wäre denn für Dich die ideale Programmiersprache?

Hier hast Du eine klitzekleine Auswahl (Vorsicht, Humor nützlich!):

  http://web.archive.org/web/20130930062739/http://helloworldsite.he.funpic.de/hello.htm
Gast #3719032
Lesenswert?

Frank M. schrieb:
>Wie Du am Erfolg von C erkennen kannst (immerhin werden
>mittlerweile damit ganze Betriebssysteme geschrieben), bist Du mit
>Deinen Argumenten irgendwo hinter irgendeinem Planeten auf diesem
>Sonnensystem stehengeblieben.

Woher kommt denn der "Erfolg"?
Dadurch, daß an den Lehreinrichtungen nichts Anderes unterrichtet 
wird?

Es ist nicht immer das Bessere, das sich durchsetzt.
Gast #3719083
Lesenswert?

Boris schrieb:
>Ich schreibe (und lese) jedenfalls lieber....

Bis Du den Quelltext nach 3-4 Jahren mal wieder in die Hand bekommst
und Änderungen durchführen sollst.
Dann kommt der Moment, wor Du denkst: "Ach hätte ich das doch
übersichtlich geschrieben!"

Die Zeit, die Du da eingespart hattest, gibst Du dann doppelt drauf.
#3719091
Lesenswert?

Dieselwolf schrieb:
> Boris schrieb:
>>Ich schreibe (und lese) jedenfalls lieber....
>
> Bis Du den Quelltext nach 3-4 Jahren mal wieder in die Hand bekommst

Gerade dann ist das schneller, einfacher und übersichtlicher.
Der ternäre Operator ist ein Operator wie jeder andere auch. Kein Grund 
ihn nicht einzusetzen wo er Sinn macht und einem das Leben leichter 
machen kann.
Gast #3719098
Lesenswert?

Karl-Heinz behauptete:
>Gerade dann ist das schneller, einfacher und übersichtlicher.

Siehst Du: Für Dich ist die eine Variante übersichtlicher, für einen
Anderen die Andere.

Ich bin aus dem Holz: Quelltexte so ausführlich und eindeutig, wie es
die Syntax der Sprache, die man benutzen muß zulässt.

Wenn man die Wahl hat: Eine Sprache benutzen, die man selbst am 
Vernünftigsten hält.
#3719108
Lesenswert?

Dieselwolf schrieb:
> Karl-Heinz behauptete:
>>Gerade dann ist das schneller, einfacher und übersichtlicher.
>
> Siehst Du: Für Dich ist die eine Variante übersichtlicher, für einen
> Anderen die Andere.

Was findest du übersichtlicher
1
  if( a ) {
2
    if( b )
3
      func( 1, 2 );
4
    else
5
      func( 1, 4 );
6
  }
7
  else {
8
    if( b )
9
      func( 3, 2 );
10
    else
11
      func( 3, 4 );
12
  }

Die Absicht ist klar:
Wenn a gegeben ist, dann soll die Funktion mit einem Wert von 1 als 
erstem Paramater aufgerufen werden, ansonsten mit einem Wert von 3
Wenn b wahr ist, dann soll die Funktion mit einem Wert von 2 als erstem 
Paramater aufgerufen werden, ansonsten mit einem Wert von 4.

Obiges lässt sich mit dem ternären Operator schreiben als
1
  func( a ? 1 : 3,
2
        b ? 2 : 4
3
       );
und drückt kurz und präzise genau das gewünschte aus. Und zwar ohne das 
man alle möglichen Fallunterscheidungen ausformulieren muss.
Nimm noch eine dritte Bedingung mit dazu und du ertrinkst in if-s und 
else.

Man kann das natürlich schreiben als
1
    if( a )
2
      arg1 = 1;
3
    else
4
      arg1 = 3;
5

6
    if( b )
7
      arg2 = 2;
8
    else
9
      arg2 = 4;
10

11
    func( arg1, arg2 );
und hat damit 2 temporäre Variablen, die eigentlich keiner braucht. An 
die Einfachheit und Übersichtlichkeit, die durch die Verwendung des 
ternären Operators entsteht, kommt aber auch diese Variante nicht ran.

Mir kommt das eher vor, wie auf gut österreichisch:
Wozu braucht man das? Das habn wir noch nie so gemacht. Da könnt ja 
jeder kommen!

Der ternäre Operator ist ein Operator wie jeder andere auch. Er hat 
seine Bedeutung. In manchen Fällen erleichtert er das Leben. In manchen 
Fällen ist er nicht angebracht. Ja, man kann sich damit auch in den Fuss 
schiessen. Aber nur weil man sich damit einen Finger abtrennen kann, ist 
eine Kreissäge noch lange kein verteufelungswürdiges Werkzeug. Auf jeden 
Fall ist allerdings ein Taschenmesser kein Ersatz dafür, nur weil es 
damit schwerer ist, sich die Hand abzutrennen.
Moderator Persönliche Seite #3719146
Lesenswert?

Walter Tarpan schrieb:
> Frank M. schrieb:
>> Das else ist überflüssig:
>> if(isRunning)
>> {
>>   return GetNextValue();
>> }
>> return sDefaultValue;
>>
>> ;-)
>
> Ob das dann besser lesbar ist...

Finde ich schon. Am Ende einer non-Void-Funktion sollte immer ein return 
stehen. Dem wird meine Variante eher gerecht ;-)

Aber man könnte herrlich darüber streiten, ob mehrere Ausgänge innerhalb 
einer Funktion überhaupt sinnvoll sind.

> ...aber ich muß zugeben, mir ist auch gerade langweilig genug, daß ich
> darüber diskutieren würde.

Geht mir genauso ;-)
Moderator #3719211
Lesenswert?

Es ist meist ein Vorteil und selten ein Nachteil, wenn man doppelt
vorkommende Code-Passagen meidet. Für lange Passagen gilt das sowieso,
oft aber auch für kurze. Mit dem ternären Operator lässt sich doppelter
Code in vielen Fällen vermeiden.

Beispiel 1 (ohne ternären Operator, nur zur Motivation):

Schlecht:
1
  array[n+i] = array[n+i] * 2;

Gut:
1
  array[n+i] *= 2;

Denn hier sieht man sofort, dass die Quelle und das Ziel dasselbe
Array-Element ist.


Beispiel 2:

Schlecht:
1
  if(bedingung)
2
  {
3
    array[n+i] = x;
4
  }
5
  else
6
  {
7
    array[n+i] = y;
8
  }

Gut:
1
  array[n+i] = bedingung ? x : y;

Denn hier sieht man sofort, dass dem Array-Element (und nur diesem) auf
jeden Fall ein Wert zugeweisen wird. Nur die Quelle ist von der
Bedingung abhängig, nicht aber das Ziel. Deswegen sollte dieses
außerhalb des bedingt ausgeführten Codes stehen.


Bspiel 3:

Schlecht:
1
  if(bedingung)
2
  {
3
    return x;
4
  }
5
  else
6
  {
7
    return y;
8
  }

Gut:
1
  return bedingung ? x : y;

Denn hier sieht man sofort, dass die Funktion an dieser Stelle endet,
ohne Wenn und Aber. Lediglich der Rückgabewert ist an eine Bedingung
geknüpft, nicht aber die Tatsache, dass die Funktion an diese Stelle zum
Aufrufer zurückkehrt. Deswegen sollte das return außerhalb des bedingt
ausgeführten Codes stehen.

Neben der Verbesserung der Lesbarkeit und des logischen Aufbaus eines
Programms gibt es aber auch Fälle, wo der ternäre Operator sogar
zwingend erforderlich ist oder nur sehr umständlich durch andere
Konstrukte ersetzbar ist. Dazu gehört bspw. die bedingte Intialisierung
von Variablen.

Weil er so nützlich ist, gibt es ihn (teilweise mit anderer Syntax) in
den meisten gängigen Programmierprachen:

  http://en.wikipedia.org/wiki/%3F:

Einfach zu sagen "Will ich nicht, brauch ich nicht, deswegen sofort in
den Müll damit" ist deswegen ein wenig zu kurz gedacht :)
Gast #3719626
Lesenswert?

Lothar Miller schrieb:
> Ein brauchbarer C-Compiler macht aber genau das aus dem "Maskencode".

Ein guter XYZ-Compiler ist mittlerweile so schlau (genauer: seine 
Verfasser waren so schlau), daß er die hinter einer Formulierung 
steckende Absicht des Schreibers erkennt und nach Abwägung der 
Möglichkeiten der Zielhardware sich den dafür optimalen Maschinencode 
zusammenbastelt. Man sehe sich mal an, was z.B. der Keil aus einem 
C-Quellcode macht, wenn man ihn nur ordentlich optimieren läßt. Es ist 
wirklich erstaunlich.



Dieselwolf schrieb:
> Wenn man die Wahl hat: Eine Sprache benutzen, die man selbst am
> Vernünftigsten hält.

Nachtrag meinerseits: ..sofern diese Sprache für die Zielplattform 
verfügbar ist. Sonst muß man notgedrungen in irgendeinen sauren Apfel 
beißen.


Karl Heinz schrieb:
> Was findest du übersichtlicher
>   if( a ) {
>     if( b )
>       func( 1, 2 );
>     else
>       func( 1, 4 );
>   }
>   else {
>     if( b )
>       func( 3, 2 );
>     else
>       func( 3, 4 );
>   }

Zu allererst fände ich es übersichtlicher, die zugrundeliegenden Blöcke 
erkennen zu können, ohne erst nach den öffnenden Klammern suchen zu 
müssen. Schreibst du etwa immer so?

Als nächstes fände ich es übersichtlicher, sich echte Gedanken über den 
geschriebenen Code zu machen, bevor man ihn in die Tasten haut. 
Üblicherweise bauschen temporäre Variablen weniger auf als eine 
Breitseite von Funktionsaufrufen und überflüssige Klammern.

etwa so:
   if (a)
   { if (b) func(1, 2); else func(1, 4);
   }
   else
   { if (b) func(3, 2); else func(3, 4);
   }

oder etwa so:
int tmpa, tmpb;
tmpa = 3; if (a) tmpa = 1;
tmpb = 4; if (b) tmpb = 2;
func(tmpa, tmpb);

Nochmal dasselbe, diesmal von unserem Programmier-Begeisterten:
Yalu X. schrieb:
> Schlecht:
>   if(bedingung)
>   {
>     array[n+i] = x;
>   }
>   else
>   {
>     array[n+i] = y;
>   }
>
> Gut:
>   array[n+i] = bedingung ? x : y;

und noch besser:

int tmp;
tmp = y;
if (bedingung) tmp = y; //Kommentar, warum
array[beliebig komplizierte Berechnung] = tmp;

Im Grunde geht es nur um wirklich überflüssige Dinge in C. Der ? 
Operator ist de facto überflüssig und trägt nur zur Unleserlichkeit bei, 
genauso wie der Komma-Operator. Sowas wie 'typedef' übrigens auch - oder 
hat jemand dazu eine wirklich stichhaltige Begründung für 'typedef'?


W.S.
Persönliche Seite #3719637
Lesenswert?

W.S. schrieb:
> Sowas wie 'typedef' übrigens auch - oder
> hat jemand dazu eine wirklich stichhaltige Begründung für 'typedef'?

Man merkt, daß Du mit C nicht arbeitest, sondern es aus recht 
praxisferner Sicht kritisierst. Offensichtlich hast Du noch nie etwas 
mit Funktionspointern zu tun gehabt.

Deine Definition von "lesbarkeit" weicht auch stark von meiner ab.

Den C-Stil von Karl Heinz finde ich nun auch nicht besonders schön (für 
mich gehören zusammengehörige geschweifte Klammern in die gleiche 
Spalte, und nach öffnenden und vor schließenden runden Klammern gehört 
kein Whitespace, vor öffnende dann, wenn es sich nicht um einen 
Funktionsaufruf handelt) -- aber das sind rein kosmetische Differenzen, 
die sich bei C-Anwendern mit Erfahrung individuell einspielen und 
anpassen. Was die grundliegenden Ansichten zu C betrifft, sehen wir die 
Dinge recht ähnlich.

Ich verdiene mir meinen Lebensunterhalt seit bald 25 Jahren mit C, und 
ich habe schon ziemlich viel fremden C-Code gesehen, insofern bilde ich 
mir ein, ein gewisses Gefühl für Lesbarkeit von C-Code zu haben.

Unnötige Zwischenvariablen, mehrere Anweisungen in einer Zeile etc. sind 
das genaue Gegenteil von lesbarem Code. Code, der so formatiert ist, wie 
Deiner, landet bei mir automatisch in einem "Beautifier", um eine 
lesbare und halbwegs verbreiteten Konventionen übliche Formatierung zu 
erhalten.
#3720259
Lesenswert?

W.S. schrieb:

> Zu allererst fände ich es übersichtlicher, die zugrundeliegenden Blöcke
> erkennen zu können, ohne erst nach den öffnenden Klammern suchen zu
> müssen. Schreibst du etwa immer so?

Ist eine reine Konventionssache.
Wenn mein Arbeitgeber die Klammern in einer neuen Zeile will, kriegt er 
sich auch dort. Wenn ich für mich schreibe, ist das je nach Projekt 
unterschiedlich. Lange Jahre hab ich die öffnende Klammer ans Zeilenende 
geschrieben, dann wieder ein paar Jahre in eine neue Zeile. Letzten 
Endes macht es für mich wenig Unterschied. Die schliessende Klammer 
steht in der Spalte auf jeden Fall unter der 'Anweisung' in der die 
öffnende steht. Ob da jetzt die öffnende Klammer alleine steht, oder ob 
die in dieser Zeile am Zeilenende steht, ist mit persönlich mitlerweile 
egal. Ich finde in beiden Formatierungen optisch die jeweils öffnende 
Klammer recht zuverlässig - das hat sich längst automatisiert ohne dass 
ich groß darüber nachdenken muss.
Viel wichtiger ist die konsistente Einrückung.

> etwa so:
>    if (a)
>    { if (b) func(1, 2); else func(1, 4);
>    }
>    else
>    { if (b) func(3, 2); else func(3, 4);
>    }

Wer mir allerdings so etwas als 'guten Stil' unterjubeln will, den nehm 
ich nicht mehr ernst. Sorry.


Dein einziges Argument gegen den ?: Operator ist: Ein ? in einem Code 
kommt mir seltsam vor, daher verteufle ich das erst mal.
#3720309
Lesenswert?

W.S. schrieb:
> und noch besser:
>
> int tmp;
> tmp = y;
> if (bedingung) tmp = y; //Kommentar, warum
> array[beliebig komplizierte Berechnung] = tmp;

Finde ich nicht. Wie man sieht schleichen sich da auch leicht Fehler 
ein. (In der dritten Zeile meintest Du vermutlich 'x' statt 'y')
Habe ich aber erst beim 2ten lesen gesehen, es ist halt unübersichtlich.
#3720338
Lesenswert?

W.S. schrieb:
> Sowas wie 'typedef' übrigens auch - oder
> hat jemand dazu eine wirklich stichhaltige Begründung für 'typedef'?

Wie willst du sowas wie int16_t definieren, wenn du keine Typedefs hast? 
#define ist zwar im Prinzip möglich, aber ich stimme mit Stroustrup 
dahingehend überein, dass man den Präprozessor so sparsam wie möglich 
verwenden sollte. Dieser Mischmasch aus zwei sehr unterschiedlichen 
Sprachen (Präprozessor / C[++]) ist nicht sauber und hinsichtlich 
Namescopes grad in grossen Projekten recht problematisch.

Unglücklich bin ich über typedef nur, was die syntaktische Seite angeht. 
In die Grammatik passen sie nicht sauber hinein. Aber das wurde 
letztlich schon mit der Erfindung von C verbockt, wo typedefs erst 
nachträglich hinzugefügt wurden.
Gast #3720395
Lesenswert?

foo schrieb:
> Tja, jeder wie er will.
> Bei meinen Funktionen gibt es genau einen Returnpfad.
> uint8 foo(void)
> {
>     uint8 r;
>
>     if(bedingung)
>     {
>         r = 1u;
>     }
>     else
>     {
>         r = 2u;
>     }
>
>     return(r);
> }

Ja, kann man zur Religion erheben. Man kann sich auch auf den Standpunkt 
stellen, dass man erst Predicates teste (ganz modernes Zeug ...), bevor 
es im eigentliche Code ohne Beiwerk zur Sache geht.
1
uint8_t bla(int i) {
2
    // Check predecates - see function documentation for contract
3
    if(i < 0 || i > MAGIC_MAX) {
4
        return BLA_OUT_OF_RANGE;
5
    }
6

7
    if(condition != COND0 || condition == COND_STRANGE) {
8
        return BLA_COND_IN_WRONG_STATE;
9
    }
10

11

12
    //
13
    // Perform the required bla() magic
14
    //
15
    return condition ? BLA1 : BLA2;
#3720495
Lesenswert?

foo schrieb:
> Tja, jeder wie er will.
> Bei meinen Funktionen gibt es genau einen Returnpfad.
>
>
1
> uint8 foo(void)
2
> {
3
>     uint8 r;
4
> 
5
>     if(bedingung)
6
>     {
7
>         r = 1u;
8
>     }
9
>     else
10
>     {
11
>         r = 2u;
12
>     }
13
> 
14
>     return(r);
15
> }

Noch besser, weil kürzer:

1
uint8 foo(void)
2
{
3
    uint8 r = 2u;
4

5
    if(bedingung)
6
    {
7
        r = 1u;
8
    }
9

10
    return (r);
11
}


Jörg Wunsch schrieb:
> Besonders bei größeren Funktionen mit vielen Bedingungen kann das
> schnell ein ausgesprochen unübersichtliches Geschachtel werden.

Korrekt. Bei kleineren Funktionen à la get_value() kann man es aber 
schon sehr gut so machen.
Moderator Persönliche Seite #3720508
Lesenswert?

Mark Brandis schrieb:
> Noch besser, weil kürzer:
>
>
>
1
> uint8 foo(void)
2
> {
3
>     uint8 r = 2u;
4
> 
5
>     if(bedingung)
6
>     {
7
>         r = 1u;
8
>     }
9
> 
10
>     return (r);
11
> }
12
>

Suboptimal. Wenn bedingung gegeben ist, werden überflüssigerweise 2 
Zuweisungen gemacht.

(Vielleicht ist der gcc so schlau, dass er in dem obigen 
Trivial-Beispiel die 2u-Zuweisung intern in ein "else" packt. Aber davon 
würde ich nicht unbedingt ausgehen)
#3720512
Lesenswert?

Mark Brandis schrieb:
> Was soll sich da nicht lohnen? Obiges Beispiel ist extrem gut lesbar.
> Und wartbar. Und der MISRA Checker freut sich. Und überhaupt. :-)

Bei einer solchen Zwergfunktion wie oben ist es für Verständnis und 
Überblick ziemlich irrelevant. Das lohnt erst wirklich bei grösseren 
Funktionen, bei denen man sich sonst stunden/tagelang wundert, weshalb 
der Aufräumcode am Schluss der Funktion manchmal ignoriert wird.
Moderator Persönliche Seite #3720524
Lesenswert?

Robert K. schrieb:
> MISRA C 2004:
>
> 14.7 (req): A function shall have a single point of exit at the end of
> the function.

Ich muss da Jörg schon Recht geben. Wenn man das bei größeren Funktionen 
ohne wenn und aber durchhalten will, dann muss man manchmal ziemliche 
Verrenkungen machen. Man schleppt dann den Return-Code durch alle 
möglichen Fallunterscheidungen (evtl. über mehrere Ebenen) mit.

Ein vorzeitiges bedingtes return kann da schon mal Klarheit im Code 
schaffen. Danach kanns nämlich meist unbedingt (d.h. auf höchster Ebene 
in der Funktion) weitergehen.
#3720530
Lesenswert?

Frank M. schrieb:
> Robert K. schrieb:
>> MISRA C 2004:
>>
>> 14.7 (req): A function shall have a single point of exit at the end of
>> the function.
>
> Ich muss da Jörg schon Recht geben. Wenn man das bei größeren Funktionen
> ohne wenn und aber durchhalten will, dann muss man manchmal ziemliche
> Verrenkungen machen. Man schleppt dann den Return-Code durch alle
> möglichen Fallunterscheidungen (evtl. über mehrere Ebenen) mit.

Dann sind vielleicht die Funktionen auch einfach zu groß. Wer eine 
riesige Funktion hat, macht meist schon was falsch.
#3720535
Lesenswert?

Frank M. schrieb:
> Ein vorzeitiges bedingtes return kann da schon mal Klarheit im Code
> schaffen. Danach kanns nämlich meist unbedingt (d.h. auf höchster Ebene
> in der Funktion) weitergehen.

Wobei man dann mit C++ besser dran ist. Weil man Aufräumcode in 
Destruktoren lokaler Daten verfrachten kann, statt sie an jenes Ende der 
Funktion zu packen, das beim vorzeitigen C-return links liegen gelassen 
wird.
Moderator Persönliche Seite #3720549
Lesenswert?

Michael schrieb:
> Und der gcc ist natürlich - wie so ziemlich jeder Compiler der letzten
> zwanzig Jahre - clever genug, das zu optimieren.

Die Welt besteht nicht nur aus gcc.

Du hast leider keine Ahnung, wie (teilweise absichtlich!) dumm manche 
Compiler wirklich sind. Schau Dir den XC8 PIC-Compiler an. Der streut 
nicht nur überflüssige Befehle in den erzeugten Code, sondern übersetzt 
Divisionen und Modula-Operationen mit Zweierpotenzen nicht als Schiebe- 
bzw. Maskierungsbefehle, sondern fängt tatsächlich an, echte 
int16-Divisionen draus zu machen. Da wird man ja regelrecht gezwungen, 
dass man den Code cryptischer formuliert, damit dem Compiler auf die 
Sprünge geholfen werden kann.

(Aufgefallen bei der Portierung von IRSND (dem Pendant von IRMP) auf 
PIC XC8)
Moderator Persönliche Seite #3720560
Lesenswert?

A. K. schrieb:
> Wobei man dann mit C++ besser dran ist. Weil man Aufräumcode in
> Destruktoren lokaler Daten verfrachten kann, statt sie an jenes Ende der
> Funktion zu packen, das beim vorzeitigen C-return links liegen gelassen
> wird.

Da hast Du allerdings recht. Bei einem vorzeitigen return kann man schon 
mal vergessen, irgendwas wieder wegzuräumen. Und wenn man es 
tatsächlichn machen muss, dann stehts doppelt in der Funktion. Ist 
unschön und erhöht den Pflegeaufwand, stimmt.
#3720568
Lesenswert?

Ausschliesslich für kleine Mikrocontroller bestimmte Compiler sind bei 
Optimierung nicht annähernd auf dem Standard von GCC. Einerseits weil 
hinter GCC 3 Jahrzehnte Optimierung für Highend-Maschinen stecken. 
Andererseits weil zu viel unerwartete Optimierung im Kundenkreis 
unerwünscht ist.

Auch unter den Kennern der Freiheitsgrade von C sieht es beispielsweise 
nicht jeder gerne, wenn die teure Division, die er eigens vor der 
Interrupt-Sperre in den Code einstreute, vom GCC seelenruhig dorthin 
verschoben wird, wo der Quotient benötigt wird. Genau mitten in die 
gesperrte Zeit hinein.
Gast #3720572
Lesenswert?

Frank M. schrieb:
> Du hast leider keine Ahnung, wie (teilweise absichtlich!) dumm manche
> Compiler wirklich sind.

Und die eine Zuweisung bringt dich jetzt um?

Vor allem immer und überall. So daß du die Nanosekunde nicht nur an der 
einen so hochkritischen Stelle einsparen willst, sondern auch in dem 
Programmteil, der sich gerade langweilt, bis der Benutzer mal wieder was 
eingegeben hat?
#3720577
Lesenswert?

Frank M. schrieb:
> Da hast Du allerdings recht. Bei einem vorzeitigen return kann man schon
> mal vergessen, irgendwas wieder wegzuräumen. Und wenn man es
> tatsächlichn machen muss, dann stehts doppelt in der Funktion. Ist
> unschön und erhöht den Pflegeaufwand, stimmt.

Da ich mich nicht mit MISRA rumschlagen muss bin ich in solchen Fällen 
auch so frei, ab und an mal ein goto unterzubringen. Nämlich von 
Mittendrin zum  Aufräumschwanz der Funktion. Und da dort ein Label steht 
sollte dann auch der dümmste merken, dass irgendwo ein goto sein könnte.
Moderator Persönliche Seite #3720587
Lesenswert?

Michael schrieb:
> Frank M. schrieb:
>> Du hast leider keine Ahnung, wie (teilweise absichtlich!) dumm manche
>> Compiler wirklich sind.
>
> Und die eine Zuweisung bringt dich jetzt um?

Nein, das hat konkret mit der einen Zuweisung nichts zu tun. Aber mit 
der Einstellung des Programmierers zum Compiler.

Viele PC-Programme werden von Version zu Version immer langsamer. Warum? 
Weil die Programmierer immer undisziplinierter werden: "Das macht schon 
der Compiler". Oder: "Wer heute keine 4GB im PC hat, ist halt draußen!".

> Vor allem immer und überall. So daß du die Nanosekunde nicht nur an der
> einen so hochkritischen Stelle einsparen willst, sondern auch in dem
> Programmteil, der sich gerade langweilt, bis der Benutzer mal wieder was
> eingegeben hat?

Es geht nicht um eine Nanosekunde, sondern um zigtausend mal 1 
Nanosekunde.
#3720589
Lesenswert?

Frank M. schrieb:
> Viele PC-Programme werden von Version zu Version immer langsamer. Warum?
> Weil die Programmierer immer undisziplinierter werden:

Nö. Nicht auf der Ebene. Nicht solcher Kleinscheiss mördert die 
Performance, sondern hinzu kommende Funktionalität, die ihren Preis hat. 
Die Performance geht dann im Vorfeld verloren, bei Leuten, die die 
Kosten nicht rechtzeitig abschätzen können.

Wenn du im Profiling den Hotspot deines Programms gefunden hast, oder 
ihn sowieso schon kennst, dann kannst du dich sehr konkret diesem 
Bisschen Code widmen. Deshalb das ganze Programm auf die Nanosekunde zu 
tunen, zu Lasten der Wartbarkeit, ist nicht ratsam.

Frank M. schrieb:
> Es geht nicht um eine Nanosekunde, sondern um zigtausend mal 1
> Nanosekunde.

Also einige zig Mikrosekunden. Grosse Sache! ;-)
Moderator Persönliche Seite #3720596
Lesenswert?

A. K. schrieb:
> Frank M. schrieb:
>> Viele PC-Programme werden von Version zu Version immer langsamer. Warum?
>> Weil die Programmierer immer undisziplinierter werden:
>
> Nö. Nicht auf der Ebene. Nicht solcher Kleinscheiss mördert die
> Performance, sondern hinzu kommende Funktionalität, die ihren Preis hat.

Okay, ich will mich ja auch nicht an dem "Kleinscheiss" aufhängen. Auch 
wenn Kleinvieh u.U. auch Mist macht ;-)

> Die Performance geht dann im Vorfeld verloren, bei Leuten, die die
> Kosten nicht rechtzeitig abschätzen können.

Ja, wahrscheinlich hast Du mit dieser Einschätzung recht.
Gast #3720613
Lesenswert?

Frank M. schrieb:
> Viele PC-Programme werden von Version zu Version immer langsamer. Warum?
> Weil die Programmierer immer undisziplinierter werden: "Das macht schon
> der Compiler". Oder: "Wer heute keine 4GB im PC hat, ist halt draußen!".

Nein, das liegt vor allem an den verwendeten Algorithmen und 
Softwarearchitekturen, an dem verwenden von Libs wo keiner weiß was die 
tun usw..
#3720640
Lesenswert?

Frank M. schrieb:

> Daher für mich hier EOD

"End of days" mit dem Gouvernator in der Hauptrolle? Handwerklich gut 
gemachter Actionfilm, lebt hauptsächlich vom Hauptdarsteller mit etwas 
eingestreutem Mystery. Einige Lücken in der Handlung aber das ist normal 
in dem Genre. Guter Durchschnitt.
Aber hey wir sind hier doch nicht im Cineasten Forum.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren