TomasL skrev:Kod: Markera allt
uint32 i;
sint16 arr[..]
i = arr[x++]
i |= arr[x++]<<16
Ovanstående kod funkar, kanske ibland, ibland alltid, ibland inte, ibland funkar den slumpmässigt.
Problemet uppstår när den högre byten i
"arr" inte är 0, eftersom
"i" i detta fallet är 32 bitar, och man läser in från en 16 bitars array, så kan vad som helst hamna i de övre 16 bitarna i variabeln
"i",
Samma gäller ju även om
"arr" är en vanlig variabel, då får de övre 16 bitarna "slumpmässiga" värden, dvs värden från dataomrrådet intill
"arr".
för att slippa detta måste man naturligtvis casta 16bitars variabeln, så man enbart läser in 16 bitar, dvs
Kod: Markera allt
uint32 i;
sint16 arr[..]
i = (uint16)arr[x++]
i |= arr[x++]<<16
Det finns problem med exempelkoden, men jag tror inte heller att TomasLs förklaring (eller fix) är korrekt.
Innan operatorer appliceras på en heltalsoperand så uppgraderas typen ("type promotion") enligt följande:
signed char, short, signed bitfield --> int
char, unsigned char, unsigned short, unsigned bitfields --> int (om alla originaltypens möjliga värden får plats) eller unsigned int annars.
Alltså (givet sizeof(unsigned int) == 4 och sizeof(short) == 2):
Kod: Markera allt
unsigned int i;
short arr[..];
i = arr[x++]; /* typeof(arr[..]) --> int */
i |= arr[x++]<<16; /* Här shiftar vi en int (signed) */
Det finns flera problem här:
Rad 3: Type conversion int -> unsigned int. OK om originalvärdet är >= 0. Ingen varning från kompilatorn (definierat och korrekt enligt C-standarden).
Rad 4a: Bit-shift av signed är odefinierat om värdet antingen är negativt från början eller om det shiftas in en 1:a i sign bit. Här är det alltså viktigt att casta till
unsigned int (ej unsigned short om jag förstår rätt).
Rad 4b: Type conversion int -> unsigned int. OK om originalvärdet är >= 0.
Originalkoden använder dessutom icke-standard-typer vilket gör det omöjligt att se hur koden kommer att fungera. Även standard (C99) fixed-width-typer (int16_t, uint32_t) är definierade i termer av bastyperna int etc, så även här kommer koden att bete sig olika på olika plattformar.
Slutsats 1: Använd alltid
unsigned int för bit-operationer (lite yxigt men säkert!). Alternativt: skifta aldrig upp så att resultatet fyller alla bitar i den "uppgraderade" typen, och aldrig ett negativt värde.
Slutsats 2: Vill du ändra signedness, casta till int eller unsigned int, även om både originaltypen och resultattypen är kortare. Mer generellt: Använd om möjligt variabler av typen int/unsigned int, så slipper du otrevliga överraskningar.
Slutsats 3: Undvik typedefs eftersom de gör koden svårare att förstå (sällsynta undantag finns dock, t.ex. funktionspekare).
Edit: la till Slutsats 2 & 3.