Forum: Compiler & IDEs die gemeinsten Fallstricke in C


von Micha (Gast)


Lesenswert?

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
1
uint8_t;
2
  .
3
  .
4
  .
5
x--;
6
if (x < 0) blafasel();

bin mal gespannt auf weitere Beiträge.

von error in line 11 (Gast)


Lesenswert?

1
for(int i=0;i<5;i++);
2
{
3
    blafusel(i);
4
}

von Peter II (Gast)


Lesenswert?

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.

von Dominik S. (dasd)


Lesenswert?

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 ==).

von Der Weise (Gast)


Lesenswert?

Oh gut ist auch:
1
for (int i = 0; i < n; i++) {
2
  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...

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Was geht ist zB:
1
extern void bar (void);
2
3
unsigned foo (unsigned x)
4
{
5
    x--;
6
    if (x + 1 == 0)
7
        bar();
8
        
9
    return x;
10
}

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Micha schrieb
> if (x < 0) blafasel();

Was hat -Wall -Wextra denn dazu gesagt?  Sicher sowas wie:
1
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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Random .. (thorstendb) Benutzerseite


Lesenswert?

Micha schrieb
> uint8_t;
> x--;
> if (x < 0) blafasel();

x kann hier nie <0 werden ...
1
if(x) x--;
wo ist das Problem? :-)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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
1
    x[i]     = 217;
2
    x[i + 1] = 117;
3
    x[i + 2] = 017;

;-)

von Joerg W. (joergwolfram)


Lesenswert?

1
funktionsaufruf1(paramter_array[index++],parameter_array[index++]);

Wenn zum Beispiel index=1 ist, entspricht das:

1
funktionsaufruf(paramter_array[2],parameter_array[1]);

wohingegen

1
funktionsaufruf1(paramter_array[index++],parameter_array[index]);

das "gefühlt richtige" Ergebnis liefert, bei index=1 wäre das dann:

1
funktionsaufruf(paramter_array[1],parameter_array[2]);

von PoWl (Gast)


Lesenswert?

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ä?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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:
1
foo.c:6:27: warning: operation on 'index' may be undefined

von Kan a. (Firma: Basta) (kanasta)


Lesenswert?

welches Statement zuerst inkrementiert wird hängt vom Compiler (und 
seinen Einstellungen) ab.

von Udo S. (urschmitt)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Udo Schmitt schrieb:
> Leider war das
> damals die Auswertung der Antwort einer Autorisierungszentrale an einen
> Geldautomaten

Aua. :-O

von Stefan E. (sternst)


Lesenswert?

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.

von Udo S. (urschmitt)


Lesenswert?

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.

von Random .. (thorstendb) Benutzerseite


Lesenswert?

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...

von Udo S. (urschmitt)


Lesenswert?

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.

von Nico S. (nico22)


Lesenswert?

Random ... schrieb:
> so was führt doch zu "unpredictable behaviour".

Warum? && ist doch ein Sequence Point?

von Stefan E. (sternst)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Stefan E. (sternst)


Lesenswert?

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).

von Karl H. (kbuchegg)


Lesenswert?

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?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Udo S. (urschmitt)


Lesenswert?

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 :-)

von Udo S. (urschmitt)


Lesenswert?

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

von Stefan E. (sternst)


Lesenswert?

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. ;-)

von Random .. (thorstendb) Benutzerseite


Lesenswert?

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...).
1
char * pc;
2
3
if ((pc++ == '0') && (pc++ == '2') {
4
  strcpy(..., pc);
5
}
6
7
if(pc[0]=='0' && pc[1]=='2') {
8
    strcpy(..., &pc[2]);
9
}
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:
1
if(str && !strcmp(str, "foo")) {
2
  ...;
3
}

von Karl H. (kbuchegg)


Lesenswert?

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 :-)

von Karl H. (kbuchegg)


Lesenswert?

Random ... schrieb:

>
1
> char * pc;
2
> 
3
> if ((pc++ == '0') && (pc++ == '2') {
4
>   strcpy(..., pc);
5
> }
6
> 
7
> if(pc[0]=='0' && pc[1]=='2') {
8
>     strcpy(..., &pc[2]);
9
> }
10
>
> 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.

von Der E. (rogie)


Lesenswert?

Ein "beliebter" Fallstrick ist auch
1
int a[5];
2
3
for(int i=0; i <=5;i++) a[i]=0;

Oder sowas:
1
char c[5];
2
3
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).

von Stefan E. (sternst)


Lesenswert?

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.

von Udo S. (urschmitt)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Wenn du die fehlenden Dereferenzier-* noch ergänzt.
Die hatte ich schon in meinem Beispiel vergessen, sorry.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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).

von Karl H. (kbuchegg)


Lesenswert?

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.

von Udo S. (urschmitt)


Lesenswert?

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.

von Random .. (thorstendb) Benutzerseite


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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. :-)

von Stefan E. (sternst)


Lesenswert?

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:
1
if ((pc++ == '0') && (pc++ == '2')) {
hat kein "unpredictable behaviour".

von Hans-Georg L. (h-g-l)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Hans-Georg L. (h-g-l)


Lesenswert?

Compiler Fehler wurden damals so angezeigt ;)
                              ^
------------------------------+

von Udo S. (urschmitt)


Lesenswert?

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 :-)

von Peter D. (peda)


Lesenswert?

Ich hab das mal durch den AVR-GCC gejagt.
Der Code wird vollkommen korrekt übersetzt:
1
char* test( char *pc, char *dest )
2
{
3
  b6:   cf 93           push    r28
4
  b8:   df 93           push    r29
5
  ba:   fc 01           movw    r30, r24
6
  if ((*pc++ == '0') && (*pc++ == '2')) {
7
  bc:   ec 01           movw    r28, r24
8
  be:   21 96           adiw    r28, 0x01       ; 1
9
  c0:   80 81           ld      r24, Z
10
  c2:   80 33           cpi     r24, 0x30       ; 48
11
  c4:   39 f4           brne    .+14            ; 0xd4 <test+0x1e>
12
  c6:   81 81           ldd     r24, Z+1        ; 0x01
13
  c8:   21 96           adiw    r28, 0x01       ; 1
14
  ca:   82 33           cpi     r24, 0x32       ; 50
15
  cc:   19 f4           brne    .+6             ; 0xd4 <test+0x1e>
16
    strcpy( dest, pc);
17
  ce:   cb 01           movw    r24, r22
18
  d0:   be 01           movw    r22, r28
19
  d2:   04 d0           rcall   .+8             ; 0xdc <strcpy>
20
  }
21
  return pc;
22
}
23
  d4:   ce 01           movw    r24, r28
24
  d6:   df 91           pop     r29
25
  d8:   cf 91           pop     r28
26
  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

von Μαtthias W. (matthias) Benutzerseite


Lesenswert?

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
>
1
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

von Karl H. (kbuchegg)


Lesenswert?

Μα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

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Was ich immer wieder nett finde:
1
#include <stdio.h>
2
3
#define NEG(a) -a
4
5
int main ()
6
{
7
    int x = 3;
8
    int y;
9
10
    y = NEG(-x);
11
    printf ("y = %d\n", y);
12
}

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

von Hans-Georg L. (h-g-l)


Lesenswert?

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)

von Peter D. (peda)


Lesenswert?

Hans-georg Lehnard schrieb:
> Wenn du ein definiertes Verhalten haben willst setze Klammern
>
> #define NEG(a) (-a)

Dann aber auch richtig:
1
#define NEG(a) (-(a))


Peter

von Karl H. (kbuchegg)


Lesenswert?

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.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

Ein anderer beliebter Fallstrick ist die Annahme ...
1
  result = foo() + bar();

... dass die Reihenfolge der Aufrufe
  * zuerst foo
  * dann bar
ist.

Auswertungsreihenfolge hat nichts mit Operator Precedence zu tun!
Auch im Fall
1
  result = foo() * ( 2 + bar() )
ist die Reihenfolge der Aufrufe nicht definiert. Es ist keineswegs 
sicher gestellt, dass bar() vor foo() aufgerufen werden muss.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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:
1
...
2
main ()
3
{
4
    int x = 3;
5
    int y;
6
7
    y = - -x;
8
    printf ("y = %d\n", y);
9
}

Der Preprocessor baut da ein Blank zwischen die beiden Minuszeichen.

Jetzt:

$ cc --traditional -E foo.c:
1
...
2
main ()
3
{
4
    int x = 3;
5
    int y;
6
7
    y = --x;
8
    printf ("y = %d\n", y);
9
}

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

von Karl H. (kbuchegg)


Lesenswert?

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 :-)

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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 ;-)

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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:
1
main ()
2
{
3
    int columns = 80;
4
    int x = 4711;
5
6
    if (columns > 80)
7
        check_line (x);
8
    else
9
        printf ("columns <= 80\n");   // Diese Zeile wird NICHT ausgegeben!
10
}

Rätselhaft? Nicht wenn man das Makro dazu kennt:
1
#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:
1
#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:
1
    if (columns > 80)
2
    {
3
        check_line (x);
4
    }
5
    else
6
    {
7
        printf ("columns <= 80\n");   // Diese Zeile wird NICHT ausgegeben!
8
    }

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.

von (prx) A. K. (prx)


Lesenswert?

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?

von (prx) A. K. (prx)


Lesenswert?

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)

von Verwirrter Anfänger (Gast)


Lesenswert?

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:
1
if(a > 10)
2
  do_something();
3
do_something_else();
wird beim debuggen zu:
1
if(a > 10)
2
  do_something();
3
  printf("Doing something...");
4
do_something_else();
oder
1
if(a > 10)
2
  first_do_another_thing();
3
  do_something();
4
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...

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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

von (prx) A. K. (prx)


Lesenswert?

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.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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)

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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. ;-)

von Karl H. (kbuchegg)


Lesenswert?

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.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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/usr/src/cmd/sh/mac.h

Da sieht "C" plötzlich ganz anders aus ;-)

von Peter D. (peda)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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

von Xenu (Gast)


Lesenswert?

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:
1
char buf [BUFSIZE];

In anderen Sourcedateien hatte ich folgende Deklaration, um auf den
Puffer zuzugreifen:
1
extern char* buf;

Nur ist das halt nicht dasselbe wie:
1
extern char buf[];

Mein Programm lief nicht, der gcc spuckte keinen Fehler aus.
Nach Betrachten des Assemblercodes kam dann der große Aha-Moment.

von Peter D. (peda)


Lesenswert?

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.:
1
  float * fp;
2
  fp = ...
3
  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

von Karl H. (kbuchegg)


Lesenswert?

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)

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Peter Dannegger schrieb:
> Warum nicht?
> Ich verwende es ohne Probleme, z.B.:

$ cat foo.c
1
#include <string.h>
2
3
char buf[20];
4
extern void print_global_string (void);
5
6
int main ()
7
{
8
    strcpy (buf, "hello, world");
9
    print_global_string ();
10
    return 0;
11
}

$ cat bar.c
1
#include <stdio.h>
2
3
extern char * buf;
4
5
void print_global_string (void)
6
{
7
    printf ("%s\n", buf);
8
}

$ cc foo.c bar.c -o foo && ./foo
Segmentation fault

Und nu?

von Peter D. (peda)


Lesenswert?

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:
1
MAIN.C:6: error: conflicting types for 'buf'
2
MAIN.C:4: error: previous declaration of 'buf' was here


Peter

von Peter D. (peda)


Lesenswert?

Die automatische Typumwandlung kann oft auch eine Falle sein:
1
int test()
2
{
3
  int a = -60;
4
  unsigned int b = 20;
5
6
  a /= b;
7
  return a;
8
}

-60 / 20 = 3273

Besser wäre daher eine Warnung bei Typumwandlungen, die Informationen 
verlieren (Wertebereich oder Vorzeichen).


Peter

von Yalu X. (yalu) (Moderator)


Lesenswert?

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"

von (prx) A. K. (prx)


Lesenswert?

Frank M. schrieb:

> Da sieht "C" plötzlich ganz anders aus ;-)

Mit
  #define TRUE  (-1)
aber zur Laufzeit auch. ;-)

von Gert (Gast)


Lesenswert?

format c:
LOL (OK, off Topic ;-) )

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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?

von Karl H. (kbuchegg)


Lesenswert?

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 )

von Stefan W. (stefan_w37)


Lesenswert?

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 :)

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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 ;-)

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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...

von Simon K. (simon) Benutzerseite


Lesenswert?

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 ;-)

von Rolf M. (rmagnus)


Lesenswert?

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.

von Simon K. (simon) Benutzerseite


Lesenswert?

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) )

von Peter D. (peda)


Lesenswert?

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:
1
#define check_line(a) ( (a) > 0 ? (a) = 0 : 0 )


Peter

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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 ;-)
1
#include <stdio.h>
2
3
int
4
main ()
5
{
6
    int i;
7
    int a[3];
8
9
    for (i = 0; i < 3; i++)
10
    {
11
        a[i] = 2 * i;
12
    }
13
14
    i = 1;
15
    printf ("%d %d\n", i[a], a[i]);
16
    printf ("%d %d\n", 1[a], a[1]);
17
    printf ("%d %d\n", 2[a], a[2]);
18
    printf ("%c %c\n", i["fun"], "fun"[i]);
19
    printf ("%s %s\n", &i["fun"], &"fun"[i]);
20
    return 0;
21
}

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 :-)

von Peter D. (peda)


Lesenswert?

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

von Kan a. (Firma: Basta) (kanasta)


Lesenswert?

Peter Dannegger schrieb:
> C ist eben sehr Assembler nah:

pfff...verglichen womit? HTML?

von Kan a. (Firma: Basta) (kanasta)


Lesenswert?

...so häufig sind Softerrors jetzt auch nicht. Grundsätzlich sind 
(unmodifizierte) Logikoperationen bestimmt wichtiger als die große 
Hammingdistanz.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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].

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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 ;-)

von confuse (Gast)


Lesenswert?

http://german-bash.org/339753
[duck, und wech...] ;-)

von Christian B. (casandro)


Lesenswert?

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)

von Simon K. (simon) Benutzerseite


Lesenswert?

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?

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

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 ?

von Tester (Gast)


Lesenswert?

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.

von Kan a. (Firma: Basta) (kanasta)


Lesenswert?

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.

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

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".

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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.

von Kan a. (Firma: Basta) (kanasta)


Lesenswert?

> Zwischen Assembler und C liegen Welten.

Konkreter: Zwischen Assembler und C liegt ein Compiler.

von n.n. (Gast)


Lesenswert?

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/

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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).

von n.n. (Gast)


Lesenswert?

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.  :)

von Xenu (Gast)


Lesenswert?

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:
1
   char ch;
2
  
3
   do
4
   {
5
      ch = fgetc(stream); 
6
      putch(ch);
7
   } while (ch != EOF);

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Xenu schrieb:
> Unter nur der relevante Teil:

Das ist aber die Variante, wie man's nicht macht.  Oder was wolltest
du uns damit schreiben? ;-)

von Xenu (Gast)


Lesenswert?

>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.

von Christian B. (casandro)


Lesenswert?

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-news-htmlcss-is-turing-complete/

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

> 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 :-(

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Joachim Drechsel schrieb:
> Kontrollstrukturen

Steuerstrukturen bitte.  Die Dinger "kontrollieren" nichts.

"to control" kann im Deutschen sowohl steuern, regeln als auch
kontrollieren bedeuten.

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

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 :-)))

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Christian Berger schrieb:

> Die Kombination aus HTML und CSS ist Turing vollständig.
> 
http://lemire.me/blog/archives/2011/03/08/breaking-news-htmlcss-is-turing-complete/

Danke für den interessanten Link; insbesondere auch zum Class 4 
Zellularautomat 110 in http://en.wikipedia.org/wiki/Rule_110

von Da D. (dieter)


Lesenswert?

Joachim Drechsel schrieb:
> hepp ein Päckchen Korinthen :-)))

Ihgitt, du wirfst mit fremder Leute Kot um dich? =-O

von Der E. (rogie)


Lesenswert?

goto's verwenden, die aus Funktionen rausspringen.

Sorry musste aus gegebenen Anlass 
(Beitrag "goto-Label an Funktion übergeben?") sein ;-)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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).

von Mark B. (markbrandis)


Lesenswert?

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 ;-)

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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).

von thomasH (Gast)


Lesenswert?

Aus aktuellem Anlass, etwas aus der Kategorie unerwartete implizite 
Typenkonverierung:
1
void main() {
2
  foo(10, -60, 50); 
3
}
4
5
void foo(uint32_t nOp1, int32_t  nOp2, uint32_t nOp3) 
6
{
7
  if(nOp1+nOp2 < nOp3)
8
  {
9
    // das wird nicht gemacht, hätte ich aber erwartet
10
  } else {
11
    // das wird gemacht
12
  }  
13
}

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:
1
6.3.1.8
2
If both operands have the same type, then no further conversion is needed.
3
Otherwise, if both operands have signed integer types or both have unsigned
4
integer types, the operand with the type of lesser integer conversion rank is
5
converted to the type of the operand with greater rank.
6
Otherwise, if the operand that has unsigned integer type has rank greater or
7
equal to the rank of the type of the other operand, then the operand with
8
signed integer type is converted to the type of the operand with unsigned
9
integer type.
10
Otherwise, if the type of the operand with signed integer type can represent
11
all of the values of the type of the operand with unsigned integer type, then
12
the operand with unsigned integer type is converted to the type of the
13
operand with signed integer type.
14
Otherwise, both operands are converted to the unsigned integer type
15
corresponding to the type of the operand with signed integer type.

von Der E. (rogie)


Lesenswert?

1
char* generateConfigString()
2
{
3
  //std::string result;
4
  char result[100];
5
6
  //... 
7
8
 //return result.c_str();
9
 return result; 
10
}

Edit: Pardon hier ist ja reines C gefragt ;-)

von Jules (Gast)


Lesenswert?

Dominik S. schrieb:
> Ein Klassiker in C wäre z.B. Zuweisung statt Vergleich (= statt ==)

ja, der ist fies!

von lex (Gast)


Lesenswert?

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 :-)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Der Entwickler schrieb:
> [c]
> char* generateConfigString()
> {
>   //std::string result;
>   char result[100];
>
>   //...
>
>  //return result.c_str();
>  return result;
1
test.c: In function `generateConfigString':
2
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!
1
test.c: In function `foo':
2
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).

von (prx) A. K. (prx)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Rolf Magnus (Gast)


Lesenswert?

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"...

von (prx) A. K. (prx)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Der E. (rogie)


Lesenswert?

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:
1
char* generateConfigString()
2
{
3
  std::string result;
4
  
5
  //... 
6
7
 return result.c_str();
8
}

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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:
1
test.cc: In function `char* generateConfigString()':
2
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.

von Der E. (rogie)


Lesenswert?

Stimmt, die c_str() Methode liefert ja const char *.

Danke dir, dass du das mal überprüft hast. ;-)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.