Jste zde

warning: comparison between signed and unsigned integer expressions

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.

Komentáře

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 je int, ARRAY_SIZE() vraci size_t):

for (i = 0; i < ARRAY_SIZE(bdevms); i++) {
        /* ... */
}

a zadne jine. Reseni castovanim hodnot na (int) jen znecitelnuje kod.

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

for (size_t i = 0; i < ARRAY_SIZE(bdevms); i++) {
        // ...
}

?

Vyhneš se tak navíc jednomu potenciálnímu problému, a to tomu, že pokud je i typu int, a ta hodnota ARRAY_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...

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.

Přidat komentář