toofishes.net

C, unsigned integers, and the infinite for loop

Take a close look at the two snippets below. If you can see what is wrong in the first one, you either 1) paid attention to the blog article title or 2) have likely been burned by this before.

Incorrect

size_t i;
alpm_filelist_t *filelist = alpm_pkg_get_files(pkg);
/* iterate through the list backwards, unlinking files */
for(i = filelist->count - 1; i >= 0; i--) {
    alpm_file_t *file = filelist->files + i;
    unlink_file(file);
}

Correct

size_t i;
alpm_filelist_t *filelist = alpm_pkg_get_files(pkg);
/* iterate through the list backwards, unlinking files */
for(i = filelist->count; i > 0; i--) {
    alpm_file_t *file = filelist->files + i - 1;
    unlink_file(file);
}

What we see here is a classic case of a unsigned variable combined with an optimizing C compiler. Unfortunately in this case warnings are not emitted and a few vocal WTFs and inline printf calls ensued before I realized exactly what was happening.

In the incorrect code, the i >= 0 loop test might as well not even be there, as size_t is defined as an unsigned integer. That results in 0--, yielding some very large integer value, which is of course greater than zero.

The correct code ensures our iteration loop variable never takes a value “below” zero by virtue of moving the minus one offset around. Keep this in mind anytime you are iterating an array backwards (the easiest place to fall into this trap)- if your loop variable is unsigned, letting it go past zero is a definite no-no.

Tags

See Also