I am currently writing a C program that requires frequent comparisons of string lengths so I wrote the following helper function:
int strlonger(char *s1, char *s2) {
return strlen(s1) - strlen(s2) > 0;
}
I have noticed that the function returns true even when s1 has shorter length than s2. Can someone please explain this strange behavior?
What you’ve come across is some peculiar behavior that arises in C when handling expressions that contain both signed and unsigned quantities.
When an operation is performed where one operand is signed and the other is unsigned, C will implicitly convert the signed argument to unsigned and perform the operations assuming the numbers are nonnegative. This convention often leads to nonintuitive behavior for relational operators such as
<and>.Regarding your helper function, note that since
strlenreturns typesize_t(an unsigned quantity), the difference and the comparison are both computed using unsigned arithmetic. Whens1is shorter thans2, the differencestrlen(s1) - strlen(s2)should be negative, but instead becomes a large, unsigned number, which is greater than0. Thus,returns
1even ifs1is shorter thans2. To fix your function, use this code instead:Welcome to the wonderful world of C! 🙂
Additional Examples
Since this question has recently received a lot of attention, I’d like to provide a few (simple) examples, just to ensure that I am getting the idea across. I will assume that we are working with a 32-bit machine using two’s complement representation.
The important concept to understand when working with unsigned/signed variables in C is that if there is a mix of unsigned and signed quantities in a single expression, signed values are implicitly cast to unsigned.
Example #1:
Consider the following expression:
Since the second operand is unsigned, the first one is implicitly cast to unsigned, and hence the expression is equivalent to the comparison,
which of course is false. This is probably not the behavior you were expecting.
Example #2:
Consider the following code that attempts to sum the elements of an array
a, where the number of elements is given by parameterlength:This function is designed to demonstrate how easily bugs can arise due to implicit casting from signed to unsigned. It seems quite natural to pass parameter
lengthas unsigned; after all, who would ever want to use a negative length? The stopping criterioni <= length-1also seems quite intuitive. However, when run with argumentlengthequal to0, the combination of these two yields an unexpected outcome.Since parameter
lengthis unsigned, the computation0-1is performed using unsigned arithmetic, which is equivalent to modular addition. The result is then UMax. The<=comparison is also performed using an unsigned comparison, and since any number is less than or equal to UMax, the comparison always holds. Thus, the code will attempt to access invalid elements of arraya.The code can be fixed either by declaring
lengthto be anint, or by changing the test of theforloop to bei < length.Conclusion: When Should You Use Unsigned?
I don’t want to state anything too controversial here, but here are some of the rules I often adhere to when I write programs in C.
DON’T use just because a number is nonnegative. It is easy to make mistakes, and these mistakes are sometimes incredibly subtle (as illustrated in Example #2).
DO use when performing modular arithmetic.
DO use when using bits to represent sets. This is often convenient because it allows you to perform logical right shifts without sign extension.
Of course, there may be situations in which you decide to go against these “rules”. But most often than not, following these suggestions will make your code easier to work with and less error-prone.