Understanding C Expressions and Data Types
Understanding C Expressions and Data Types
Expressions in C
Expressions are the fundamental building blocks of any C program. They combine data and operators to perform
computations and produce results.
Important Notes:
The char type usually represents ASCII values.
Size of int depends on system architecture:
16-bit systems → int = 16 bits
32-bit systems → int = 32 bits
Always write portable code without assuming fixed size.
Floating-point values follow the IEEE representation. The range is at least 1E-37 to 1E+37 .
Available Modifiers:
signed → allows both positive and negative values.
unsigned → stores only non-negative values, doubles the positive range.
short → reduces storage size (commonly 16-bit).
long → increases storage size (commonly 32-bit or 64-bit).
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.
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.
Copy
Examples of Variable Declarations:
int i, j, l;
short int si;
unsigned int ui;
double balance, profit, loss;
Type Independence: Variable names are independent of their type; the compiler determines type by declaration.
Local Variables
Declared within a block or function.
Exist only while their block is executing (created at entry, destroyed at exit).
Cannot be accessed outside their declaring block.
Default storage: stack, unless declared with static.
Declared variables in inner blocks can hide variables in outer blocks with the same name.
Example: Variable Shadowing
Copy
#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
Variables declared inside the parentheses of a function definition.
Behave like local variables, destroyed after function execution ends.
Receive argument values during a function call.
Example:
Copy
int is_in(char *s, char c) {
while (*s)
if (*s == c) return 1;
else s++;
return 0;
}
Global Variables
Declared outside of all functions.
Accessible across the entire file (file scope).
Stored in a fixed memory region, persisting throughout program execution.
Can be shadowed by local variables of the same name.
Best practice: minimize usage to avoid unintended side effects.
Example:
Copy
#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;
}
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.
Type Qualifiers in C
const
Definition: The const qualifier specifies that a variable’s value cannot be changed by the program after initialization.
Syntax Example: const int a = 10; - declares an integer that cannot be modified.
Usage in Functions: const can protect function arguments from modification, particularly when using pointers:
Copy
#include <stdio.h>
void sp_to_dash(const char *str);
int main(void) {
sp_to_dash("this is a test");
return 0;
}
void sp_to_dash(const char *str) {
while(*str) {
if(*str==' ') printf("-");
else printf("%c", *str);
str++;
}
}
Standard Library: Many functions like strlen(const char *str) use const to ensure the argument string is not modified.
Benefits: Protects against unintended modification and allows compiler optimizations.
volatile
Definition: The volatile qualifier tells the compiler that a variable’s value may change unexpectedly, outside program
control.
Usage: Common in hardware access, global variables modified by interrupts, or memory-mapped I/O.
Example: volatile int timer_flag;
Combination with const: const volatile char *port = (const volatile char*)0x30; - read-only for the program but may
change externally.
Best Practice: Place extern declarations in header files included in multiple source files.
static
Definition: Creates variables with permanent storage that are limited in scope to their function or file.
Local Static Variables:
Declared inside a function.
Maintain value between function calls.
Not accessible outside their function.
Example:
Copy
int series(void) {
static int series_num = 100;
series_num += 23;
return series_num;
}
Copy
static int series_num;
void series_start(int seed) {
series_num = seed;
}
int series(void) {
series_num += 23;
return series_num;
}
Benefits: Allows encapsulation of variables within a function or file, reducing conflicts and enhancing modularity.
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
Copy
type variable_name = constant;
Examples
Copy
char ch = 'a';
int first = 0;
double balance = 123.23;
Important Points
Global and static local variables are initialized only once, at program startup.
Local variables are initialized each time their block is entered.
Uninitialized local variables contain unpredictable (garbage) values.
Uninitialized global and static local variables are automatically initialized to zero.
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
1. Integer Constants:
Whole numbers without fractional parts
Can be positive or negative (e.g., 10, -45)
2. Floating-Point Constants:
Contain decimal points or use scientific notation
Examples: 12.34, 4.56e-2
3. Character Constants:
Enclosed in single quotes — represent a single character
Examples: 'A', '5', '%'
Wide-character constants use L prefix
Example:
Copy
wchar_t wc;
wc = L'A'; // wide-character constant
4. String Constants:
Sequence of characters enclosed in double quotes
Example: "Hello World"
5. Enumeration Constants:
Named integer constants defined using enum
Example:
Copy
enum Weekday { SUN, MON, TUE, WED };
Type Suffixes
U or u → Unsigned integer
L or l → Long integer or Long double
F or f → Float constant
LL or ll → Long long integer (C99)
Copy
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.
Syntax Rules
Octal constants: Start with 0 (zero).
Hexadecimal constants: Start with 0x or 0X.
They represent integer values and can be assigned to any integer-type variable.
Copy
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
💡 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.
Copy
"This is a test"
Strings differ from characters — 'a' is a single character, while "a" is a string of length 1.
C has no formal string type; strings are arrays of characters.
Code Meaning
\b Backspace
\f Form feed
\n New line
\r Carriage return
\t Horizontal tab
\\ Backslash
\v Vertical tab
\a Alert (beep)
\? Question mark
\N Octal constant
Example:
Copy
#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:
Arithmetic Operators
Relational Operators
Logical Operators
Bitwise Operators
Special Operators (e.g., assignment, sizeof, conditional)
Copy
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.
lvalue: Refers to an object that can appear on the left side of an assignment (modifiable memory).
rvalue: Represents the data value that can appear on the right side of an assignment.
Example:
int x = 10;
int y;
y = x + 5; // y is assigned the value 15
Copy
int x;
char ch;
float f;
ch = x; // int → char
x = f; // float → int
f = ch; // char → float
f = x; // int → float
When assigning int → char, only the lower 8 bits of the integer are stored.
When assigning float → int, the fractional part is truncated.
When assigning char → float, the character’s ASCII value is converted into float format.
Precision Loss: Converting from a larger to a smaller data type may cause information loss.
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
Similar shorthand operators exist for subtraction (-=), multiplication (*=), division (/=), and modulus (%=).
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:
Copy
int x = 5, y = 2;
printf("%d ", x / y); // Output: 2
printf("%d", x % y); // Output: 1
Copy
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.
Example:
Copy
if (x > 5 && !(y < 3) || z < = 10)
printf("Condition is true");
Operator Precedence
Operators are evaluated in a specific order of precedence. Parentheses can override this order to clarify or control the
evaluation sequence.
Assignment = += -= *= /= %=
Copy
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
The prefix form (++x or --x) increments/decrements the variable before its value is used in an expression.
The postfix form (x++ or x--) increments/decrements the variable after its value is used.
Copy
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.
Highest ++ -- , (prefix)
- (unary minus)
, ,
* / %
Lowest + - ,
Relational Operators
Operator Meaning Example Result
Logical Operators
Operator Meaning Example Result
0 0 0 0 1
0 1 0 1 1
1 0 0 1 0
1 1 1 1 0
Copy
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;
}
Highest !
, , ,
> >= < <=
== !=,
&&
Lowest ||
💡 Use parentheses to group logical conditions. For example, !(0 && 0) || 0 evaluates to true.
Bitwise Operators
What Are They?
Bitwise operators perform operations on individual bits of integer values.
They allow direct manipulation of binary data — a feature crucial in low-level programming.
Commonly used in embedded systems, device drivers, encryption, and data compression.
| Bitwise OR
Usage Example:
Copy
char ch = read_modem();
return (ch & 127); // clears parity bit (AND with 01111111)
0 0 0
0 1 1
1 0 1
1 1 0
Bit-Shift Operators
x << n : Shifts bits left by n positions (multiplies by 2ⁿ).
x >> n : Shifts bits right by n positions (divides by 2ⁿ).
Bits shifted off are lost; zeros (or ones for negatives) are filled in.
Copy
Example:
unsigned char x = 7; // 00000111
x = x << 1; // 00001110 = 14
x = x << 3; // 01110000 = 112
x = x >> 2; // 00011100 = 28
Operation Effect
Copy
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;
}
Copy
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.
Equivalent to:
Copy
if (x > 9) y = 100;
else y = 200;
Highest ,
( ) [ ] -> ,
, , , ,
! ~ ++ -- sizeof
, ,
* / %
,
+ -
<< >>,
, , ,
< <= > >=
== !=,
, ,
& ^ |
&& ||,
?: (Ternary)
Lowest , (Comma)
Tip: Always use parentheses ( ) to make expressions clear and avoid ambiguity when using multiple operators together.
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.