Beyond basic if-else
and switch
blocks, C provides powerful conditional mechanisms that are essential for writing concise, efficient, and robust code.
1. The
Conditional (Ternary) Operator ?:
The ternary
operator is a compact, inline if-else
. It's C's only operator that takes three operands.
It's an expression, meaning it evaluates to a value, which makes it
perfect for conditional assignments.
·
Syntax: condition ? value_if_true : value_if_false
Example: Conditional Assignment
Instead of writing
a full if-else
block to find the maximum of two numbers, you can use
the ternary operator for a single, clean assignment.
C
#include <stdio.h>
int main() {
int a =
10, b =
20;
int max;
// Verbose if-else block
if (a > b) {
max = a;
}
else {
max = b;
}
// Concise ternary operator equivalent
max = (a > b) ? a : b;
printf(
"The maximum value is: %d\n", max);
// Prints 20
return
0;
}
2. Short-Circuit
Evaluation (&&
and ||
)
The logical AND (&&
) and OR (||
) operators exhibit a crucial behavior known as short-circuit
evaluation. This is a guaranteed rule in C.
·
A && B
: If A
evaluates to false (0), the entire expression must be false, so B
is never evaluated.
·
A || B
: If A
evaluates to true (non-zero), the entire expression must be true, so B
is never evaluated.
This is not just an optimization; it's a powerful feature for writing safe code, especially when dealing with pointers.
Example: Safe Pointer Dereferencing
This is the most critical use case. You can check if a pointer is valid before attempting to use it in the same expression.
C
#include <stdio.h>
struct Node {
int data;
struct Node* next;
};
void process_node(struct Node *node) {
// Because of short-circuiting, if 'node' is NULL, the second part
// (node->data == 100) is NEVER evaluated, preventing a crash.
if (node !=
NULL && node->data ==
100) {
printf(
"Node exists and its data is 100.\n");
}
else {
printf(
"Node is NULL or its data is not 100.\n");
}
}
int main() {
struct Node *my_node =
NULL;
process_node(my_node);
// Safely handles the NULL pointer
return
0;
}
3. Advanced switch
Statement: Intentional Fall-Through
The case
labels in a switch
are just entry points. Without a break
, execution will fall through to the statements
of the next case. While often a source of bugs, this can be used intentionally
to group cases.
Example: Grouping Character Types
C
#include <stdio.h>
int main() {
char ch =
'7';
switch (ch) {
case
'a':
case
'e':
case
'i':
case
'o':
case
'u':
printf(
"It's a lowercase vowel.\n");
break;
case
'0':
case
'1':
case
'2':
case
'3':
case
'4':
case
'5':
case
'6':
case
'7':
case
'8':
case
'9':
printf(
"It's a digit.\n");
break;
default:
printf(
"It's some other character.\n");
break;
}
return
0;
}
4. Conditions Based on Assignment Expressions
A very common and idiomatic C pattern is to use the result of an assignment expression directly as a condition. The value of an assignment expression is the value that was assigned.
· The Idiom: This is most frequently seen in loops that read input.
Example: The while ((c =
getchar()) != EOF)
Loop
This compact line of code performs three steps in order:
1. getchar()
is called, reading a character from input.
2. The returned character is assigned to the variable c
.
3. The value of that entire assignment (which is the
character that was just read) is then compared to EOF
(End Of File).
C
#include <stdio.h>
int main() {
int c;
// Must be int to hold EOF
printf(
"Type some text and press Enter (Ctrl+D to end):\n");
// This loop reads, assigns, and checks in one line
while ((c = getchar()) != EOF) {
putchar(c);
}
return
0;
}
Note: The extra parentheses (c = getchar())
are crucial. Because !=
has higher precedence than =
, c = getchar() != EOF
would be incorrectly parsed as c = (getchar() !=
EOF)
, which would assign 0
or 1
to c
.
Branching statements, also known as jump statements, unconditionally transfer the flow of a program's control from one point to another.