Svar: Ja.Är det "int" mm som du avser med inbyggda typer och "uint32" som egen typ?
Enkla men fatala buggar
Re: Enkla men fatala buggar
Re: Enkla men fatala buggar
... fast detta ger ju typen "pekare till array av unsigned char", så den går inte att använda som 32-bitstal utan en massa typecasting. (Don't do it! )blueint skrev:typedef unsigned char ui32[4];
Re: Enkla men fatala buggar
Nej, inte ens "char" har nödvändigtvis samma längd oavsett system, det är bara definierat att den skall vara >= 8 bitar.TomasL skrev: Beträffande mina typer uint32, sint16 osv, är det en mycket bra skola att INTE använda de inbyggda typerna, eftersom konstigheter kan uppkomma när man porterar kod.
I C är det väl i princip bara "char" som har samma längd oavsett system.
Varför inte använda de standardiserade uint32_t och int16_t?TomasL skrev: Beträffande mina typer uint32, sint16 osv, är det en mycket bra skola att INTE använda de inbyggda typerna, eftersom konstigheter kan uppkomma när man porterar kod.
Re: Enkla men fatala buggar
Jag kan ju också bidra med en enkel men fatal bugg, som finns i gcc.
Något förenklat ser den ut såhär:
Detta fungerar av en slump på ett stort antal arkitekturer som hanterar variabla argument (t.o.m. ett visst antal argument) på samma sätt som
icke variabla, men det är inte alls garanterat att så är fallet (t.ex. så fungerar detta inte på OpenRISC, där jag drabbades av denna bugg).
Det finns en bugg-rapport gjord för 10 år sedan, med en uppdatering 2007, men inget har hänt sen dess.
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=12081
Något förenklat ser den ut såhär:
Kod: Markera allt
void f0(int x, int y);
void f1(int x, int y);
void f2(int x, int y)
{
void (*f) (int, ...) = f0;
f1(x+1, y+1);
f(x, y);
}
icke variabla, men det är inte alls garanterat att så är fallet (t.ex. så fungerar detta inte på OpenRISC, där jag drabbades av denna bugg).
Det finns en bugg-rapport gjord för 10 år sedan, med en uppdatering 2007, men inget har hänt sen dess.
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=12081
Re: Enkla men fatala buggar
Fast definitionen "void (*f) (int, ...)" är ju egentligen en icke-definition eftersom den inte bestämmer hårt vad slags argument funktionspekaren skall ha.
- Swech
- EF Sponsor
- Inlägg: 4695
- Blev medlem: 6 november 2006, 21:43:35
- Ort: Munkedal, Sverige (Sweden)
- Kontakt:
Re: Enkla men fatala buggar
Hade det inte varit ide att lägga buggarna i WIkin när ni dividerat klart om huruvida buggarna
är buggar eller ej
Swech
är buggar eller ej
Swech
Re: Enkla men fatala buggar
Förstår inte riktigt vad du försöker poängtera?blueint skrev:Fast definitionen "void (*f) (int, ...)" är ju egentligen en icke-definition eftersom den inte bestämmer hårt vad slags argument funktionspekaren skall ha.
Det är klart det är en definition, det definierar en funktionspekare till en funktion med variabla argument.
Re: Enkla men fatala buggar
Just det. Och det verkar vara samma del av standarden som gäller även för dina andra två exempel där en array indexeras med i samma uttryck. Standarden säger:arvidb skrev:Ok, efter lite mer googling:
Resultatet av uttrycket 'i = ++i + 1' (liksom av 'i = i++ + 1') är odefinierat i C-standarden, eftersom två tilldelningar till samma variabel sker utan sekvenspunkt emellan. Resultatet kan alltså vara att inget alls händer, att det motsvarar i += 2, att programmet kraschar eller något annat, beroende på kompilator och månens fas...
Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be accessed only to determine the value to be stored.
Re: Enkla men fatala buggar
Angående originalinläggets:
Första tilldelningen kommer att läsa en sint16 ur arraryen och lagrar det värdet omvandlat till uint32 i i. Detta är det första problemet, eftersom negativa tal enligt C-standard hanteras som att unsigned-variabeln slår runt. -1 från arrayen omvandlas då till exempel till 0xFFFFFFFF, vilket troligen var oönskat. Så en cast till uint16 är ju en möjlighet, men man kan ju också fundera på varför arrayen är signed om talen ändå skall tolkas som unsigned i slutändan?
Sedan andra radens beteende beror på storleken på int. Är int 16 bitar kommer ingen ytterligare integer promotion att utföras och shiften utförs som en 16-bitars skift. Det är dock undefined behavior att skifta en 16-bitars integer 16 steg eller mer, så här kan vad som helst hända (inte ens begränsat till att i får fel värde).
Är int 32 bitar kommer istället sint16 expanderas till int (sint32) och skiften utföras. Om talet ur arrayen är negativt eller skiften ger overflow är detta också undefined behavior enligt C-standarden på samma sätt som ovan, men använder man GCC är detta väldefinierat och kommer att ge det resultat som verkar vara det önskade. Se nedan, men observera min fetmarkering!
Vill man inte göra några antaganden om miljö och endast använda standard-grejer bör koden vara:
Observera omvandlingen i två steg till uint32_t! Utan den kommer andra raden få samma problem som första raden hade i originalet!
Kod: Markera allt
uint32 i;
sint16 arr[..]
i = arr[x++]
i |= arr[x++]<<16
Sedan andra radens beteende beror på storleken på int. Är int 16 bitar kommer ingen ytterligare integer promotion att utföras och shiften utförs som en 16-bitars skift. Det är dock undefined behavior att skifta en 16-bitars integer 16 steg eller mer, så här kan vad som helst hända (inte ens begränsat till att i får fel värde).
Är int 32 bitar kommer istället sint16 expanderas till int (sint32) och skiften utföras. Om talet ur arrayen är negativt eller skiften ger overflow är detta också undefined behavior enligt C-standarden på samma sätt som ovan, men använder man GCC är detta väldefinierat och kommer att ge det resultat som verkar vara det önskade. Se nedan, men observera min fetmarkering!
http://gcc.gnu.org/onlinedocs/gcc/Integ ... ementationThe results of some bitwise operations on signed integers (C90 6.3, C99 6.5).
Bitwise operators act on the representation of the value including both the sign and value bits, where the sign bit is considered immediately above the highest-value value bit. Signed ‘>>’ acts on negative numbers by sign extension.
GCC does not use the latitude given in C99 only to treat certain aspects of signed ‘<<’ as undefined, but this is subject to change.
Vill man inte göra några antaganden om miljö och endast använda standard-grejer bör koden vara:
Kod: Markera allt
uint32_t i;
int16_t arr[..];
i = (uint16_t)arr[x++];
i |= ((uint32_t)(uint16_t)arr[x++])<<16;
Re: Enkla men fatala buggar
Rent generellt kan man väl säga att man bör undvika alla former av uttryck som inte ger ett definierat resultat i C. Annars kommer väl fler och fler mer eller mindre "avancerade" exempel upp här där resultatet är odefinierat. Håll er till kod som är definierad.
Sen vet jag inte om "void (*f) (int, ...)" hör hemma i den här tråden... Det var ju "enkla men fatala buggar", men använder man uttryck som detta så ska man nog också vara påläst om vad det innebär. Och med "buggar" menar alltså TS buggar i det egna programmet, inte buggar i GCC.
----- next subject ----
Personligen anser jag väl annars att ett av de största misstagen man gjorde när man skapade C var att samma definition av variabel är helt okila beroende på plattform. int kan vara 16, 32 eller 64 bitar, char kan vara 8 eller 16 och ibland kan även char vara unsigned.
Ett enkelt men fatalt fel jag har gjort har varit just på grund av denna korkade char:
Detta uttryck blir alltid falskt, eftersom char bara kan anta värden mellan -127 och +128.
(om char är definierat som signed char alltså, vilket det är i GCC per default. Man kan komma förbi detta genom att ha -funsigned-char som parameter för GCC vid kompileringen. Men då gäller det ju att man alltid har det, annars står man ju där med skägget i brevlådan nästa gång i alla fall!)
För att vara helt garanterad från sådana fel borde man alltså skriva:
eller ännu hellre, skippa char helt och hållet i programmet:
Sen vet jag inte om "void (*f) (int, ...)" hör hemma i den här tråden... Det var ju "enkla men fatala buggar", men använder man uttryck som detta så ska man nog också vara påläst om vad det innebär. Och med "buggar" menar alltså TS buggar i det egna programmet, inte buggar i GCC.
----- next subject ----
Personligen anser jag väl annars att ett av de största misstagen man gjorde när man skapade C var att samma definition av variabel är helt okila beroende på plattform. int kan vara 16, 32 eller 64 bitar, char kan vara 8 eller 16 och ibland kan även char vara unsigned.
Ett enkelt men fatalt fel jag har gjort har varit just på grund av denna korkade char:
Kod: Markera allt
char x;
if (x == 0x81) ...
(om char är definierat som signed char alltså, vilket det är i GCC per default. Man kan komma förbi detta genom att ha -funsigned-char som parameter för GCC vid kompileringen. Men då gäller det ju att man alltid har det, annars står man ju där med skägget i brevlådan nästa gång i alla fall!)
För att vara helt garanterad från sådana fel borde man alltså skriva:
Kod: Markera allt
char x;
if ((uint8_t)x == 0x81) ...
Kod: Markera allt
uint8_t x;
if (x == 0x81) ...
Re: Enkla men fatala buggar
Så här kan man också göra:
Kod: Markera allt
unsigned char ch;
if( ch == 0x81 )
printf("Seems it's an unsigned day today ;-)\n");
Re: Enkla men fatala buggar
blueint: Ja, men nu glömde du att klamra in det som kommer efter "if()".
Inte nödvändigt, men rekommenderat för att undvika fatala buggar.
Inte nödvändigt, men rekommenderat för att undvika fatala buggar.
Re: Enkla men fatala buggar
Körde en funktionspekare i c som en callback i ett interrupt på en ARM cortex-m3
Problemet var att gcc tyckte att länkregistret(LR) var ledigt att använda eftersom den inte hittade några subanrop i interruptet.
När interruptet skulle lämnas så var länkregistret(LR) trashat och den hoppade till en illegal address och jag fick ett "hard fault".
Så kan det gå när gcc optimerar.
Löste det genom att ha en liten subrutin som är deklarerad noinline i gcc.
Problemet var att gcc tyckte att länkregistret(LR) var ledigt att använda eftersom den inte hittade några subanrop i interruptet.
När interruptet skulle lämnas så var länkregistret(LR) trashat och den hoppade till en illegal address och jag fick ett "hard fault".
Så kan det gå när gcc optimerar.
Löste det genom att ha en liten subrutin som är deklarerad noinline i gcc.
Re: Enkla men fatala buggar
Brukar man inte antingen blockera registren, typ "register R8" är min!
Eller trycka kopia på alla register vid anrop, och sedan kopiera tillbaks så att inget förstörs?
GCC optimeringen lät iaf lite väl "för smart".
Eller trycka kopia på alla register vid anrop, och sedan kopiera tillbaks så att inget förstörs?
GCC optimeringen lät iaf lite väl "för smart".
Re: Enkla men fatala buggar
Det är väl ett hyfsat enkelt misstag att råka casta en void (*f) (int, int) till en void (*f) (int, ...)?jesse skrev: Sen vet jag inte om "void (*f) (int, ...)" hör hemma i den här tråden... Det var ju "enkla men fatala buggar", men använder man uttryck som detta så ska man nog också vara påläst om vad det innebär. Och med "buggar" menar alltså TS buggar i det egna programmet, inte buggar i GCC.
Och resultatet kan vara fatalt under vissa omständigheter.
Varför måste exemplen vara från program skrivna av en själv?
(För att klargöra, buggen finns i gccs kod, det är alltså inte så att gcc gör något fel med uttrycket)
Det där låter konstigt tycker jag, kompilatorn borde inte göra skillnad på funktionsanrop genom funktionspekare och "vanliga" funktionsanrop.Micke_s skrev:Körde en funktionspekare i c som en callback i ett interrupt på en ARM cortex-m3
Problemet var att gcc tyckte att länkregistret(LR) var ledigt att använda eftersom den inte hittade några subanrop i interruptet.
När interruptet skulle lämnas så var länkregistret(LR) trashat och den hoppade till en illegal address och jag fick ett "hard fault".
Så kan det gå när gcc optimerar.
Löste det genom att ha en liten subrutin som är deklarerad noinline i gcc.
I alla fall inte med avseende på nödvändigheten att spara undan LR.
Så antingen är det nåt som fattas i din beskrivning, eller så har du helt enkelt stött på en bugg i gcc.