Forum: Compiler & IDEs Compiler Bug oder 1001 C-Integer Promotion Rule?


von Falk B. (falk)


Lesenswert?

Hallo,

folgender Code läuft nicht wie erwartet. Es ist ein Fragment für eine 
kleine Menusteuerung. Hier soll einfach ein Index in einem Bereich über 
einen Drehgeber verstellt werden und ein Über/Unterlauf erfolgen.
1
int8_t read_encooder(void);
2
3
int8_t letter, encoder;
4
5
...
6
7
encoder = read_encoder();
8
9
...
10
            letter += encoder;
11
            if (letter > (sizeof(file_letters)-1) ) {
12
                letter =0;
13
            } else if (letter < 0 ) {
14
                letter = sizeof(file_letters)-1;
15
            }
16
            menu_dir[menu_letter_pos] = pgm_read_byte(&file_letters[letter]);

Wenn man es so macht, funktioniert der Unterlauf über 0 nicht. Der 
Vergleich brcs ist falsch!
1
            letter += encoder;
2
    7166:  80 91 4b 04   lds  r24, 0x044B
3
    716a:  8e 0d         add  r24, r14
4
    716c:  80 93 4b 04   sts  0x044B, r24
5
            if (letter > (sizeof(file_letters)-1) ) {
6
    7170:  88 32         cpi  r24, 0x28  ; 40
7
    7172:  18 f0         brcs  .+6        ; 0x717a <menu_fsm+0x86>
8
                letter =0;
9
    7174:  10 92 4b 04   sts  0x044B, r1
10
    7178:  05 c0         rjmp  .+10       ; 0x7184 <menu_fsm+0x90>
11
            } else if (letter < 0 ) {
12
    717a:  87 ff         sbrs  r24, 7
13
    717c:  03 c0         rjmp  .+6        ; 0x7184 <menu_fsm+0x90>
14
                letter = sizeof(file_letters)-1;
15
    717e:  87 e2         ldi  r24, 0x27  ; 39
16
    7180:  80 93 4b 04   sts  0x044B, r24
17
            }

Macht man aber einen Cast, funktioniert es! Der Vergleich brlt ist hier 
korrekt!

1
int8_t read_encooder(void);
2
3
int8_t letter, encoder;
4
...
5
6
encoder = read_encoder();
7
8
...
9
            letter += encoder;
10
            if (letter > (int8_t)(sizeof(file_letters)-1) ) {
11
                letter =0;
12
            } else if (letter < 0 ) {
13
                letter = sizeof(file_letters)-1;
14
            }
15
            menu_dir[menu_letter_pos] = pgm_read_byte(&file_letters[letter]);
1
           letter += encoder;
2
    7166:  80 91 4b 04   lds  r24, 0x044B
3
    716a:  8e 0d         add  r24, r14
4
    716c:  80 93 4b 04   sts  0x044B, r24
5
            if (letter > (int8_t)(sizeof(file_letters)-1) ) {
6
    7170:  88 32         cpi  r24, 0x28  ; 40
7
    7172:  1c f0         brlt  .+6        ; 0x717a <menu_fsm+0x86>
8
                letter =0;
9
    7174:  10 92 4b 04   sts  0x044B, r1
10
    7178:  05 c0         rjmp  .+10       ; 0x7184 <menu_fsm+0x90>
11
            } else if (letter < 0 ) {
12
    717a:  87 ff         sbrs  r24, 7
13
    717c:  03 c0         rjmp  .+6        ; 0x7184 <menu_fsm+0x90>
14
                letter = sizeof(file_letters)-1;
15
    717e:  87 e2         ldi  r24, 0x27  ; 39
16
    7180:  80 93 4b 04   sts  0x044B, r24
17
            }

Alle beteiligten Datentypen sind int8_t. Sizeof liefert int?

Was ist hier faul?

MfG
Falk

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Und hier ein Testfall für alle :-)
1
#include <stdint.h>
2
3
int b[14];
4
5
int8_t f (int8_t a)
6
{
7
    if (a > sizeof (b) -1)
8
        return a+1;
9
    return a -1 ;
10
}

Der Vergleich wird unsigned ausgeführt weil die rechte Seite unsigned 
ist.  Compilerwarnung beachten!
1
foo.c: In function 'f':
2
foo.c:6:11: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
3
     if (a > sizeof (b) -1)
4
           ^

sizeof liefert ein size_t, gemäß ABI ein "unsigned int".

von Thomas M. (thomil)


Lesenswert?

Ich kann dein genaues Problem gerade auch nicht erklären, allerdings 
solltest du beachten, dass der over/underflow eines signed Datentypes, 
wie int8_t es einer ist, in C undefiniertes verhalten ist.

von Dumdi D. (dumdidum)


Lesenswert?

Ist der Assembler Code exact gleich? Dann liegt Dein Fehler woanders.

von Falk B. (falk)


Lesenswert?

@dumdi dum (dumdidum)

>Ist der Assembler Code exact gleich? Dann liegt Dein Fehler woanders.

Nein, siehe mein Posting. Einmal wir dein Vergleich mit brcs gemacht und 
einmal mit brlt.

von Falk B. (falk)


Lesenswert?

@ Johann L. (gjlayde) Benutzerseite

>Der Vergleich wird unsigned ausgeführt weil die rechte Seite unsigned
>ist.  Compilerwarnung beachten!

Bei mir gibt es keine Warnung, -Wall ist aktiv.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Falk Brunner schrieb:
> @ Johann L. (gjlayde) Benutzerseite
>
>>Der Vergleich wird unsigned ausgeführt weil die rechte Seite unsigned
>>ist.  Compilerwarnung beachten!
>
> Bei mir gibt es keine Warnung, -Wall ist aktiv.

Erstens ist dein Compilerversion geheim, und zweitens fehlt ein -W.

Falls -Wsign-compare noch nicht einzug in die Warning-Defaults deiner 
Version gefunden hat, setz es eben explizit. Und bei deinem Glück am 
besten gleich -Werror=sign-compare.

von Patrick D. (oldbug) Benutzerseite


Lesenswert?

1
int8_t read_encooder(void);
2
3
int8_t letter, encoder;
4
...
5
6
encoder = read_encoder();

Ka ob's hilft: Tippfehler!?

von Falk B. (falk)


Lesenswert?

@Johann L. (gjlayde) Benutzerseite

>> Bei mir gibt es keine Warnung, -Wall ist aktiv.

>Erstens ist dein Compilerversion geheim, und zweitens fehlt ein -W.

Ist der letzte offizielle WinAVR von 2010 mit avr-gcc 4.3.3.

>Falls -Wsign-compare noch nicht einzug in die Warning-Defaults deiner
>Version gefunden hat, setz es eben explizit. Und bei deinem Glück am
>besten gleich -Werror=sign-compare.

Aha, da spuckt er gleich einen error aus. OK, vielen Dank. Damit wäre 
das geklärt.

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


Lesenswert?

Johann L. schrieb:
> Erstens ist dein Compilerversion geheim, und zweitens fehlt ein -W.

Besser: -Wextra (hieß mal -W)

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.