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.