Pokud ve svém C či C++ programu porovnáváte znaménková a bezznaménková čísla, používáte překladač GCC a máte zaplá varování, tak jste se již určitě setkali s varováním warning: comparison between signed and unsigned integer expressions
. Toto varování bývá velmi často přehlíženo, a to mnohdy z toho důvodu, že dotyčnému není jasné, kde by mohl vzniknout problém. Cílem tohoto krátkého příspěvku je na tento problém poukázat.
Pro zjednodušení budu mluvit pouze o ISO C99 (v ISO C++98 je situace obdobná, neřku-li stejná). Mějme následující kód:
int i; unsigned int j; // ... přiřazení hodnot do proměnných i a j if (i < j) { // ... }
Když program s daným kódem přeložíte pomocí GCC, tak dostanete výše uvedené varování. Kde může být problém? Pokud bude proměnná i
obsahovat pouze nezáporné hodnoty, tak porovnání (2) proběhne dle očekávání. Problém může nastat, pokud i
obsahuje zápornou hodnotu. Mějme např. i = -1
a j = 1
. Výsledek i < j
by tudíž měl být 1
. Když ale program spustíte, zjistíte, že tomu tak není. Proč?
Dle normy, konkrétně sekce 6.3.1.8, se před vlastním porovnáním hodnoty typu int
a unsigned int
provede konverze hodnoty typu int
na hodnotu typu unsigned int
. Tudíž, z -1
se stane velmi vysoké číslo (konkrétně UINT_MAX). To se následně porovná s hodnotou 1
a výsledkem je, k velkém překvapení, to, že i > j
. Proto, pokud v programu porovnáváte znaménková a bezznaménková čísla, dobře se ujistěte, že nebudete porovnávat záporná čísla s kladnými, jinak vás může zaskočit nemilé chování vašeho programu.
gcc warnings
Dovolim si podotknout, ze co se tyce C (tedy, neplati pro g++), tak
-Wsign-compare
je soucasti-Wextra
, a da se explicitne vypnout. Driv bylo toto varovani soucasti-Wall
(ale to se psal rok 1996-7) :).Toto varovani je z 90% bogus: bud ten signed int nemuze byt nikdy zaporny, anebo se porovnava s -1 a ocekava se, ze se v ramci porovnani ona -1 prevede na unsigned. Nepisu 100%: vzpomenme si treba na read() vracejici -1 pri chybe. Nicmene zapinam
-Wno-sign-compare
v momente, kdy si gcc stezuje na konstrukce typu (i jeint
,ARRAY_SIZE()
vracisize_t
):a zadne jine. Reseni castovanim hodnot na
(int)
jen znecitelnuje kod.Re: gcc warnings
Díky za komentář. S tím, co jsi psal, souhlasím, ale i tak je třeba na to dávat pozor. Především je třeba chápat, kdy problém nevznikne, a kdy ano. Pokud si tohle člověk uvědomí, tak pak je vše v pořádku.
K tomu kódu. Nebylo by vhodnější prostě napsat
?
Vyhneš se tak navíc jednomu potenciálnímu problému, a to tomu, že pokud je
i
typuint
, a ta hodnotaARRAY_SIZE(bdevms)
bude větší než INT_MAX, tak tvůj program obsahuje konstrukci s nedefinovaným chováním (u znaménkových typů není normou definováno, co se má stát v případě přetečení -- je to definováno pouze u bezznaménkových typů). Ale to je spíše teoretická poznámka...Re: gcc warning
Jasne -- ja bych to tak napsal. Obvykle vsechno prekladam s
-Wall -Wextra
a chci cisty vystup bez ruznych varovani. Toto je jen ilustrace toho, kdy si gcc stezuje zbytecne.