Datum:
Die Sprache C ist berühmt/berüchtigt für ihre Gemeinheiten. Eigentlich kann einem ja in C nix passieren, solange man sich streng an die Logik hält. Aber uneigentlich hat es vermutlich fast jeden C Programmierer schon mal massiv erwischt. Es geht nicht so wie es soll, man grübelt tagelang, bis man sich dann irgendwann massiv vor den Kopf haut... Wär mal interessant, was andere aus dem Nähkästchen zu plaudern haben. Hier ist mein Beitrag: Eine Variable als uint8_t vereinbart. Eigentlich mach ich das fast immer, hab bisher noch nie eine Variable mit negativem Inhalt benötigt. Über eines hatte ich allerdings nicht nachgedacht. Wenn der Inhalt der Variablen 0 ist und es wird 1 subtrahiert... wird eine uint Variable trotzdem nicht negativ 8o
uint8_t; . . . x--; if (x < 0) blafasel(); |
bin mal gespannt auf weitere Beiträge.
Beitrag #2557042 wurde von einem Moderator gelöscht.
Datum:
for(int i=0;i<5;i++); { blafusel(i); } |
Datum:
Micha schrieb > wird eine uint Variable > trotzdem nicht negativ 8o dich wundert das eine variable ohne vorzeichen nicht negativ wird? Ich glaube das ist hier die größte merkwürdigkeit. Der compieler wird vermutlich sogar das komplette IF entfernen weil es eh nie war werden kann.
Datum:
Micha schrieb > Eine Variable als uint8_t vereinbart. Eigentlich mach ich das fast > immer, hab bisher noch nie eine Variable mit negativem Inhalt benötigt. Naja... das ist aber an sich kein C-Fallstrick, sondern ein "Nicht nachgedacht"-Fallstrick und der kann dir in jeder Sprache begegnen :D Wenn man immer alles als unsigned deklariert ohne darüber nachzudenken was später überhaupt mit der Variable geschehen soll braucht man sich nicht wundern :D Ein Klassiker in C wäre z.B. Zuweisung statt Vergleich (= statt ==).
Datum:
Oh gut ist auch:
for (int i = 0; i < n; i++) { for (int j = 0; i < m; j++) { ... |
Aus Faulheitsgründen den Code mit drumherum nochmal kopiert woanders hin und da auch nochmal den selben Fehler gesucht...
Datum:
Was geht ist zB:
extern void bar (void); unsigned foo (unsigned x) { x--; if (x + 1 == 0) bar(); return x; } |
Beitrag #2557325 wurde von einem Moderator gelöscht.
Datum:
Micha schrieb
> if (x < 0) blafasel();
Was hat -Wall -Wextra denn dazu gesagt? Sicher sowas wie:
foo.c:9:1: warning: comparison is always false due to limited range of data type |
Der mit den geschachtelten Schleifen und dem Verwechseln der Indizes ist da schon gemeiner, aber hat natürlich nicht viel mit "Fallstricke in C" zu tun.
Datum:
Ein 'gemeiner' Fallstrick in C ist für mich zb i = 018; Die führende 0-en hätte ich vor vielen Jahren gerne gehabt, um tabellenartige Ausrichtungen eben mit führenden 0-en zu kriegen. Gemeinerweise ist das dann aber eine Oktalzahl und keine Dezimalzahl.
Beitrag #2568541 wurde von einem Moderator gelöscht.
Datum:
Micha schrieb > uint8_t; > x--; > if (x < 0) blafasel(); x kann hier nie <0 werden ...
if(x) x--;
|
wo ist das Problem? :-)
Datum:
Karl Heinz Buchegger schrieb: > Ein 'gemeiner' Fallstrick in C ist für mich zb > > i = 018; Das ist ein Fehler zur Compilezeit, aber kein Fallstrick. Fallstrick wird dann
x[i] = 217;
x[i + 1] = 117;
x[i + 2] = 017;
|
;-)
Datum:
funktionsaufruf1(paramter_array[index++],parameter_array[index++]); |
Wenn zum Beispiel index=1 ist, entspricht das:
funktionsaufruf(paramter_array[2],parameter_array[1]); |
wohingegen
funktionsaufruf1(paramter_array[index++],parameter_array[index]); |
das "gefühlt richtige" Ergebnis liefert, bei index=1 wäre das dann:
funktionsaufruf(paramter_array[1],parameter_array[2]); |
Datum:
Joerg Wolfram schrieb: > funktionsaufruf1(paramter_array[index++],parameter_array[index++]); > Wenn zum Beispiel index=1 ist, entspricht das: > funktionsaufruf(paramter_array[2],parameter_array[1]); d.h. er setzt zuerst als 2. argument die variable index ein, inkrementiert sie, setzt diese dann ins erste argument ein, inkrementiert sie, und am ende ist index=3? > funktionsaufruf1(paramter_array[index++],parameter_array[index]); > das "gefühlt richtige" Ergebnis liefert, bei index=1 wäre das dann: > funktionsaufruf(paramter_array[1],parameter_array[2]); und hier macht er es umgekehrt oder wie? zuerst ins 1. argument index einsetzen, inkrementieren, dann ins 2. einsetzen und das wars? und dann ist index=2? was wie wo? warum? hä?
Datum:
Joerg Wolfram schrieb: > Wenn zum Beispiel index=1 ist, entspricht das: > > funktionsaufruf(paramter_array[2],parameter_array[1]); Es kann dem entsprechen, es kann aber genausogut anders herum sein. Auch hier: Compilerwarnungen einfach ansehen:
foo.c:6:27: warning: operation on 'index' may be undefined |
Datum:
welches Statement zuerst inkrementiert wird hängt vom Compiler (und seinen Einstellungen) ab.
Beitrag #2568610 wurde von einem Moderator gelöscht.
Datum:
Ein ähnlicher Fall zu dem schon von Joerg Wolfram genannten:
pc ist ein Zeiger auf einen String in dem Daten stehen die zeichenweise
ausgewertet werden sollen.
char * pc;
if ((pc++ == '0') && (pc++ == '2') {
strcpy(..., pc);
...
}
Funktionierte mit Microsoft C5.x einwandfrei, mit MS-C 6.0 und gleichen
build optionen nicht mehr.
Fehler:
1. Man weiss nicht wann die beiden post-increments passieren, bei MSC
6.0 wurden sie erst nach beiden Vergleichen ausgeführt, so daß beide
Vergleiche mit dem ersten Zeichen gemacht wurden.
2. Wenn der erste Teilvergleich nicht wahr ist, ist der ganze Ausdruck
unwahr und die weitere Auswertung wird abgebrochen, damit fehlt u.U das
2. increment und die weitere Auswertung des Strings ist um ein Zeichen
versetzt.
Der Fehler war nicht von mir, aber ich musste ihn finden. Leider war das
damals die Auswertung der Antwort einer Autorisierungszentrale an einen
Geldautomaten, das ganze hat damals einen Schaden von mehreren 10000 DM
verursacht.
Datum:
Udo Schmitt schrieb: > Leider war das > damals die Auswertung der Antwort einer Autorisierungszentrale an einen > Geldautomaten Aua. :-O
Datum:
Udo Schmitt schrieb: > Fehler: > 1. Man weiss nicht wann die beiden post-increments passieren, Doch, weiß man. Der &&-Operator ist ein Sequence-Point, daher ist das Verhalten sehr wohl definiert. Udo Schmitt schrieb: > bei MSC > 6.0 wurden sie erst nach beiden Vergleichen ausgeführt, so daß beide > Vergleiche mit dem ersten Zeichen gemacht wurden. Das ist dann ein Compiler-Fehler. Udo Schmitt schrieb: > 2. Wenn der erste Teilvergleich nicht wahr ist, ist der ganze Ausdruck > unwahr und die weitere Auswertung wird abgebrochen, damit fehlt u.U das > 2. increment und die weitere Auswertung des Strings ist um ein Zeichen > versetzt. Nicht "u.U.", sondern "ganz sicher". Mich würde aber nicht wundern, wenn das durchaus so gewollt war, dass wenn das erste Zeichen nicht passt, der Pointer auch nur einmal inkrementiert wird. Sonst könnte z.B. das "02" nicht in "x02" gefunden werden.
Datum:
Jörg Wunsch schrieb: > Aua. :-O Jepp, "02" hiess glaube ich "Karte gesperrt, einziehen". Leider wurde 2 mal die erste Ziffer '0' genommen und "00" hies "Alles ok". Die Software war nur auf Pilotmaschinen aber das hat eine Bande anscheinend ganz schnell rausgekriegt und innerhalb von einem Wochenende plus 1-2 Arbeitstage den Betrag mit eigentlich schon gesperrten geklauten Karten abgezockt.
Beitrag #2568675 wurde von einem Moderator gelöscht.
Beitrag #2568680 wurde von einem Moderator gelöscht.
Datum:
Udo Schmitt schrieb: > char * pc; > > if ((pc++ == '0') && (pc++ == '2') { > strcpy(..., pc); > ... > } trotzdem ... so was führt doch zu "unpredictable behaviour". Vor allem bei der Entwicklung von Geldautomatensoftware würde ich mich hüten, potentiell gefährliche Konstrukte zu verwenden. Da schreib ich lieber ne Zeile mehr... Abgesehen davon ... sowas macht man generell nicht, weil man nie sicher sein kann, was rauskommt...
Beitrag #2568687 wurde von einem Moderator gelöscht.
Datum:
Stefan Ernst schrieb: > Doch, weiß man. Der &&-Operator ist ein Sequence-Point, daher ist das > Verhalten sehr wohl definiert. Jetzt habe ich doch tatsächlich was gelernt. "Sequence point" war mir nicht geläufig, seit wann ist das im Standard? (Habe seit etwa 7 Jahren nur noch wenig mit c und c++ zu tun) Das war Microsoft C 6.0 in den späten 80er Jahren. Stefan Ernst schrieb: > Mich würde aber nicht wundern, wenn > das durchaus so gewollt war, Nein war an der Stelle definitiv nicht gewollt.
Datum:
Random ... schrieb: > so was führt doch zu "unpredictable behaviour". Warum? && ist doch ein Sequence Point?
Datum:
Random ... schrieb: > trotzdem ... so was führt doch zu "unpredictable behaviour". Der Code ist prinzipiell völlig in Ordnung so. Random ... schrieb: > Da schreib ich lieber ne > Zeile mehr... Man ist nie sicher vor Compiler-Fehler, egal wie du den Code formulierst.
Beitrag #2568698 wurde von einem Moderator gelöscht.
Datum:
Udo Schmitt schrieb: > Stefan Ernst schrieb: >> Doch, weiß man. Der &&-Operator ist ein Sequence-Point, daher ist das >> Verhalten sehr wohl definiert. > > Jetzt habe ich doch tatsächlich was gelernt. > "Sequence point" war mir nicht geläufig, seit wann ist das im Standard? > (Habe seit etwa 7 Jahren nur noch wenig mit c und c++ zu tun) > Das war Microsoft C 6.0 in den späten 80er Jahren. Soweit ich mich erinnere: Seit dem ersten ANSI C Standard. Und ich glaub, die haben das Konzept des Sequence Points damals auch von K&R übernommen.
Beitrag #2568701 wurde von einem Moderator gelöscht.
Datum:
Stefan Ernst schrieb: > Das ist dann ein Compiler-Fehler. Wofür die älteren Versionen von Microsoft-C aber durchaus berüchtigt waren. Stefan Ernst schrieb: > Der Code ist prinzipiell völlig in Ordnung so. Nö, denn das fehlende 2. Inkrement (wenn der erste Teil falsch ist) war ja offenbar unbeabsichtigt.
Datum:
Udo Schmitt schrieb: > "Sequence point" war mir nicht geläufig, seit wann ist das im Standard? > (Habe seit etwa 7 Jahren nur noch wenig mit c und c++ zu tun) Seit wann genau kann ich nicht sagen, aber in ANSI-C ist jedenfalls schon drin. Udo Schmitt schrieb: > Das war Microsoft C 6.0 in den späten 80er Jahren. War mir nicht bewusst, dass von so ollen Kamellen die Rede ist (die Versions-Nummern der MS-Compiler sind mir nicht geläufig).
Datum:
Jörg Wunsch schrieb: > Stefan Ernst schrieb: >> Das ist dann ein Compiler-Fehler. > > Wofür die älteren Versionen von Microsoft-C aber durchaus berüchtigt > waren. :-) War damals nicht die Doktrin: Verwende nur MS-Compiler mit einer ungeradzahligen Versionsnummer?
Datum:
Karl Heinz Buchegger schrieb: > War damals nicht die Doktrin: Verwende nur MS-Compiler mit einer > ungeradzahligen Versionsnummer? Ich kannte es so: Turbo-C für zuverlässige Codegenerierung, MSC für einen schnellen Compiler und aggressiv (eben manchmal überaggressiv) optimierten Code.
Datum:
Karl Heinz Buchegger schrieb: > War damals nicht die Doktrin: Verwende nur MS-Compiler mit einer > ungeradzahligen Versionsnummer? Rofl. Ich muss heute abened mal nachschauen ob in meinem K&R "sequence point" ein Thema ist. Das schafft mich jetzt schon etwas das ich das komplett vergessen oder sogar nie gehört haben sollte. Also Danke an Stefan Ernst, habe wieder was gelernt :-)
Datum:
Jörg Wunsch schrieb: > Ich kannte es so: Turbo-C für zuverlässige Codegenerierung, MSC für > einen schnellen Compiler und aggressiv (eben manchmal überaggressiv) > optimierten Code. Jepp so hatten wir damals entwickelt. Wobei, war MSC 6.0 nicht der erste MS Compiler mit einer IDE? p.s. Damals wurde Dokumentation noch in cm oder Meter gemessen
Datum:
Jörg Wunsch schrieb: > Stefan Ernst schrieb: >> Der Code ist prinzipiell völlig in Ordnung so. > > Nö, denn das fehlende 2. Inkrement (wenn der erste Teil falsch > ist) war ja offenbar unbeabsichtigt. Ich habe mit dem Satz dies > trotzdem ... so was führt doch zu "unpredictable behaviour". kommentiert. Und in Bezug auf das "unpredictable behaviour" ist der Code "prinzipiell völlig in Ordnung so". Außerdem sollte das "prinzipiell" eigentlich eben genau ausdrücken, dass ich keine logische oder allgemeine Richtigkeit im weiteren Kontext meine. ;-)
Datum:
Stefan Ernst schrieb: > Random ... schrieb: >> trotzdem ... so was führt doch zu "unpredictable behaviour". > > Der Code ist prinzipiell völlig in Ordnung so. > > Random ... schrieb: >> Da schreib ich lieber ne >> Zeile mehr... > > Man ist nie sicher vor Compiler-Fehler, egal wie du den Code > formulierst. Klar, aber man kann sich sicher sein, dass der Code mit nem anderen C Compiler noch genau so funktioniert (von Compilerfehlern mal abgesehen...).
char * pc; if ((pc++ == '0') && (pc++ == '2') { strcpy(..., pc); } if(pc[0]=='0' && pc[1]=='2') { strcpy(..., &pc[2]); } |
Hab das mal zerlegt. Ist der Code überhaupt korrekt? Wie sieht der string pc aus? "00CardName" oder so? Ein schönes Beispiel für den Abbruch der Verarbeitung ist mMn die Sicherung von strings:
if(str && !strcmp(str, "foo")) { ...; } |
Beitrag #2568738 wurde von einem Moderator gelöscht.
Datum:
Udo Schmitt schrieb: > Jepp so hatten wir damals entwickelt. Wobei, war MSC 6.0 nicht der erste > MS Compiler mit einer IDE? Ende der 80-er. IDE? Die hätten wir nie angerührt! Nacktes make und PLINK zum Linken von Overlays waren angesagt. (Dazu eine höllisch komplizierte und fehlerträchtige Speicherverwaltung damit man EMS und XMS Speicher benutzen konnte). Und als das Projekt in den letzten Zügen kurz vor der Fertigstellung war, brachte Watcom den ersten Compiler raus, mit dem die ganzen Klimmzüge wegen Flat Memory Model obsolet waren :-)
Datum:
Random ... schrieb: >
> char * pc; > > if ((pc++ == '0') && (pc++ == '2') { > strcpy(..., pc); > } > > if(pc[0]=='0' && pc[1]=='2') { > strcpy(..., &pc[2]); > } > |
> Hab das mal zerlegt. Ist der Code überhaupt korrekt?
Wenn du die fehlenden Dereferenzier-* in der ersten Version noch
ergänzt.
Aber ganz gleich sind die Teile auch nicht. Wenn hinter dieser Sequenz
pc noch anderweitig benutzt wird, unterscheiden sie sich.
Datum:
Ein "beliebter" Fallstrick ist auch
int a[5]; for(int i=0; i <=5;i++) a[i]=0; |
Oder sowas:
char c[5]; strcpy(c,"12345"); |
Meist deswegen fies, weil das meist nicht immer gleich zu einen Problem führt sondern erst viel später (weil man sich u.U. dahinter stehende Variablen "zerschossen" hat).
Datum:
Udo Schmitt schrieb: > Stefan Ernst schrieb: >> Mich würde aber nicht wundern, wenn >> das durchaus so gewollt war, > Nein war an der Stelle definitiv nicht gewollt. Mal so nebenbei, dann hat es ja mit Microsoft C5.x auch nur auf Grund eines Compiler-Fehlers funktioniert.
Datum:
Karl Heinz Buchegger schrieb: > Wenn du die fehlenden Dereferenzier-* noch ergänzt. Die hatte ich schon in meinem Beispiel vergessen, sorry.
Datum:
Udo Schmitt schrieb: > Wobei, war MSC 6.0 nicht der erste > MS Compiler mit einer IDE? Möglicherweise von MS, das weiß ich nicht. Borland hatte die IDE schon mit Turbo-Pascal auf CP/M, das dürfte deutlich davor gewesen sein (Wikipedia nennt das Jahr 1983 für die Version 1.0).
Datum:
Jörg Wunsch schrieb: > Udo Schmitt schrieb: >> Wobei, war MSC 6.0 nicht der erste >> MS Compiler mit einer IDE? > > Möglicherweise von MS, das weiß ich nicht. > > Borland hatte die IDE schon mit Turbo-Pascal auf CP/M, das dürfte > deutlich davor gewesen sein (Wikipedia nennt das Jahr 1983 für die > Version 1.0). Na ja. IDE ist deutlich übertrieben :-) und war letztendes auch nur ein Abklatsch der 'IDE' von UCSD Pascal. Beides war mehr so eine Menüzeile aus der man Editor und Compiler aufrufen konnte. Plus ein wenig 'Zucker' um Projekte und damit Files zu verwalten.
Datum:
Jörg Wunsch schrieb: > Wikipedia nennt das Jahr 1983 für die > Version 1.0). Ich habe mit Turbo Pascal 3.0 begonnen das muss 1985 oder 1986 gewesen sein, etwa ein Jahr später kam Turbo Pascal 4.0 heraus, mit einer richtigen IDE mit integriertem Debugger und keinem 64K Limit mehr (TP 3.0 hat .com Dateien erzeugt). Etwa in dem Zeitraum kamen auch die Turbo C Versionen heraus. Die Letzte mit der ich gearbeitet hatte war Borland C++ 2.0 mit einem halben Meter Doku.
Datum:
Der Entwickler schrieb: > Ein "beliebter" Fallstrick ist auch > int a[5]; > > for(int i=0; i <=5;i++) a[i]=0; Jap. Wenn man seinen src aber generell so auslegt, dass man (fast) ausschliesslich mit 'i < MAX' Vergleichen arbeitet, vereinfacht man sich vieles.
Datum:
Karl Heinz Buchegger schrieb: > Na ja. IDE ist deutlich übertrieben :-) und war letztendes auch nur ein > Abklatsch der 'IDE' von UCSD Pascal. UCSD Pascal war doch ein reiner Kommandozeilencompiler, oder? Habe ich aber nie selbst in der Hand gehabt. Aber ich hatte mal einen anderen Pascal-Compiler für CP/M und auch C-Compiler, die halt alle deutlich umständlicher zu benutzen waren. Die brauchten ja teilweise eine eigene Diskette nur für das Compilersystem, die Nutzerdaten mussten also zwangsläufig auf einer zweiten stehen. Keine 2 Minuten später hatte man dann sein "hello world" compiliert ... dagegen ware Turbo-Pascal in der Tat ein Rennauto. > Beides war mehr so eine Menüzeile aus der man Editor und Compiler > aufrufen konnte. Von der Erinnerung her würde ich sagen, dass bei einem Compile-Fehler der Editor auch mit der passenden Stelle aufgerufen wurde, oder? Abgesehen vom Debugger (wirkliches symbolisches Debuggen im heutigen Sinne gab's unter CP/M noch nicht) also erstmal alle wesentlichen Grundfunktionen, die auch heute noch eine IDE bietet. Ich habe damit mal die Ansteuerung meines EPROMMers geschrieben. Anfangs komplett in Pascal, nach der Inbetriebnahme dann die innere Schleife als inline assembly. Wenn ich alle Kontakte mal putze, geht er vielleicht heute noch. :-)
Datum:
Random ... schrieb: > Klar, aber man kann sich sicher sein, dass der Code mit nem anderen C > Compiler noch genau so funktioniert (von Compilerfehlern mal > abgesehen...). Aber das kann man doch auch mit der ursprünglichen Variante (außer eben bei einem Compiler-Fehler). Nochmal ganz deutlich:
if ((pc++ == '0') && (pc++ == '2')) { |
hat kein "unpredictable behaviour".
Datum:
Der Compiler selbst hatte keine IDE. Dafür gabs dann Multiedit und Co. und der MS make nammte sich Nmake ;) Aber wenn ich mich richtig erinnere hatte der dazugehörige Codeview Debugger so etwas ähnliches wie IDE.
Datum:
Jörg Wunsch schrieb: > Karl Heinz Buchegger schrieb: >> Na ja. IDE ist deutlich übertrieben :-) und war letztendes auch nur ein >> Abklatsch der 'IDE' von UCSD Pascal. > > UCSD Pascal war doch ein reiner Kommandozeilencompiler, oder? Ich habs nur in Form eine 'IDE' kennen gelernt (auf einem Phillips P2000 - Z80 System). Das UCSD Pascal hatte damals den Charm, dass man kein Betriebssystem in dem Sinne, wie es mit CP/M kam, brauchte. > Von der Erinnerung her würde ich sagen, dass bei einem Compile-Fehler > der Editor auch mit der passenden Stelle aufgerufen wurde, oder? Weiß ich nicht mehr.
Datum:
Compiler Fehler wurden damals so angezeigt ;)
^
------------------------------+
Datum:
Hans-georg Lehnard schrieb: > Aber wenn ich mich richtig erinnere hatte der dazugehörige Codeview > Debugger so etwas ähnliches wie IDE. Das kann auch sein, schon ziemlich lange her :-)
Datum:
Ich hab das mal durch den AVR-GCC gejagt. Der Code wird vollkommen korrekt übersetzt:
char* test( char *pc, char *dest ) { b6: cf 93 push r28 b8: df 93 push r29 ba: fc 01 movw r30, r24 if ((*pc++ == '0') && (*pc++ == '2')) { bc: ec 01 movw r28, r24 be: 21 96 adiw r28, 0x01 ; 1 c0: 80 81 ld r24, Z c2: 80 33 cpi r24, 0x30 ; 48 c4: 39 f4 brne .+14 ; 0xd4 <test+0x1e> c6: 81 81 ldd r24, Z+1 ; 0x01 c8: 21 96 adiw r28, 0x01 ; 1 ca: 82 33 cpi r24, 0x32 ; 50 cc: 19 f4 brne .+6 ; 0xd4 <test+0x1e> strcpy( dest, pc); ce: cb 01 movw r24, r22 d0: be 01 movw r22, r28 d2: 04 d0 rcall .+8 ; 0xdc <strcpy> } return pc; } d4: ce 01 movw r24, r28 d6: df 91 pop r29 d8: cf 91 pop r28 da: 08 95 ret |
Man sieht auch sehr schön die geradezu panische Angst des AVR-GCC vor Postincrement. Er legt sich lieber eine Kopie an und macht zweimal ein extra ADIW. Peter
Datum:
Stefan Ernst schrieb: > Random ... schrieb: >> Klar, aber man kann sich sicher sein, dass der Code mit nem anderen C >> Compiler noch genau so funktioniert (von Compilerfehlern mal >> abgesehen...). > > Aber das kann man doch auch mit der ursprünglichen Variante (außer eben > bei einem Compiler-Fehler). > Nochmal ganz deutlich >
if ((pc++ == '0') && (pc++ == '2')) { |
hat kein "unpredictable
> behaviour".
Erhöht aber die Wahrscheinlichkeit von unpredictable behavior vor der
Tastatur deutlich. Ich hätte bei der Zeile auch erstmal nachschauen
müssen ob das definiertes Verhalten ist. Und ich mach schon ein paar
Jahre in C. Mag gültig sein aber ob das guter Stil ist?
Matthias
Datum:
Μαtthias W. schrieb: > müssen ob das definiertes Verhalten ist. Und ich mach schon ein paar > Jahre in C. Mag gültig sein aber ob das guter Stil ist? Jedesmal wenn du einen Pointerzugriff absicherst if( Pointer && Pointer->function() ) machst du nichts anderes als auszunutzen dass * in C && eine Short Circuit Evaluation machen muss * letzten Endes der && ein Sequence Point darstellt, denn natürlich muss auch if( foo() && foo()->function() ) funktionieren, wenn der Pointer durch die Funktion foo geliefert wird. while( ( c = getchar() ) && c != '\n' ) ... ist eine Variation davon, die in vielen C Büchern ziemlich bald am Anfang vorkommt. Genau das gleiche. Das ist definiert weil && ein Sequence Point ist
Beitrag #2571234 wurde von einem Moderator gelöscht.
Datum:
Was ich immer wieder nett finde:
#include <stdio.h> #define NEG(a) -a int main () { int x = 3; int y; y = NEG(-x); printf ("y = %d\n", y); } |
gcc machts seit ein paar Jahren "richtig", was andere C-Compiler daraus machen, kann man erahnen, wenn man sich den Preprocessor-Output via "gcc --traditional -E foo.c" anschaut. Gruß, Frank
Datum:
Was ist für dich richtig ? #define NEG(a) -a #define DCR(a) --a defines sind reine Textersetzung. Wenn du ein definiertes Verhalten haben willst setze Klammern #define NEG(a) (-a) #define DCR(a) (--a)
Datum:
Hans-georg Lehnard schrieb: > Wenn du ein definiertes Verhalten haben willst setze Klammern > > #define NEG(a) (-a) Dann aber auch richtig:
#define NEG(a) (-(a))
|
Peter
Datum:
Frank M. schrieb: > gcc machts seit ein paar Jahren "richtig", richtig ist in deinem Code ein Ergebnis von 2 Und eigentlich sollte das so ziemlich jeder Compiler der letzten 30 Jahre hinkriegen. Aber in einem Punkt hast du schon recht. Präprozessor-Dinge sind auch beliebte Fallstricke für Programmierer, die allzu sorglos Makros einsetzen wo sie eigentlich Funktionen nehmen sollten.
Datum:
Hans-georg Lehnard schrieb: > Was ist für dich richtig ? Wenn Du genau nachliest, hatte ich das Wort "richtig" bewusst in Anführungszeichen gesetzt. > Wenn du ein definiertes Verhalten haben willst setze Klammern Das brauchst Du mir nicht zu sagen. Ich habe dieses Beispiel aus dem "C Puzzle Book". Lesenswert, da sind viele kleine C-Rätsel drinne. Naja, ist schon 25 Jahre her, wo ich das gelesen habe.
Datum:
Ein anderer beliebter Fallstrick ist die Annahme ...
result = foo() + bar(); |
... dass die Reihenfolge der Aufrufe * zuerst foo * dann bar ist. Auswertungsreihenfolge hat nichts mit Operator Precedence zu tun! Auch im Fall
result = foo() * ( 2 + bar() )
|
ist die Reihenfolge der Aufrufe nicht definiert. Es ist keineswegs sicher gestellt, dass bar() vor foo() aufgerufen werden muss.
Datum:
Karl Heinz Buchegger schrieb: > Frank M. schrieb: > >> gcc machts seit ein paar Jahren "richtig", > > richtig ist in deinem Code ein Ergebnis von 2 Neuere Versionen von gcc sehen das anders: $ cc foo.c && ./a.out Ausgabe: y = 3 Das liegt an: $ cc -E foo.c:
...
main ()
{
int x = 3;
int y;
y = - -x;
printf ("y = %d\n", y);
}
|
Der Preprocessor baut da ein Blank zwischen die beiden Minuszeichen. Jetzt: $ cc --traditional -E foo.c:
...
main ()
{
int x = 3;
int y;
y = --x;
printf ("y = %d\n", y);
}
|
Jetzt fehlt das Blank und Dein "richtiges" Ergebnis mit dem Wert 2 würde da auch rauskommen, wenn gcc sich nicht weigern würde, traditional-Code zu compilieren. Ich weiß nicht, wann das im gcc geändert wurde, muss irgendwann in den Neunzigern gewesen sein. Gruß, Frank
Datum:
Frank M. schrieb: >> richtig ist in deinem Code ein Ergebnis von 2 > > Neuere Versionen von gcc sehen das anders: Ach shit. Ja, da hat der gcp recht. In ISO C basiert die Ersetzung auf einer Tokenisierung. Wir sagen zwar oft, dass der Preprocessor 'nur Textersetzung' macht, aber eigentlich stimmt das schon lange nicht mehr. Das ist so ein Fall, bei dem es einen Unterschied macht. ein anderer #define foo() baz foo()bar wird zu baz bar und nicht zu bazbar Bei der Ersetzung bleiben die Tokens erhalten. Vor der Ersetzung gab es 2 Tokens "foo()" und "bar" und danach sind es immer noch 2 Token. Da hab ich wohl zu schnell gebrüllt :-)
Datum:
Karl Heinz Buchegger schrieb: > Ja, da hat der gcp recht. > In ISO C basiert die Ersetzung auf einer Tokenisierung. [...] Dann baut Microsoft Visual C++ 2008 immer noch Mist. Ich habe es eben mal getestet: Hier wird 2 ausgegeben. Und auch MS Visual C++ 2010 gibt 2 aus. Auch andere UNIX-Compiler beharren auf der 2. gcc ist der einzige, der mich eines anderen belehrt hat. > #define foo() baz > > foo()bar > > wird zu > > baz bar > > und nicht zu > > bazbar Gerade mit -traditionl getestet. Hier setzt der Preprocessor immer ein Blank ein. Das hat wohl damit zu tun, dass es nun ein Makro und keine Konstante ist. > Da hab ich wohl zu schnell gebrüllt :-) Wie gesagt: Die anderen Compiler, die ich kenne, geben auch heute noch 2 als Ergebnis aus. "richtig" kann man so oder so werten ;-)
Datum:
Hier noch ein schönes Problem, das ich Ende der 80er hatte, als ich den Gosling-Emacs (ist ein Text-Editor, Source stammte ursprünglich von BSD) auf Unix-System-V portieren wollte: Der portierte Emacs zeigte keine Zeilenumbrüche an. Selbst beim Debuggen konnte man sehen, dass einfach C-Code komplett übersprungen wurde... bis ich endlich nach langem Suchen herausfand, dass eine vermeintliche Funktion tatsächlich ein Preprocessor-Makro war, der in einem der dutzenden Include-Dateien versteckt war... Ich kann das hier nicht mehr wortwörtlich wiedergeben. Daher schreibe ich hier stellvertretend mal einen Ersatz-Code, welcher den Fallstrick verdeutlichen soll:
main ()
{
int columns = 80;
int x = 4711;
if (columns > 80)
check_line (x);
else
printf ("columns <= 80\n"); // Diese Zeile wird NICHT ausgegeben!
}
|
Rätselhaft? Nicht wenn man das Makro dazu kennt:
#define check_line(a) if (a > 0) a = 0 |
Was lernt man daraus? Man sollte in Makros nicht nur die Argumente klammern, sondern am besten auch das ganze Makro in geschweifte Klammern setzen, also:
#define check_line(a) { if (a > 0) a = 0; } |
Da ich in der Vergangenheit viel im Team programmiert habe und mich nicht immer auf den Code von "Kollegen" verlassen konnte, habe ich mir seitdem angewöhnt, if-Statements immer als Blöcke zu schreiben, auch wenn nur eine Zeile nach dem if folgt. Also:
if (columns > 80)
{
check_line (x);
}
else
{
printf ("columns <= 80\n"); // Diese Zeile wird NICHT ausgegeben!
}
|
Und so halte ich das heute noch. Ich nenne das "defensives Programmieren". Gruß, Frank P.S. Die Lösung, warum der Gosling-Emacs unter BSD die Zeilen korrekt umbrach und unter Unix-System-V nicht, war schnell gefunden: Jemand, meinte, über den Code einen automatischen "C-Beautifier" laufen lassen zu müssen, damit der Code lesbarer und damit portabler wird. Dieser Automatismus hatte die geschweiften Klammern nach dem if (columns > 80) entfernt.
Datum:
Karl Heinz Buchegger schrieb: > i = 018; > > Gemeinerweise ist das dann aber eine Oktalzahl und keine Dezimalzahl. Bist du dir ganz sicher, dass ausgerechnet dies eine Oktalzahl ist?
Datum:
Frank M. schrieb: > Was lernt man daraus? Man sollte in Makros nicht nur die Argumente > klammern, sondern am besten auch das ganze Makro in geschweifte Klammern > setzen, also: > > #define check_line(a) { if (a > 0) a = 0; } Auch damit setzt du dich ggf. noch in die Tinte: if (...) check_line(a); else ...sonstwas... Sieht perfekt aus, aber der Compiler hat dazu seine eigene Ansicht. Wer es mit sowas wirklich ernst meint, der verwendet #define check_line(a) do{ if (a > 0) a = 0; }while(1)
Datum:
Frank M. schrieb: > Da ich in der Vergangenheit viel im Team programmiert habe und mich > nicht immer auf den Code von "Kollegen" verlassen konnte, habe ich mir > seitdem angewöhnt, if-Statements immer als Blöcke zu schreiben, auch > wenn nur eine Zeile nach dem if folgt. Bei uns in der Firma hat das zu den normalen coding style guide gehört. Unter anderem verhindert das auch dumme Fehler, die man macht wenn man spät abends noch was fixen will:
if(a > 10) do_something(); do_something_else(); |
wird beim debuggen zu:
if(a > 10) do_something(); printf("Doing something..."); do_something_else(); |
oder
if(a > 10) first_do_another_thing(); do_something(); do_something_else(); |
Wenn man wach ist fällt einem sowas natürlich schnell auf, aber wenn abends die Server nicht laufen und alles was man ausprobiert nur Unsinn macht, dann ist es sinnvoll den Code so narrensicher wie möglich zu haben...
Datum:
A. K. schrieb: > Karl Heinz Buchegger schrieb: > >> i = 018; >> >> Gemeinerweise ist das dann aber eine Oktalzahl und keine Dezimalzahl. > > Bist du dir ganz sicher, dass ausgerechnet dies eine Oktalzahl ist? Hehe, der war gut :-) foo.c:7:13: error: invalid digit "8" in octal constant
Datum:
Wer mal versucht hat, den Fähigkeiten des C Präprozessors mit Tokenisierung, # und ## Operatoren etc. komplett auszuloten, der wird beim nächsten Compiler feststellen, dass bei diesem Aspekt der Compiler die genaue Einhaltung des Standards schwächer ist als in den meisten anderen Stellen.
Datum:
Verwirrter Anfänger schrieb: > Bei uns in der Firma hat das zu den normalen coding style guide gehört. Sehe ich auch so. Geschweifte Klammern sind ein absolutes Muss. Schade, dass Karl Heinz dies bei seinen C-Beispiel-Hilfen (die ich für bewundernswert halte) das nicht konsequent durchhält. Gerade Anfänger sollte man direkt von Anfang an "erziehen". > Wenn man wach ist fällt einem sowas natürlich schnell auf, aber wenn > abends die Server nicht laufen und alles was man ausprobiert nur Unsinn > macht, dann ist es sinnvoll den Code so narrensicher wie möglich zu > haben... Absolutes ACK.
Datum:
A. K. schrieb: > #define check_line(a) do{ if (a > 0) a = 0; }while(1) Natürlich so: #define check_line(a) do{ if (a > 0) a = 0; }while(0)
Datum:
A. K. schrieb: > Natürlich so: > #define check_line(a) do{ if (a > 0) a = 0; }while(0) Ich weiß, worauf Du hinauswillst, aber das zu lesen ist einfach Sch... ;-) Wenn man konsequent im eigentlichen Quellcode geschweifte Klammern verwendet, kann es einem egal sein, wie das Makro aussieht. Und das war eigentlich die Intention meines Beitrags.
Datum:
Frank M. schrieb: >> #define check_line(a) do{ if (a > 0) a = 0; }while(0) > > Ich weiß, worauf Du hinauswillst, aber das zu lesen ist einfach Sch... Kannst ja den alten Trick verwenden: #define MACRO_BEGIN do{ #define MACRO_END }while(0) #define check_line(a) MAKRO_BEGIN if (a > 0) a = 0; MAKRO_END und schon sieht es wieder wunderschön aus. ;-)
Datum:
Frank M. schrieb: > Verwirrter Anfänger schrieb: > >> Bei uns in der Firma hat das zu den normalen coding style guide gehört. > > Sehe ich auch so. Geschweifte Klammern sind ein absolutes Muss. Schade, > dass Karl Heinz dies bei seinen C-Beispiel-Hilfen das nicht > konsequent durchhält. Das liegt daran, dass ich strikt gegen solche Makro-Schweinereien bin, wie du sie hier aufgeführt hast. :-) Ne, im Ernst. Ich bin noch nie in dieses Problem gelaufen, dass ich mir mit einem Makro das if-Nesting verbogen habe. Wahrscheinlich muss ich mir da erst mal die Finger drann verbrennen. Aber wie gesagt: solche Schweiniereien mach ich grundsätzlich nicht mit und wenn, dann nur mit entsprechender Absicherung durch die do-while {} Geschichte. Ich bin auch ganz starker Verfechter der Konvention: Makros werden grundsätzlich und immer in Grossbuchstaben geschrieben. Und umgekehrt - ein Name in dem nur Grossbuchstaben vorkommen ist immer ein Makro. Bei sowas #define check_line(a) ..... würde ich eine Revolte anzetteln :-) Ein anderer Grund ist, dass ich jetzt seit vielen Jahren beruflich praktisch nur noch C++ mache und da gibt es für derartige Dinge immer bessere Möglichkeiten als ein Makro. D.h. die Verwendung von Makros ist (bis auf ab und zu mal eine #define Konstante und ein paar #if) praktisch bei 0 angekommen.
Datum:
A. K. schrieb: > Kannst ja den alten Trick verwenden: > #define MACRO_BEGIN do{ > #define MACRO_END }while(0) Gefällt mir schon besser! :-) Das weckt die Erinnerungen an den uralten Source der Bourne-Shell (ja, vom berühmten Stephen R. Bourne). Er mochte C wohl auch nicht so recht, denn seine "Lieblingssprache" war Algol, die er damals selbst mitentwickelt hat. Aus diesem Grund finden man in seinen C-Sourcen der Shell so etwas: #define IF if( #define THEN ){ #define ELSE } else { #define ELIF } else if ( #define FI ;} Das galt auch für while, switch usw. Sämtliche Macros kann man hier nachlesen: http://minnie.tuhs.org/cgi-bin/utree.pl?file=V7/us... Da sieht "C" plötzlich ganz anders aus ;-)
Datum:
Karl Heinz Buchegger schrieb: > #define check_line(a) ..... > > würde ich eine Revolte anzetteln :-) Die AVR Include-Files strotzen nur so vor kleingeschriebenen Macros. Peter
Datum:
Peter Dannegger schrieb: >> würde ich eine Revolte anzetteln :-) > > Die AVR Include-Files strotzen nur so vor kleingeschriebenen Macros. :-) Ich meinte hier bei mir in der Firma.
Datum:
Karl Heinz Buchegger schrieb: > Das liegt daran, dass ich strikt gegen solche Makro-Schweinereien bin, > wie du sie hier aufgeführt hast. :-) Das sehe ich auch so. Ich mag auch keine Makro-Schweinereien. > Ich bin auch ganz starker Verfechter der Konvention: Makros werden > grundsätzlich und immer in Grossbuchstaben geschrieben. Und umgekehrt - > ein Name in dem nur Grossbuchstaben vorkommen ist immer ein Makro. Bei > sowas Konventionen sind immer gut, gerade in C. Auch ich halte mich an die Großbuchstaben-Konvention bei Konstanten und Makros. Aber leider kann man sich nicht immer darauf verlassen, wenn man Sources von anderen Leuten weiterbearbeiten muss - leider. > #define check_line(a) ..... > > würde ich eine Revolte anzetteln :-) Wie Peter schon schrieb: kleingeschriebene Makros findet man zuhauf... leider.
Datum:
Naja, die Macros in den Compiler-Includes sollten schon einigermaßen störfest sein. Ich wüßte da jetzt keine ernsthaften Probleme. Der größte Fallstrick beim AVR-GCC war für mich die volatile Falle. Da hab ich lange Zeit gebraucht, um mich daran zu gewöhnen. Und die char-Falle, wenn man in Strings numerische Werte eingebettet hat und diese dann unerwünscht signed expandiert werden. Leider meckert der AVR-GCC rum, wenn man zur Vermeidung dieser Falle die Strings (UART-Puffer) unsigned definiert. Ich tus trotzdem (lieber meckern als falschen Code) und trickse ihn aus (void*). Ansonsten sagt mir die sparsame Syntax schon sehr zu. Ich mag es auch, wenn sich Sprachen sehr logisch verhalten. Peter
Datum:
Ich musste vor langer Zeit den Unterschied zwischen Pointer- und Arraydeklarationen lernen, und zwar bei mehreren Sourcedateien (war ein AVR-Projekt): In einer Sourcedatei hatte ich einen globalen Puffer:
char buf [BUFSIZE]; |
In anderen Sourcedateien hatte ich folgende Deklaration, um auf den Puffer zuzugreifen:
extern char* buf; |
Nur ist das halt nicht dasselbe wie:
extern char buf[]; |
Mein Programm lief nicht, der gcc spuckte keinen Fehler aus. Nach Betrachten des Assemblercodes kam dann der große Aha-Moment.
Datum:
Xenu schrieb: > In anderen Sourcedateien hatte ich folgende Deklaration, um auf den > Puffer zuzugreifen: > extern char* buf; > Nur ist das halt nicht dasselbe wie: > extern char buf[]; Warum nicht? Ich verwende es ohne Probleme, z.B.:
float * fp;
fp = ...
fp[cal_no] = fval;
|
Ein Fehler kann nur dann entstehen, wenn man den Zeiger auf das Ursprungs-Array zu ändern versucht. Aber das ist dann ein Fehler des Programmierers. Peter
Datum:
Peter Dannegger schrieb: > Xenu schrieb: >> In anderen Sourcedateien hatte ich folgende Deklaration, um auf den >> Puffer zuzugreifen: >> extern char* buf; >> Nur ist das halt nicht dasselbe wie: >> extern char buf[]; > > Warum nicht? An dieser Stelle bedeutet es tatsächlich was anderes. buf ist nun mal keine Variable in der eine Adresse drinnen steht. Dieser Fehler ist die Manifestation des Ausspruchs "Ein Array ist kein Pointer". (Du hast vielleicht übersehen, dass von globalen Variablen die Rede ist)
Datum:
Peter Dannegger schrieb: > Warum nicht? > Ich verwende es ohne Probleme, z.B.: $ cat foo.c
#include <string.h> char buf[20]; extern void print_global_string (void); int main () { strcpy (buf, "hello, world"); print_global_string (); return 0; } |
$ cat bar.c
#include <stdio.h> extern char * buf; void print_global_string (void) { printf ("%s\n", buf); } |
$ cc foo.c bar.c -o foo && ./foo Segmentation fault Und nu?
Datum:
Danke für die Erklärung. Bei der Deklaration ist es ein Unterschied. Nur bei der Verwendung oder als Parameter ist es egal. Zum Fehlervermeidung sollte man daher auch immer das eigene Include mit in das jeweilige C-File einbinden. Dann kriegt man bei Unterschieden einen Error:
MAIN.C:6: error: conflicting types for 'buf' MAIN.C:4: error: previous declaration of 'buf' was here |
Peter
Datum:
Die automatische Typumwandlung kann oft auch eine Falle sein:
int test() { int a = -60; unsigned int b = 20; a /= b; return a; } |
-60 / 20 = 3273 Besser wäre daher eine Warnung bei Typumwandlungen, die Informationen verlieren (Wertebereich oder Vorzeichen). Peter
Datum:
Peter Dannegger schrieb: > Besser wäre daher eine Warnung bei Typumwandlungen, die Informationen > verlieren (Wertebereich oder Vorzeichen). :) Den gleichen Gedanken hatte ich gerade in einem anderen Thread: Beitrag "Re: 16bit multiplikation mit 8bit controller" Edit: Rolf Magnus mat mich netterweise darauf hingewiesen, dass in GCC diese Warnung vorgesehen ist, man muss sie mur mit -Wconversion aktivieren: Beitrag "Re: 16bit multiplikation mit 8bit controller"
Datum:
Frank M. schrieb: > Da sieht "C" plötzlich ganz anders aus ;-) Mit #define TRUE (-1) aber zur Laufzeit auch. ;-)
Datum:
format c: LOL (OK, off Topic ;-) )
Datum:
A. K. schrieb: > Mit > #define TRUE (-1) > aber zur Laufzeit auch. ;-) Ich habe mich auch schon gefragt, was er damit bezweckt hat. Hast Du eine Idee?
Datum:
Frank M. schrieb: > A. K. schrieb: >> Mit >> #define TRUE (-1) >> aber zur Laufzeit auch. ;-) > > Ich habe mich auch schon gefragt, was er damit bezweckt hat. Hast Du > eine Idee? Ein Byte mit lauter 1-Bit als Gegensatz zu einem mit lauter 0-Bits. Aber wenn du mich fragst, was das bringt: keinen blassen Schimmer. Vielleicht hat es ihm einfach nur besser gefallen, wenn er sich 'boolsche' Variablen im Debugger angesehen hat, denen er TRUE explizit zugewiesen hat. Vielleicht hat ihm auch ~ als Negierung besser gefallen, als ein schnödes ! if( ~Wert == TRUE )
Datum:
Peter Dannegger schrieb: > Karl Heinz Buchegger schrieb: >> #define check_line(a) ..... >> >> würde ich eine Revolte anzetteln :-) > > Die AVR Include-Files strotzen nur so vor kleingeschriebenen Macros. > > > Peter assert() ist auch eins - und das ist nicht nur bei AVR vorhanden :)
Datum:
Karl Heinz Buchegger schrieb: > Ein Byte mit lauter 1-Bit als Gegensatz zu einem mit lauter 0-Bits. Ich würde sogar sagen: Ein Word mit lauter 1-Bit, die Shell hat er wohl auf einer PDP11 geschrieben ;-) > Aber wenn du mich fragst, was das bringt: keinen blassen Schimmer. > Vielleicht hat es ihm einfach nur besser gefallen, wenn er sich > 'boolsche' Variablen im Debugger angesehen hat, denen er TRUE explizit > zugewiesen hat. Kann gut sein, 16 Einsen (bzw. 0177777 oktal) stechen einem da schon mehr ins Auge. Damals gab es ja den Typ "bool" in C noch nicht. Vermutlich wollte er damit den Wert von TRUE von den "allgemeinen" Integer-Zahlen abheben. > Vielleicht hat ihm auch ~ als Negierung besser gefallen, als ein > schnödes ! > > if( ~Wert == TRUE ) Wenn ich ein #define NOT ~ in seinen Macros gefunden hätte, würde ich Dir das glatt abnehmen ;-)
Datum:
Kann es sein, dass ein Laden von -1 in ein PDP11-CPU-Register direkt ein Carry-Bit o.ä. gesetzt hat? Dann wäre ein if (x == TRUE) vom C-Compiler sehr effizient umzusetzen...
Datum:
A. K. schrieb: > Frank M. schrieb: > >> Was lernt man daraus? Man sollte in Makros nicht nur die Argumente >> klammern, sondern am besten auch das ganze Makro in geschweifte Klammern >> setzen, also: >> >> #define check_line(a) { if (a > 0) a = 0; } > > Auch damit setzt du dich ggf. noch in die Tinte: > if (...) > check_line(a); > else > ...sonstwas... > Sieht perfekt aus, aber der Compiler hat dazu seine eigene Ansicht. > > Wer es mit sowas wirklich ernst meint, der verwendet > #define check_line(a) do{ if (a > 0) a = 0; }while(1) Meiner Meinung nach einer der wenigen Fälle, wo man mal den ?-Operator benutzen kann. #define check_line(a) ( (a) > 0 ? (a) = 0 ) aber check_line würde ich nichts desto trotz komplett groß schreiben ;-)
Datum:
Simon K. schrieb: > Meiner Meinung nach einer der wenigen Fälle, wo man mal den ?-Operator > benutzen kann. > > #define check_line(a) ( (a) > 0 ? (a) = 0 ) Geht nicht. Was sollte denn von diesem Konstrukt der Wert sein, wenn a nicht größer als 0 ist? Der dritte Operand ist nicht optional.
Datum:
Rolf Magnus schrieb: > Simon K. schrieb: >> Meiner Meinung nach einer der wenigen Fälle, wo man mal den ?-Operator >> benutzen kann. >> >> #define check_line(a) ( (a) > 0 ? (a) = 0 ) > > Geht nicht. Was sollte denn von diesem Konstrukt der Wert sein, wenn a > nicht größer als 0 ist? Der dritte Operand ist nicht optional. Oh, übersehen dann halt: #define check_line(a) ( (a) > 0 ? (a) = 0 : (a) = (a) )
Datum:
Simon K. schrieb: > dann halt: > > #define check_line(a) ( (a) > 0 ? (a) = 0 : (a) = (a) ) Wenn a volatile ist, wird der letzte Ausdruck unnütz ausgeführt. Ein Ausdruck muß aber keine Variable sein, es geht auch eine Zahl:
#define check_line(a) ( (a) > 0 ? (a) = 0 : 0 ) |
Peter
Datum:
Stefan W. schrieb: > assert() ist auch eins - und das ist nicht nur bei AVR vorhanden :) Die gängige Konvention ist doch, dass "function-like macros" klein geschrieben werden, "constant value macros" dagegen groß. Der ganze C-Standard enthält einige dieser funktionsähnlichen Makros, die allesamt klein geschrieben sind. Es ist natürlich Aufgabe des Implementierers, dass sich der Makro auch wirklich wie eine Funktion benimmt. Geht natürlich nicht vollständig, wenn beispielsweise jemand ein Argument wie "*cp++" übergibt, dann kann es passieren, dass die Inkrementierung zweimal erfolgt. Irgendwo gab's da was in den Untiefen von X11, bei dem ausdrücklich davor gewarnt wird, das ++ mit in den Aufruf zu schreiben, da die vermeintliche Funktion ein Makro ist.
Datum:
Kein Fallstrick, aber trotzdem hübsch anzusehen. Ich wette, dass der eine oder andere die eine oder andere Schreibweise von Arrays und deren Elementen noch nie gesehen hat ;-)
#include <stdio.h> int main () { int i; int a[3]; for (i = 0; i < 3; i++) { a[i] = 2 * i; } i = 1; printf ("%d %d\n", i[a], a[i]); printf ("%d %d\n", 1[a], a[1]); printf ("%d %d\n", 2[a], a[2]); printf ("%c %c\n", i["fun"], "fun"[i]); printf ("%s %s\n", &i["fun"], &"fun"[i]); return 0; } |
Ausgabe: 2 2 2 2 4 4 u u un un Zum Beispiel war mir die Schreibweise Index[Array] statt Array[Index] lange Zeit unbekannt. Viel Spaß beim ... Verstehen :-)
Datum:
Frank M. schrieb: > Zum Beispiel war mir die Schreibweise Index[Array] statt Array[Index] > lange Zeit unbekannt. C ist eben sehr Assembler nah: Zieladresse = Startadresse + Offset Und die Addition ist ja bekanntermaßen kommutativ. Der AVR-GCC nutzt das sogar aus, lädt den Index in den Z-Pointer und addiert dann die konstante Startadresse. Peter
Beitrag #2573816 wurde vom Autor gelöscht.
Beitrag #2573864 wurde von einem Moderator gelöscht.
Datum:
...so häufig sind Softerrors jetzt auch nicht. Grundsätzlich sind (unmodifizierte) Logikoperationen bestimmt wichtiger als die große Hammingdistanz.
Datum:
Frank M. schrieb: > Zum Beispiel war mir die Schreibweise Index[Array] statt Array[Index] > lange Zeit unbekannt. Dann hast du noch nie in den IOCCC reingeschaut. ;-) Das C99 Rationale meint dazu übrigens: 6.5.2.1 Array subscripting The C89 Committee found no reason to disallow the symmetry that permits a[i] to be written as i[a].
Datum:
Jörg Wunsch schrieb: > Dann hast du noch nie in den IOCCC reingeschaut. ;-) Stimmt. Für mich war immer die Bibel K&R das Maß der Dinge. Dort muss ich es entweder überlesen haben oder es steht nicht drin ;-)
Beitrag #2574000 wurde von einem Moderator gelöscht.
Beitrag #2574020 wurde von einem Moderator gelöscht.
Beitrag #2574294 wurde vom Autor gelöscht.
Beitrag #2574332 wurde von einem Moderator gelöscht.
Datum:
Also die häufigsten Probleme sind sicherlich Stringüberläufe. Das Problem ist, das C so implementiert ist, dass das a) nicht abgefangen werden kann (in den üblichen String Implementierungen) und b) häufig der Stack und somit der Rücksprungzeiger überschrieben wird. Dann gibts natürlich auch noch die üblichen Portabilitätsprobleme: unsigned int i=0; i--; gibt für i unterschiedliche Ergebnisse, abhängig von Compiler und Architektur. Die Kombination aus "nicht Hochsprache" und "nicht maschinennah" war für mich der Grund die weitere Beschäftigung mit C einzustellen. Es lohnt den Mehraufwand für mich nicht. Wenn ich eine höhere Sprache verwenden will, nehme ich Pascal. Da kümmert sich auch der Compiler selbst um Units und Libraries. Pascal gibt mir im obigen Anwendungsfall einen Fehler aus. (eventuell erst zur Laufzeit und nur wenn ich die Laufzeitprüfungen aktiviere)
Datum:
Kan asta schrieb: > Peter Dannegger schrieb: >> C ist eben sehr Assembler nah: > > pfff...verglichen womit? HTML? Weil HTML eine prozedurale Programmiersprache ist, so wie C?
Datum:
Naja, HTML ist keine Programmiersprache. Eher etwas, um Seiten zu beschreiben. Der Vergleich hat einen Klumpfuß ;) Vermutlich ist der Vergleich auch nicht so ernst gemeint ... Klar hat C so seine Macken. Aber es taugt für ganz kleine wie für ganz große Programme. Das kann eigentlich kaum eine andere Sprache. Ich habe mal mit Pascal angefangen, das war mir aber zu geschwätzig (1 Seite Pascal ~ 2 Zeilen C). Fehler ? Klar. So ziemlich alles was hier aufgeführt wurde. Kann man das C anlasten ? Welche Programmiersprache hat denn so etwas nicht ?
Beitrag #2574426 wurde von einem Moderator gelöscht.
Beitrag #2574430 wurde von einem Moderator gelöscht.
Beitrag #2574434 wurde von einem Moderator gelöscht.
Beitrag #2574442 wurde von einem Moderator gelöscht.
Beitrag #2574450 wurde von einem Moderator gelöscht.
Datum:
Udo Schmitt schrieb: > Der Fehler war nicht von mir, aber ich musste ihn finden. Leider war das > damals die Auswertung der Antwort einer Autorisierungszentrale an einen > Geldautomaten, das ganze hat damals einen Schaden von mehreren 10000 DM > verursacht. Der Fehler waren fehlende Test Cases.
Beitrag #2574498 wurde von einem Moderator gelöscht.
Beitrag #2574502 wurde von einem Moderator gelöscht.
Beitrag #2574505 wurde von einem Moderator gelöscht.
Beitrag #2574507 wurde vom Autor gelöscht.
Beitrag #2574580 wurde von einem Moderator gelöscht.
Datum:
Joachim Drechsel schrieb: > Vermutlich ist der Vergleich auch nicht so ernst gemeint ... Zwischen Assembler und C liegen Welten. Daher der Vergleich mit HTML, was von Stümpern auch gern als Programmiersprache bezeichnet wird.
Datum:
Kan asta schrieb: > Zwischen Assembler und C liegen Welten. Daher der Vergleich mit HTML, > was von Stümpern auch gern als Programmiersprache bezeichnet wird. Ich komme aus der Assembler-Ecke, der Umstieg auf C war nicht so schwierig. Anfangs C wie eine Art Makro-Assembler verwendet, dann immer abstrakter. Einen Unterschied von "Welten" sehe ich da jetzt nicht. Es kommt, wie immer, drauf an was man daraus macht. Ob man mit HTML/CSS programmiert ? Es ist egal wie man das nennt ;) Die meisten sagen "Nein". Ich halte das auch für unwichtig. Ich kenne einige Leute die mit HTML/CSS sehr fit sind und da tolle Sachen erstellen. "Stümper" sind das nicht (die schlagen immer 3 Kreuze wenn ich serverseitig mit C ankomme und staunen dann über so Sachen wie "Performance" da sie das von PHP her nicht kennen ;)). HTML/CSS, C etc sind alles Dinge, die einen Computer dazu bewegen sollen, das zu machen was ich will. iA nent man das "Programmieren".
Datum:
Kan asta schrieb: > Zwischen Assembler und C liegen Welten. Ich glaube, dass Du Peters Formulierung "C ist eben sehr Assembler nah:" einfach falsch verstanden hast. Natürlich sind C und Assembler grundverschiedene Programmiersprachen. Aber vieles, was in C formuliert wird, kann vom Compiler sehr transparent (und für den Programmierer auch nachvollziehbar) in Assembler übersetzt werden.
Datum:
> Zwischen Assembler und C liegen Welten.
Konkreter: Zwischen Assembler und C liegt ein Compiler.
Datum:
im obfuscated c contest fand ich mal eine nette zeile: #define _ define oder es war #define _ #define ein paar solcher scherze hatte ich mal ausprobiert, zu oft macht dann der gcc preprozessor n strich durch die richtung. kennt jemand ne sammlung von techniken aus eben diesem obfuscated c contest? http://www.ioccc.org/
Datum:
n.n. schrieb: > #define _ #define Hat mit einem heutigen Präprozessor keinen Sinn mehr. Aus dem Grund hat man ja in C99 _Pragma() als Alternative zu #pragma erfunden, weil man jenes auch innerhalb eines Makros benutzen kann. p.s.: Bitte nur sinnvolle email-Adressen angeben. Die Angabe dieser Adresse dient ausschließlich dazu, dass der Absender über bspw. eine Löschung oder Verschiebung informiert werden kann. Wer auf diese Möglichkeit verzichten möchte, gibt einfach nichts an, aber eine unsinnige Adresse bringt im besten Fall gar nichts, im schlimmsten Fall Andreas' Server mehr Arbeit (weil er ja ggf. doch versuchen wird, eine Mail zuzustellen).
Datum:
weiterer kleiner fallstrick in c: eine schleife genau 256 mal
durchlaufen lassen, mit nem uint8_t als zähler.
for(;;) geht da nicht in trivialer art.
lösung:
uint8_t count=0;
do {
//hier aktion ausführen..
count++;
} while(count);
ps: oh danke für den hinweis mit der email, werd brav das feld nun
freilassen. :)
Datum:
Zum Thema 8-Bit-Variablen fällt mir etwas aus der Borland-Builder-Hilfe ein, nämlich das Beispiel zum Thema fgetc. Unter nur der relevante Teil:
char ch; do { ch = fgetc(stream); putch(ch); } while (ch != EOF); |
Beitrag #2575504 wurde vom Autor gelöscht.
Datum:
Xenu schrieb: > Unter nur der relevante Teil: Das ist aber die Variante, wie man's nicht macht. Oder was wolltest du uns damit schreiben? ;-)
Datum:
>Das ist aber die Variante, wie man's nicht macht.
Ja eben. Das war der Original-Beispielcode aus der Borland-Builder-Hilfe
zur Funktion fgetc(). Das ist ja gerade das Lustige, dass es falsch ist.
Datum:
Joachim Drechsel schrieb: > Naja, HTML ist keine Programmiersprache. Eher etwas, um Seiten zu > beschreiben. Der Vergleich hat einen Klumpfuß ;) Das ist inzwischen Falsch. Die Kombination aus HTML und CSS ist Turing vollständig. http://lemire.me/blog/archives/2011/03/08/breaking...
Datum:
> Maybe I need to change my story.
Er sagt "vielleicht". In meinen einfachen Augen braucht eine
"richtige" Programmiersprache Kontrollstrukturen wie "if",
Schleifen etc. Egal, Turing war Mathematiker, programmiert hat
er wohl eher nicht.
Nebenbei sind HTML5/CSS3 im Moment zwar in, aber nicht zu 100%
anwedbar (vielen Dank an M$).
Ich: "Tja Kunde, ihr habt echt noch IE6 ? Sicherheitsprobleme
und die Darstellung stimmt nicht ?"
Kunde: "die Umstellung des Terminalservers kostet 12.000. Das bleibt".
Blattschuß. Nix mit HTML5/CSS3. Vielleicht in 10 Jahren wieder :-(
Datum:
Joachim Drechsel schrieb: > Kontrollstrukturen Steuerstrukturen bitte. Die Dinger "kontrollieren" nichts. "to control" kann im Deutschen sowohl steuern, regeln als auch kontrollieren bedeuten.
Datum:
Jörg Wunsch schrieb: > "to control" kann im Deutschen sowohl steuern, regeln als auch > kontrollieren bedeuten. Verstanden hast Du's aber ;) hepp ein Päckchen Korinthen :-)))
Datum:
Christian Berger schrieb: > Die Kombination aus HTML und CSS ist Turing vollständig. > http://lemire.me/blog/archives/2011/03/08/breaking... Danke für den interessanten Link; insbesondere auch zum Class 4 Zellularautomat 110 in http://en.wikipedia.org/wiki/Rule_110
Datum:
Joachim Drechsel schrieb: > hepp ein Päckchen Korinthen :-))) Ihgitt, du wirfst mit fremder Leute Kot um dich? =-O
Datum:
goto's verwenden, die aus Funktionen rausspringen. Sorry musste aus gegebenen Anlass (Beitrag "goto-Label an Funktion übergeben?") sein ;-)
Datum:
Der Entwickler schrieb: > Sorry musste aus gegebenen Anlass > (Beitrag "goto-Label an Funktion übergeben?") sein ;-) Nein, ist Quark, weil natürlich dort geklärt worden ist, dass es sowas erst einmal nicht gibt. setjmp/longjmp ist eine andere Baustelle, und die haben durchaus ihre Berechtigung (werden aber eher selten verwendet).
Datum:
Christian Berger schrieb: > Das ist inzwischen Falsch. Die Kombination aus HTML und CSS ist Turing > vollständig. Na ja - das Beispiel, das man online finden kann, erfordert zwingend die Interaktion des Benutzers. Also nicht mal eine einfache Endlosschleife kriegt man damit hin, da ist jeder Uralt-Assembler mächtiger ;-)
Datum:
Jörg Wunsch schrieb: > Der Entwickler schrieb: > >> Sorry musste aus gegebenen Anlass >> (Beitrag "goto-Label an Funktion übergeben?") sein ;-) > > Nein, ist Quark, weil natürlich dort geklärt worden ist, dass es > sowas erst einmal nicht gibt. computed goto in GNU-C?
Datum:
Johann L. schrieb: >> Nein, ist Quark, weil natürlich dort geklärt worden ist, dass es >> sowas erst einmal nicht gibt. > > computed goto in GNU-C? Erstens hat das nichts mit der Sprache C zu tun ;,), zweitens geht es meiner Meinung nach auch nur innerhalb der Funktion (hab's aber nicht probiert, da ich das Fietscher ohnehin für recht zweifelhaft halte).
Datum:
Aus aktuellem Anlass, etwas aus der Kategorie unerwartete implizite Typenkonverierung:
void main() { foo(10, -60, 50); } void foo(uint32_t nOp1, int32_t nOp2, uint32_t nOp3) { if(nOp1+nOp2 < nOp3) { // das wird nicht gemacht, hätte ich aber erwartet } else { // das wird gemacht } } |
C konvertiert wenn der unsigned Wert gleiche oder größer Bitbreite als der signed Wert hat immer zum unsigend Wert. Hier was der C99 Standard dazu sagt:
6.3.1.8 If both operands have the same type, then no further conversion is needed. Otherwise, if both operands have signed integer types or both have unsigned integer types, the operand with the type of lesser integer conversion rank is converted to the type of the operand with greater rank. Otherwise, if the operand that has unsigned integer type has rank greater or equal to the rank of the type of the other operand, then the operand with signed integer type is converted to the type of the operand with unsigned integer type. Otherwise, if the type of the operand with signed integer type can represent all of the values of the type of the operand with unsigned integer type, then the operand with unsigned integer type is converted to the type of the operand with signed integer type. Otherwise, both operands are converted to the unsigned integer type corresponding to the type of the operand with signed integer type. |
Datum:
char* generateConfigString() { //std::string result; char result[100]; //... //return result.c_str(); return result; } |
Edit: Pardon hier ist ja reines C gefragt ;-)
Datum:
Dominik S. schrieb: > Ein Klassiker in C wäre z.B. Zuweisung statt Vergleich (= statt ==) ja, der ist fies!
Datum:
Xenu schrieb: > char ch; > > do > { > ch = fgetc(stream); > putch(ch); > } while (ch != EOF); Vielleicht steh ich aufn Schlauch, aber ich komm nicht drauf wo hier das Problem liegt. :/ Ok, wahrscheinlich würd ich ne while- anstatt der do-Schleife verwenden mit einem vorangestelltem "ch = fgetc(stream); " damit unter umständen kein char geputtet wird wenn Mist aus dem stream gelesen wird. Aber wo liegt hier der 8-Bit Fallstrick? Duck & Weg :-)
Datum:
Der Entwickler schrieb: > [c] > char* generateConfigString() > { > //std::string result; > char result[100]; > > //... > > //return result.c_str(); > return result;
test.c: In function `generateConfigString': test.c:9: warning: function returns address of local variable |
Jules schrieb: >> Ein Klassiker in C wäre z.B. Zuweisung statt Vergleich (= statt ==) > > ja, der ist fies!
test.c: In function `foo': test.c:5: warning: suggest parentheses around assignment used as truth value |
Wie man sieht, genügt es für all die "fiesen Fallen", sich mal seine Compilerwarnungen durchzulesen (und sie natürlich auch einzuschalten).
Datum:
lex schrieb: > Aber wo liegt hier der 8-Bit Fallstrick? Zweierlei: Wenn "char" vorzeichenlos ist, und EOF wie üblich -1, dann ist (ch != -1) nicht sonderlich erfolgversprechend. Und selbst wenn EOF dank vorzeichenbehaftetem "char" funktioniert, dann ist bei 0xFF im Input unerwartet Schluss. Ausserdem findet sich anschliessend ein ebenso unerwartetes 0xFF am Ende vom Output-Stream.
Datum:
lex schrieb: > Aber wo liegt hier der 8-Bit Fallstrick? Dass EOF den Wert -1 hat, der von getchar() als int zurückgegeben wird. Der unterscheidet sich von allen möglichen Werten für char, die da nämlich 0 ... 255 (bzw. -128 ... +127) sein können. Die gültigen Zeichen decken damit die Bitmuster 0x0000 bis 0x00ff ab, während EOF 0xffff (oder 0xffffffff auf einer 32-bit-Maschine) ist. Wenn man das verfrüht auf 8 bit verkürzt, kann man das Zeichen 255 nicht mehr von EOF unterscheiden. Ist natürlich vor allem dann eine Fratzenfalle, wenn der Eingabestrom beliebige Binärzeichen enthalten darf.
Datum:
Das kann man übrigens noch eine Stufe weiterspinnen. Auch wenn's selten vorkommt, so darf bei einem Compiler durchaus char auch genauso groß wie int sein. Dann gibt es gar keinen int-Wert, der außerhalb des char-Bereichs liegt. Deshalb gibt's auch zusätzlich noch die Funktion feof(), um das File-Ende zu erkennen. Zugegeben kein sonderlich häufig auftretender "Fallstrick"...
Datum:
Rolf Magnus schrieb: > Zugegeben kein sonderlich häufig auftretender "Fallstrick"... Vor allem keiner, mit dem irgendwelche real existierenden Programme zurecht kämen. Denn dann würde die recht verbreitete und m.W. als zulässig angesehene Schleife int c; while ((c = getchar()) != EOF) ... ebenfalls in die Fritten gehen. Nope. Wenn auf irgendeiner Kiste mangels Hardwaresupport eine "char" Variable den gleichen Wertebereich wie "int" haben sollte, dann wird getchar ziemlich sicher dennoch sowas Ähnliches wie 8-Bit Zeichen retournieren. Und damit ist das Problem ebenfalls vom Tisch.
Datum:
Rolf Magnus schrieb: > Deshalb gibt's auch zusätzlich noch die Funktion > feof(), um das File-Ende zu erkennen. Nein. feof() und ferror() gibt es, damit man sich erkundigen kann, welche der beiden Bedingungen (end-of-file oder Fehler) beim dem Erhalt eines EOF eingetreten waren. Allerdings dürfte die korrekte Implementierung eines getchar() und zugehörigen Werts von EOF auf einer solchen Maschine, ähm, interessant werden, wenn man die Möglichkeit vorsehen will, dass getchar() tatsächlich alle möglichen Werte von `char' lesen könnte. ;-) Naja, der Standard wird sich da mit sowas wie `host character set' oder dergleichen rausreden.
Datum:
Jörg Wunsch schrieb: > Wie man sieht, genügt es für all die "fiesen Fallen", sich mal seine > Compilerwarnungen durchzulesen (und sie natürlich auch einzuschalten). Besser wäre es m.E. sogar, alle Warnungen als Fehler auszugeben. Dann passiert so etwas erst gar nicht, weil man dann nämlich quasi gezwungen wird, die "Warnungen" durchzulesen. @Jörg: Gibt der von dir verwendete Compiler die Meldung auch dann aus, wenn in meinen Beispiel statt char[100] dort std::string steht, also:
char* generateConfigString() { std::string result; //... return result.c_str(); } |
Datum:
Der Entwickler schrieb: > Besser wäre es m.E. sogar, alle Warnungen als Fehler auszugeben. -Werror Muss man natürlich auch einschalten. ;-) Mittlerweile kann ma ja sogar mittels Pragma einzelnen Warnungen für eine Funktion ausschalten, wenn es mal sinnvoll sein sollte, den Code lieber nicht zu modifizieren. > Gibt der von dir verwendete Compiler die Meldung auch dann aus, wenn in > meinen Beispiel statt char[100] dort std::string steht, also:
test.cc: In function `char* generateConfigString()': test.cc:9: error: invalid conversion from `const char*' to `char*' |
OK, das wolltest du wohl nicht wissen :), also mal den Typ der Funktion geändert auf const char *. Nein, dann warnt er leider nicht. Ist aber zugegebener Maßen schon eine etwas ältliche GCC-Version hier.
Datum:
Stimmt, die c_str() Methode liefert ja const char *. Danke dir, dass du das mal überprüft hast. ;-)
