Skip to main content

Overview

Conditional Operator (?:)

  • Definition: A ternary operator that evaluates a condition and returns one of two values based on whether the condition is truthy or falsey

  • Syntax: condition ? value_if_true : value_if_false

  • Key Characteristic: The conditional operator is an expression, not a control flow statement. It produces a value that can be used in larger expressions

  • Common Misuse: Treating ?: as a shorthand if/else by performing actions within the operator

    // INCORRECT - treats ?: like if/else
    (number > 0) ? printf("positive\n") : printf("not positive\n");

    // CORRECT - uses ?: as an expression
    printf("%d is %s\n", number, (number > 0) ? "positive" : "not positive");
  • Appropriate Use Cases:

    • Assigning one of two values to a variable
    • Passing one of two values as a function argument
    • Returning one of two values from a function
    • Any situation where the result of the operator is used, not discarded
  • Advantage: Can make simple conditional assignments more concise and readable

  • Disadvantage: Complex nested conditionals become difficult to read; if/else is clearer in such cases

Switch Statements

  • Definition: A control flow statement that performs value-based selection by matching an expression against multiple case labels
  • Syntax:
    switch(expression)
    {
    case value1:
    // statements
    break;
    case value2:
    // statements
    break;
    default:
    // statements
    }
  • Expression Requirements: The expression must be an integral type (int, char, etc.); complex boolean operations (>, <, >=, <=) are not supported
  • Case Labels: Each case label must be a constant value; multiple cases can precede the same statements
  • Break Statement: Terminates the switch block; without break, execution continues to the next case (fall-through behavior)
    switch(rank)
    {
    case 1:
    printf("A\n");
    break; // execution stops here
    case 11:
    printf("J\n");
    break;
    default:
    printf("%d\n", rank);
    }
  • Default Case: Optional; executes if no case matches the expression
  • Fall-Through: If break is omitted, execution continues into the next case; this is sometimes intentional but often a source of bugs
    switch(day)
    {
    case 1:
    case 2:
    case 3:
    printf("Weekday\n"); // executes for cases 1, 2, or 3
    break;
    case 6:
    case 7:
    printf("Weekend\n");
    break;
    }
  • Advantages:
    • More readable than long if/else if/else chains for value matching
    • Efficient for matching against many discrete values
    • Clear intent when checking a single expression against multiple values
  • Disadvantages:
    • Cannot handle range checks or complex conditions
    • Fall-through behavior can introduce subtle bugs if break is forgotten
    • Less flexible than if/else for non-equality comparisons

Pointers and Arrays

  • Fundamental Relationship: The name of an array is a pointer to the first element of that array
    int myArray[10];
    // myArray resolves to &myArray[0]
  • Array and Pointer Parameter Equivalence: In function parameters, array notation and pointer notation are interchangeable
    // These two declarations are equivalent:
    void someFunction(int array[], int used);
    void someFunction(int* array, int used);
  • Implication: An array can be passed to a function expecting a pointer, and vice versa
  • String Functions: Library functions like strlen() accept const char* parameters because strings are character arrays
    size_t strlen(const char* str);
    // Can be called with: strlen(myCharArray);

sizeof() Operator

  • Definition: An operator (not a function) that returns the size in bytes of a type or variable
  • Behavior on Locally-Declared Arrays: Returns the total size of the array in bytes
    char mystr[100];
    int size = sizeof(mystr); // Returns 100 (size of entire array)
  • Behavior on Array Parameters: When an array is passed to a function, it decays to a pointer; sizeof() returns the size of a pointer, not the array
    void checkSize(char mystr[])
    {
    int size = sizeof(mystr); // Returns size of pointer (typically 4 or 8 bytes)
    // NOT the size of the original array!
    }
  • Critical Limitation: sizeof() cannot reliably determine array capacity when the array is passed as a function parameter
  • Correct Usage: Use sizeof() only on locally-declared arrays or when you know the exact type and size
  • Best Practice: Pass array capacity as a separate parameter rather than relying on sizeof()
    // CORRECT approach
    void processArray(int array[], int capacity)
    {
    // capacity is passed explicitly
    }
  • Common Pitfall: Attempting to use sizeof() to calculate the number of elements in an array parameter
    // WRONG - will not work as intended
    int numElements = sizeof(array) / sizeof(array[0]); // in a function parameter