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.
See Also
- Unstated coding style - December 21, 2011
- i18n console output in C - February 15, 2012
- Road to pacman 3.2.0 - February 28, 2008
- Valgrind 3.3.0 and the new massif - January 23, 2008
- Using gcov for code coverage testing - December 2, 2007