Conditional Statements
Introduction
Conditional statements are fundamental control structures that allow programs to make decisions and execute different code paths based on specific conditions. They enable programs to respond dynamically to varying inputs, user interactions, and runtime conditions.
In C, conditional statements provide the foundation for implementing logic, validation, error handling, and complex decision-making processes. This chapter will explore the various conditional constructs available in C, their syntax, best practices, and common use cases.
The if Statement
The if statement is the most basic conditional construct in C. It executes a block of code only if a specified condition evaluates to true (non-zero).
Basic Syntax
if (condition) {
// Code to execute if condition is true
}Simple if Statement
#include <stdio.h>
int main() {
int age = 20;
if (age >= 18) {
printf("You are eligible to vote.\n");
}
return 0;
}if-else Statement
The if-else construct allows you to specify alternative code paths for when the condition is true or false:
#include <stdio.h>
int main() {
int number = 15;
if (number % 2 == 0) {
printf("%d is even.\n", number);
} else {
printf("%d is odd.\n", number);
}
return 0;
}if-else if-else Chain
For multiple conditions, you can chain if-else if-else statements:
#include <stdio.h>
int main() {
int score = 85;
if (score >= 90) {
printf("Grade: A\n");
} else if (score >= 80) {
printf("Grade: B\n");
} else if (score >= 70) {
printf("Grade: C\n");
} else if (score >= 60) {
printf("Grade: D\n");
} else {
printf("Grade: F\n");
}
return 0;
}The switch Statement
The switch statement provides an alternative to long if-else if chains when comparing a single variable against multiple constant values.
Basic Syntax
switch (expression) {
case constant1:
// Code for constant1
break;
case constant2:
// Code for constant2
break;
// ... more cases
default:
// Code for no match
}Simple switch Example
#include <stdio.h>
int main() {
int day = 3;
switch (day) {
case 1:
printf("Monday\n");
break;
case 2:
printf("Tuesday\n");
break;
case 3:
printf("Wednesday\n");
break;
case 4:
printf("Thursday\n");
break;
case 5:
printf("Friday\n");
break;
case 6:
printf("Saturday\n");
break;
case 7:
printf("Sunday\n");
break;
default:
printf("Invalid day\n");
}
return 0;
}switch Without break Statements
#include <stdio.h>
int main() {
int choice = 2;
printf("Menu:\n");
printf("1. Pizza\n");
printf("2. Burger\n");
printf("3. Salad\n");
switch (choice) {
case 1:
printf("You ordered Pizza - $10\n");
// Fall through to case 2
case 2:
printf("You ordered Burger - $8\n");
// Fall through to case 3
case 3:
printf("You ordered Salad - $5\n");
break;
default:
printf("Invalid choice\n");
}
return 0;
}Nested Conditionals
Conditionals can be nested within other conditionals to handle complex decision-making scenarios:
#include <stdio.h>
int main() {
int age = 25;
int has_license = 1; // 1 for true, 0 for false
if (age >= 18) {
if (has_license) {
printf("You can drive legally.\n");
} else {
printf("You need to get a license.\n");
}
} else {
printf("You are not old enough to drive.\n");
}
return 0;
}Conditional Expressions
C also supports the conditional (ternary) operator as a shorthand for simple if-else statements:
#include <stdio.h>
int main() {
int a = 10, b = 20;
// Traditional if-else
int max1;
if (a > b) {
max1 = a;
} else {
max1 = b;
}
// Conditional operator equivalent
int max2 = (a > b) ? a : b;
printf("Max (if-else): %d\n", max1);
printf("Max (conditional): %d\n", max2);
// More complex conditional expressions
char *status = (age >= 18) ? "adult" : "minor";
printf("Status: %s\n", status);
return 0;
}Best Practices for Conditionals
1. Use Braces Consistently
// Good: Always use braces
if (condition) {
do_something();
}
// Avoid: Inconsistent brace usage
if (condition)
do_something();
else
do_something_else();2. Handle All Cases in switch
// Good: Include default case
switch (value) {
case 1:
handle_case_1();
break;
case 2:
handle_case_2();
break;
default:
handle_default();
break;
}
// Avoid: Missing default case
switch (value) {
case 1:
handle_case_1();
break;
case 2:
handle_case_2();
break;
}3. Avoid Deep Nesting
// Good: Early returns to reduce nesting
int process_data(int data) {
if (data < 0) {
return -1; // Error
}
if (data > 100) {
return -2; // Error
}
// Process valid data
return data * 2;
}
// Avoid: Deep nesting
int process_data(int data) {
if (data >= 0) {
if (data <= 100) {
return data * 2;
} else {
return -2; // Error
}
} else {
return -1; // Error
}
}4. Use Descriptive Conditions
// Good: Descriptive variable names
int is_adult = (age >= 18);
int has_permission = (permission_level > 0);
if (is_adult && has_permission) {
grant_access();
}
// Avoid: Complex conditions in if statement
if (age >= 18 && permission_level > 0 && user_active && !account_locked) {
grant_access();
}Common Pitfalls and How to Avoid Them
1. Assignment vs. Comparison
#include <stdio.h>
int main() {
int x = 5;
// Wrong: Assignment instead of comparison
if (x = 10) { // This assigns 10 to x and always evaluates to true
printf("This will always execute!\n");
}
// Correct: Comparison
if (x == 10) {
printf("This checks if x equals 10\n");
}
// Better: Prevent assignment in condition
if ((x = 10)) { // Extra parentheses make assignment intentional
printf("Assigned x = 10\n");
}
return 0;
}2. Floating-Point Comparisons
#include <stdio.h>
#include <math.h>
int main() {
double a = 0.1 + 0.2;
double b = 0.3;
// Wrong: Direct floating-point comparison
if (a == b) {
printf("Equal\n");
} else {
printf("Not equal (a = %.20f, b = %.20f)\n", a, b);
}
// Correct: Use epsilon for floating-point comparison
double epsilon = 1e-9;
if (fabs(a - b) < epsilon) {
printf("Approximately equal\n");
}
return 0;
}3. Missing break in switch
#include <stdio.h>
int main() {
int choice = 1;
// Wrong: Missing break causes fall-through
switch (choice) {
case 1:
printf("Option 1\n");
// Missing break - falls through to case 2
case 2:
printf("Option 2\n");
break;
default:
printf("Default option\n");
}
// Correct: Include break statements
switch (choice) {
case 1:
printf("Option 1\n");
break;
case 2:
printf("Option 2\n");
break;
default:
printf("Default option\n");
}
return 0;
}Practical Examples
Grade Calculator with Conditionals
#include <stdio.h>
int main() {
float score;
printf("Enter student score (0-100): ");
scanf("%f", &score);
// Validate input
if (score < 0 || score > 100) {
printf("Invalid score! Please enter a value between 0 and 100.\n");
return 1;
}
// Determine letter grade
if (score >= 90) {
printf("Grade: A (Excellent)\n");
} else if (score >= 80) {
printf("Grade: B (Good)\n");
} else if (score >= 70) {
printf("Grade: C (Average)\n");
} else if (score >= 60) {
printf("Grade: D (Below Average)\n");
} else {
printf("Grade: F (Fail)\n");
}
// Provide additional feedback
if (score >= 70) {
printf("Congratulations! You passed.\n");
} else {
printf("You need to improve your performance.\n");
}
return 0;
}Leap Year Checker
#include <stdio.h>
int main() {
int year;
printf("Enter a year: ");
scanf("%d", &year);
// Leap year logic
if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) {
printf("%d is a leap year.\n", year);
} else {
printf("%d is not a leap year.\n", year);
}
// Alternative implementation with nested conditionals
printf("\nAlternative implementation:\n");
if (year % 4 == 0) {
if (year % 100 == 0) {
if (year % 400 == 0) {
printf("%d is a leap year.\n", year);
} else {
printf("%d is not a leap year.\n", year);
}
} else {
printf("%d is a leap year.\n", year);
}
} else {
printf("%d is not a leap year.\n", year);
}
return 0;
}Advanced Conditional Techniques
Short-Circuit Evaluation
#include <stdio.h>
int main() {
int a = 5, b = 0;
// Short-circuit prevents division by zero
if (b != 0 && a / b > 1) {
printf("Division is safe and result > 1\n");
} else {
printf("Either b is zero or division result <= 1\n");
}
// Short-circuit can be used for efficiency
int expensive_function_calls = 0;
// Function won't be called if first condition is false
if (a > 10 && (++expensive_function_calls > 0)) {
printf("This won't execute\n");
}
printf("Expensive function calls: %d\n", expensive_function_calls);
return 0;
}Complex Conditional Logic
#include <stdio.h>
#include <stdbool.h>
int main() {
bool is_weekend = true;
bool is_holiday = false;
int temperature = 75;
bool has_umbrella = true;
bool is_raining = false;
// Complex decision making
bool should_go_outside =
(is_weekend || is_holiday) &&
(temperature >= 60 && temperature <= 85) &&
(!is_raining || has_umbrella);
if (should_go_outside) {
printf("Great weather to go outside!\n");
} else {
printf("Better stay inside today.\n");
}
return 0;
}Summary
In this chapter, you’ve learned about:
- if Statements: Basic conditional execution
- if-else Statements: Binary decision making
- if-else if-else Chains: Multiple condition handling
- switch Statements: Efficient constant value comparison
- Nested Conditionals: Complex decision trees
- Conditional Operators: Ternary expressions
- Best Practices: Writing clean, maintainable conditional code
- Common Pitfalls: Avoiding typical mistakes
- Advanced Techniques: Short-circuit evaluation and complex logic
Conditional statements are essential for implementing program logic and decision-making capabilities. In the next chapter, we’ll explore loops, which allow programs to repeat actions efficiently.