Module 1: Chapter 2 — Expressions

Expressions in C

Expressions are the fundamental building blocks of any C program. They combine data and operators to perform computations and produce results.

Key Points About Expressions:

The Basic Data Types

C89 defines five fundamental data types that form the foundation of the language:

Important Notes:

Modifying the Basic Types

C allows type modifiers to adjust the meaning of base data types for precision, size, or sign handling.

Available Modifiers:

Key Notes About Modifiers:

  • int can be modified with signed, unsigned, short, long.
  • char can be signed or unsigned.
  • double can be modified by long (i.e., long double).
  • In C99 → long long and unsigned long long were added.
  • By default, int is signed.

Data Type Sizes and Ranges

The following table summarizes the minimal ranges and typical sizes for all C data types:

Type Typical Bits Minimal Range
char 8 -127 to 127
unsigned char 8 0 to 255
int 16 / 32 -32,767 to 32,767
unsigned int 16 / 32 0 to 65,535
long int 32 -2,147,483,647 to 2,147,483,647
long long int (C99) 64 -(263 - 1) to 263 - 1
unsigned long long int (C99) 64 0 to 264 - 1
float 32 1E-37 to 1E+37 (~6 digits precision)
double 64 1E-37 to 1E+37 (~10 digits precision)
long double 80 1E-37 to 1E+37 (~10 digits precision, extended range)

Signed vs. Unsigned Integers

Type Specifier Shortcuts

When a modifier is used without a base type, int is assumed.

Identifiers and Variables in C

Identifiers

Variables

A variable is a named memory location that stores a value which may change during program execution. Every variable must be declared before use.

Examples of Variable Declarations:

int i, j, l;
short int si;
unsigned int ui;
double balance, profit, loss;

Where Variables Are Declared

Local Variables

Example: Variable Shadowing

#include <stdio.h>
int main(void) {
   int x = 10;
   if (x == 10) {
   // hides the outer x
      int x = 99;
      printf("Inner x: %d\n", x);
   }
   printf("Outer x: %d\n", x);
   return 0;
}

Output: Inner x: 99 and Outer x: 10

In C89, local variables must be declared at the start of a block. In C99 and C++, variables can be declared at any point before first use.

Formal Parameters

Example:

int is_in(char *s, char c) {
  while (*s)
    if (*s == c) return 1;
    else s++;
  return 0;
}

Global Variables

Example:

#include <stdio.h>
int count; // global
void func1(void) {
   int temp = count;
   printf("func1 temp: %d\n", temp);
}

void func2(void) {
   int count; // local
   for (count = 1; count < 5; count++)
     putchar('.');
}

int main(void) {
  count = 100;
  func1();
  func2();
  printf("Global count: %d\n", count);
  return 0;
}

The Four Scopes in C

Scope Meaning
File Scope Identifiers declared outside all functions; visible throughout the file. (Global variables)
Block Scope Identifiers declared inside a block (between { and }). Includes function parameters.
Function Prototype Scope Applies to identifiers declared in function prototypes; visibility limited to the prototype itself.
Function Scope Applies only to labels (targets of goto) inside a function.

Type Qualifiers in C

const

volatile

Storage Class Specifiers in C

extern

static

Variable Initialization

In C, variables can be assigned an initial value at the time of declaration. Initialization allows assigning a known starting value, preventing the use of garbage data.

General Form

type variable_name = constant;

Examples

char ch = 'a';
int first = 0;
double balance = 123.23;

Important Points

Constants

Constants are fixed values that do not change during program execution. They are also called literals and can belong to any data type.

Types of Constants

Type Suffixes

Examples:

int a = 123; // Integer constant
long int b = 35000L; // Long integer
unsigned int c = 100U; // Unsigned integer
float f = 12.34F; // Float constant
double d = 123.45; // Double constant
long double ld = 1001.2L; // Long double constant

💡 Quick Tip: Use suffixes like L, U, or F to explicitly define constant types and prevent type conversion errors.

Hexadecimal and Octal Constants

What Are They?

Where Are They Used?

Syntax Rules

Examples:

int dec = 10; // Decimal 10
int oct = 012; // Octal 12 → Decimal 10
int hex = 0xA; // Hexadecimal A → Decimal 10
int hex2 = 0x80; // Hexadecimal 80 → Decimal 128
int color = 0xFF0000; // Hex code for red color

Quick Comparison

Format Prefix Base Digits Used Example Decimal Value
Decimal 10 0-9 10 10
Octal 0 8 0-7 012 10
Hexadecimal 0x or 0X 16 0-9, A-F 0xA 10

💡 Easy Trick to Remember: 0 → Octal, 0x → heXadecimal. The prefix tells the compiler which base you are using!

String Constants

A string constant is a sequence of characters enclosed in double quotes.

"This is a test"

Backslash Character Constants (Escape Sequences)

C supports special backslash character constants, also called escape sequences, for non-printing or special characters.


Code Meaning
\b Backspace
\f Form feed
\n New line
\r Carriage return
\t Horizontal tab
\" Double quote
\' Single quote
\\ Backslash
\v Vertical tab
\a Alert (beep)
\? Question mark
\N Octal constant
\xN Hexadecimal constant

Example:

#include <stdio.h>
int main(void) {
  printf("\n\tThis is a test.");
  return 0;
}

Output: Prints a new line, a tab space, and then “This is a test.”

Operators in C

C is a powerful and expressive language, rich in built-in operators that enable concise and efficient programming. Operators form the core of most C expressions and play a more central role in C than in many other programming languages. Broadly, C operators are classified into the following categories:

The Assignment Operator

In C, the assignment operator (=) can be used within expressions — a feature that distinguishes C from many older languages like BASIC, Pascal, or FORTRAN. The general form is:

variable_name = expression;

The left-hand side (lvalue) must be a variable or memory location that can store data, and the right-hand side (rvalue) is any valid expression whose value is assigned to that variable.

Example:

int x = 10;
int y;
y = x + 5; // y is assigned the value 15

Type Conversion in Assignments

When variables of different data types are mixed in an expression, C automatically converts one type to another, following specific conversion rules. In an assignment, the expression on the right-hand side is converted to the type of the variable on the left-hand side before the assignment takes place.

int x;
char ch;
float f;
ch = x; // int → char
x = f; // float → int
f = ch; // char → float
f = x; // int → float

Outcome of Common Type Conversions

Target Type Expression Type Possible Information Loss
char int (16 bits) High-order 8 bits lost
char int (32 bits) High-order 24 bits lost
short int int (32 bits) High-order 16 bits lost
int (32 bits) long int None
int float Fractional part lost
float double Rounding due to precision limits

Multiple and Compound Assignments

Arithmetic Operators

Arithmetic operators perform basic mathematical calculations. The modulus operator (%) works only with integers. Division between integers truncates the fractional part.

Operator Action
+ Addition
- Subtraction
* Multiplication
/ Division
% Modulus (remainder of division)
++ Increment by 1
-- Decrement by 1

Example:

int x = 5, y = 2;
printf("%d ", x / y); // Output: 2
printf("%d", x % y); // Output: 1

Increment and Decrement Operators

The increment (++) and decrement (--) operators simplify repetitive addition or subtraction by one. They can be used as prefix or postfix operators:

int x = 10, y;
y = ++x; // Prefix: increments before use
y = x++; // Postfix: increments after use

Prefix and postfix increments differ in the timing of the operation relative to the expression evaluation.

Relational and Logical Operators

Relational operators compare two values, while logical operators combine or invert logical conditions. In C, any nonzero value is considered true, and zero is false.

Relational Operators

Operator Meaning
> Greater than
>= Greater than or equal
< Less than
<= Less than or equal
== Equal to
!= Not equal to

Logical Operators

Operator Meaning
&& AND
|| OR
! NOT

Example:

if (x > 5 && !(y < 3) || z < = 10)
  printf("Condition is true");

Example: Implementing Logical XOR

#include <stdio.h>
int xor(int a, int b) {
  return (a || b) && !(a && b);
}
int main(void) {
  printf("%d", xor(1, 0)); // Output: 1
  printf("%d", xor(1, 1)); // Output: 0
  printf("%d", xor(0, 1)); // Output: 1
  printf("%d", xor(0, 0)); // Output: 0
  return 0;
}

Operator Precedence

Operators are evaluated in a specific order of precedence. Parentheses can override this order to clarify or control the evaluation sequence.

Category Operators (Highest → Lowest)
Increment / Decrement ++ --
Unary Operators - (unary minus)
Arithmetic * / % + -
Relational > < >= <=
Equality == !=
Logical ! && ||
Assignment
= += -= *= /= %=

Increment and Decrement Operators

What Are They?

Examples:

int x = 5;
++x; // same as x = x + 1 → x becomes 6
--x; // same as x = x - 1 → x becomes 5

Prefix vs Postfix

Example:

int x = 10;
int y = ++x; // prefix → x becomes 11, y = 11

x = 10;
y = x++; // postfix → y = 10, then x becomes 11

Efficiency

Most compilers generate faster and more optimized code using ++ and -- compared to x = x + 1 or x = x - 1. They are ideal for loops, counters, and pointer arithmetic.

Operator Precedence (Arithmetic)

Precedence Operators
Highest ++, -- (prefix)
- (unary minus)
*, /, %
Lowest +, -

💡 Use parentheses to override precedence, e.g. (x + y) * z.

Relational and Logical Operators

What Are They?

Relational Operators

Operator Meaning Example Result
> Greater than 5 > 3 True (1)
>= Greater than or equal to 3 >= 3 True (1)
< Less than 2 < 3 True (1)
<= Less than or equal to 4 <= 2 False (0)
== Equal to 5 == 5 True (1)
!= Not equal to 4 != 5 True (1)

Logical Operators

Operator Meaning Example Result
&& AND (5 > 3) && (2 < 4) True (1)
|| OR (5 < 3) || (2 < 4) True (1)
! NOT !(5 == 3) True (1)

Truth Table for Logical Operators

p q p && q p || q !p
0 0 0 0 1
0 1 0 1 1
1 0 0 1 0
1 1 1 1 0

Example Program:

#include <stdio.h>
int xor(int a, int b) {
  return (a || b) && !(a && b); // XOR logic
}

int main(void) {
  printf("%d", xor(1, 0)); // 1
  printf("%d", xor(1, 1)); // 0
  printf("%d", xor(0, 1)); // 1
  printf("%d", xor(0, 0)); // 0
  return 0;
}

Precedence (Highest to Lowest)

Precedence Operators
Highest !
>, >=, <, <=
==, !=
&&
Lowest ||

💡 Use parentheses to group logical conditions. For example, !(0 && 0) || 0 evaluates to true.

Bitwise Operators

What Are They?

Operators and Their Meanings

Operator Action
& Bitwise AND
| Bitwise OR
^ Bitwise XOR (exclusive OR)
~ One’s complement (NOT)
>> Shift right
<< Shift left

Usage Example:

char ch = read_modem();
return (ch & 127); // clears parity bit (AND with 01111111)

Truth Table for XOR ( ^ )

p q p ^ q
0 0 0
0 1 1
1 0 1
1 1 0

Bit-Shift Operators

Example:

unsigned char x = 7; // 00000111
x = x << 1; // 00001110 = 14
x = x << 3; // 01110000 = 112
x = x >> 2; // 00011100 = 28
Operation Effect
Left Shift (<<) Multiplies by 2 for each shift
Right Shift (>>) Divides by 2 for each shift

Shift Operator Demo Program:

#include <stdio.h>
int main(void) {
  unsigned int i = 1;
  for (int j = 0; j < 4; j++) {
    i = i << 1;
    printf("Left shift %d: %d\\n", j, i);
  }
  for (int j = 0; j < 4; j++) {
    i = i >> 1;
    printf("Right shift %d: %d\\n", j, i);
  }
  return 0;
}

One's Complement (NOT)

In C, the ~ operator flips every bit of a number. This is called the bitwise NOT operation.

Simple Example in C:

char encode(char ch) {
  // Flip all bits of the character
  return ~ch;
}

This function takes a character, flips all its bits, and returns the result.

💡 Bitwise operators like ~ only work with integer types (like char, int, etc.).
They are very useful in areas like systems programming, cryptography, and working directly with hardware.

Common C Operators and Their Uses

The ?: (Ternary) Operator

The & and * (Pointer) Operators

The sizeof Operator

The , (Comma) Operator

The . and -> (Structure Access) Operators

The [ ] and ( ) Operators

Operator Precedence Summary

Operators are evaluated in a specific order of priority. Parentheses ( ) can override default precedence.

Highest ( ), [ ], ->
!, ~, ++, --, sizeof
*, /, %
+, -
<<, >>
<, <=, >, >=
==, !=
&, ^, |
&&, ||
?: (Ternary)
Assignment (=, +=, *= ...)
Lowest , (Comma)

Tip: Always use parentheses ( ) to make expressions clear and avoid ambiguity when using multiple operators together.

System Development Life Cycle (SDLC)

The System Development Life Cycle (SDLC) is a step-by-step process used to design, develop, test, and maintain high-quality software systems. It ensures that software is created in a structured, organized, and efficient way.

Why SDLC is Important:

The Waterfall Model

One of the earliest and most popular SDLC models is the Waterfall Model. It follows a linear and sequential flow — meaning each phase must be completed before the next one begins.

Waterfall Model Diagram

(Waterfall Model Diagram)

Phases of the Waterfall Model:

Iteration in the Waterfall Model:

Important Note:

  • If major issues are discovered late (during testing or coding), the project can fail.
  • In large projects, this can mean millions of dollars and years of effort lost.
  • That's why proper planning, analysis, and design are critical.