C Part I · Module 3 · Expressions and Operators
Comparison Operators
Chapter 3.4 gave you a way to change an object. This chapter gives you a way to ask a question about values — whether one is larger, whether two are the same — and the answers turn out to be ordinary numbers rather than anything special. That is worth knowing precisely, because C's comparison operators produce an int, not a dedicated truth type, and the difference explains behaviour you will meet repeatedly. The chapter then does the thing that makes it one of the more important in the module: it returns to the comparison Chapter 2.6 showed you, where −1 is reported as not less than 1, and replaces the observation with a rule you can apply and a reason you can state.
Foundation12 min readCPart IFundamentalsExpressions
Part I · Module 3 · Chapter 3.5 · Comparison Operators
1. Asking a Question About Two Values
C has six operators for comparing values. Four compare magnitude and two compare for sameness:
a < b is a less than b?
a <= b is a less than or equal to b?
a > b is a greater than b?
a >= b is a greater than or equal to b?
a == b are a and b equal?
a != b are a and b different?Each is an operator with two operands, so each forms an expression — and Chapter 3.1's two questions apply to it as they do to everything else. What value does it produce, and what type?
2. The Answer Is an Ordinary Number
#include <stdio.h>
int main(void)
{
printf("4 < 7 is %d\n", 4 < 7);
printf("4 > 7 is %d\n", 4 > 7);
printf("4 == 7 is %d\n", 4 == 7);
printf("4 != 7 is %d\n", 4 != 7);
printf("4 <= 4 is %d\n", 4 <= 4);
return 0;
}Output:
4 < 7 is 1
4 > 7 is 0
4 == 7 is 0
4 != 7 is 1
4 <= 4 is 1Note the %d in every line. That is not a trick — it works because of the precise answer to Chapter 3.1's second question:
A comparison produces an
int, with the value 1 if the comparison holds and 0 if it does not.
Not a bool. Not a special truth object. An int, which you can print with %d, store in an int, or use in arithmetic. Chapter 2.10 established why: C's truth model — zero is false, anything else is true — predates its bool type by decades, and comparisons were producing plain integers long before there was anything else for them to produce.
You can store the result like any other value:
#include <stdio.h>
int main(void)
{
int temperature = 30;
int limit = 25;
int too_hot = temperature > limit;
printf("too_hot is %d\n", too_hot);
return 0;
}Output:
too_hot is 1too_hot is an ordinary int holding 1. If you would rather the object say what it means, declare it bool instead — Chapter 2.10 explained what that changes and what it does not.
3. = and == Are Different Operators
Chapter 3.4 introduced = and deliberately withheld == until now, because the two are easily confused and the confusion is expensive.
= store the right-hand value into the left-hand object
== ask whether two values are equalDifferent jobs, and only one of them changes anything. What makes this dangerous is that writing the wrong one is frequently still valid C:
#include <stdio.h>
int main(void)
{
int value = 3;
int check = (value = 5);
printf("check is %d, value is now %d\n", check, value);
return 0;
}Output:
check is 5, value is now 5The author almost certainly meant value == 5 — is value equal to 5? — which would have produced 0, because value held 3. Instead the line stored 5 into value and produced 5, so check is 5 and the object has been changed as a side effect.
Nothing here is a C error. The compiler accepted it because Chapter 3.4 established that an assignment is an expression with a value, and this is a perfectly ordinary use of one.
4. The Comparison That Reports the Opposite
Chapter 2.6 showed you a comparison whose result contradicts the code. Here it is again, now with a companion line that isolates the cause:
#include <stdio.h>
int main(void)
{
int signed_count = -1;
unsigned int unsigned_limit = 1;
printf("-1 < 1 as written : %d\n", signed_count < unsigned_limit);
printf("-1 < 1 both signed: %d\n", -1 < 1);
return 0;
}Output:
-1 < 1 as written : 0
-1 < 1 both signed: 1The second line is what arithmetic says: −1 is less than 1. The first line uses the same two numbers and reports the opposite.
The difference is the type of the second operand, and here is the rule:
When a comparison has one signed operand and one unsigned operand of the same width, C converts the signed value to the unsigned type before comparing.
So the first comparison never asks is −1 less than 1. By the time the comparison happens, the −1 has been converted, and Chapter 2.7 told you what that conversion produces: the bit pattern of −1 read as unsigned is the largest value the type can hold. The comparison being performed is therefore is 4294967295 less than 1, and the answer to that is correctly 0.
Nothing malfunctioned. The operands were changed before the operator saw them, and the operator then did its job properly on values you did not write.
This is why Chapter 3.1 asked you to get into the habit of asking about the type of every expression, not just its value. The value here is entirely explained by the types.
Your compiler will warn if you ask it to:
warning: comparison of integers of different signs: 'int' and 'unsigned int' [-Wsign-compare]which is another entry in the list of reasons the options from Chapter 1.5 are not optional. Wording differs between compilers; the category is what to recognise.
5. Practical Guidance
Three things follow, and they are worth adopting as habits rather than remembered as rules.
Avoid comparing signed with unsigned. If both values are quantities, make them both signed — which is Chapter 2.6's default advice, and §4 is a large part of why. When you genuinely must compare across signedness, be explicit about what you want; Chapter 3.12 gives you the tool.
Comparing floating-point values for equality needs care. Chapter 2.9 covered this in full: == is reliable for exactly representable values and unreliable for anything computed from decimal fractions. The operator is not the problem; the values are.
Comparing a truth value against 1 is a mistake. From §2: a true value need not be 1. Compare against 0 if you must compare at all — x != 0 is always meaningful, x == 1 frequently is not.
6. What You Should Be Able to Say Now
- C has six comparison operators:
<,<=,>,>=,==,!=. - A comparison produces an
int— 1 if it holds, 0 if it does not. Not abool, not a special type. - That result can be printed with
%d, stored in anint, or stored in aboolif you prefer the intent stated. - A true value need not be 1, which is why comparing against
1is unsafe and comparing against0is not. =stores;==asks. Writing one for the other is often still valid C and changes an object as a side effect.- Compilers warn about the slip only in some positions, and extra parentheses suppress the warning.
- Mixing signed and unsigned converts the signed operand first, which is why
-1 < 1uis 0 while-1 < 1is 1. - Nothing is broken there: the operands were converted before the operator ran.
-Wsign-comparecatches it. Prefer not to mix signedness at all.
7. Exercise
Exercise 1 — predict, then run. Give the exact output:
printf("%d\n", 10 >= 10);
printf("%d\n", 10 != 10);
printf("%d\n", 3 < 2);
printf("%d\n", (2 < 3) + (4 < 5));The last line is deliberate — explain why it is legal.
Exercise 2 — say what changed. For each, say what is printed and whether any object was modified:
int n = 4;
printf("%d\n", n == 6);int n = 4;
printf("%d\n", n = 6);Exercise 3 — explain the reversal. Write two sentences explaining why signed_count < unsigned_limit gave 0 in §4. Your explanation must mention conversion and must not use the word "bug".
Exercise 4 — predict the warning. Which of these does -Wsign-compare complain about, and why does it not complain about the others?
int a = -1;
unsigned int b = 1;
int c = 2;
a < b;
a < c;
b < b;Exercise 5 — repair it. A function-free program tracks int remaining and compares it against an unsigned int capacity. Describe the failure that can occur when remaining is negative, and give the simplest change that removes the risk entirely.
Exercise 6 — judge the comparison. For each, say whether == is a reasonable thing to write, referring to Chapter 2.9 where relevant: two int values; two double values computed from decimal fractions; a double compared against 0.5; a truth value compared against 1.
8. Summary
- Six comparison operators, four about magnitude and two about sameness.
- Each produces an
int— 1 or 0 — because C's truth model predates itsbooltype. - True does not mean 1. Any non-zero value is true, so compare against
0, never against1. =stores,==asks. The slip is usually valid C, changes an object, and is only sometimes warned about.- Signed meets unsigned and the signed operand is converted first, which is the whole explanation for
-1 < 1ubeing 0. - The operator did its job correctly on values you did not write. That is the pattern, and Chapter 3.12 gives the general rules.
- Do not mix signedness in comparisons, and treat floating-point equality as Chapter 2.9 described.
You can now ask a single question about two values. Real programs ask several at once — this and that, this or that — and C's operators for combining questions have a property that makes them different from every operator you have met so far: sometimes they do not evaluate one of their operands at all.
Standards & specifications
- Governing standard
- ISO/IEC 9899 (the C language standard)(opens ISO/IEC (JTC 1/SC 22/WG 14) in a new tab)
Defines the C language itself — the type system, expression semantics, object model, storage duration and the behaviour categories a conforming implementation must honour. It is a FAMILY of editions (C89/C90, C99, C11, C17, C23) and this curriculum names the edition wherever a claim depends on one. It does not define any foreign-function interface to a hardware description language, and it does not define platform behaviour: what a memory-mapped access does on real silicon is the implementation's and the device's contract, not this standard's. Working drafts published by WG14 are freely available and are what most engineers read day to day; a draft is not the published standard.
This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.
Where this fits
Part of the C Programming curriculum.
