warning: comparison between signed and unsigned integer expressions

Od Petr Zemek, 2010-11-03

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.

Obsah tohoto pole je soukromý a nebude veřejně zobrazen.

Filtrované HTML (využíváno)

  • Povolené HTML značky: <a href hreflang> <em> <strong> <cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <table>
  • Zvýraznění syntaxe kódu lze povolit přes následující značky: <code>, <blockcode>, <bash>, <c>, <cpp>, <haskell>, <html>, <java>, <javascript>, <latex>, <perl>, <php>, <python>, <ruby>, <rust>, <sql>, <text>, <vim>, <xml>, <yaml>.
  • Řádky a odstavce se zalomí automaticky.
  • Webové a e-mailové adresy jsou automaticky převedeny na odkazy.
CAPTCHA
3 + 4 =
Vyřešte tento jednoduchý matematický příklad a vložte výsledek. Např. pro 1+3 vložte 4.
Nějak se mi tady rozmohl spam, takže poprosím o ověření.

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...