C Programming Basics for GATE CS
Data types, sizeof, operators, precedence, storage classes, control flow — with GATE-level output tracing questions and common traps.
Last updated: April 2026 | GATE CS 2024–2026 syllabus
Key Takeaways for GATE
- Data type sizes are implementation-defined — GATE uses standard 32-bit assumptions: int=4B, char=1B, float=4B, double=8B, pointer=4B (32-bit) or 8B (64-bit).
- In C (not C++), character literals like
'a'are int, sosizeof('a')=4. - Operator precedence traps:
sizeof> arithmetic > relational > logical. Always use parentheses in GATE answers. - Storage classes: auto (stack, no init), static (data segment, 0 init, persists), register (hint to compiler), extern (linkage).
staticlocal variable retains value across calls — favourite GATE output-prediction trap.- Preprocessor runs before compilation —
#definedoes blind text substitution with no type checking. - Short-circuit evaluation:
&&stops on first false,||stops on first true — affects side effects.
1. Data Types & Sizes
| Type | Size (32-bit) | Range (signed) | Format |
|---|---|---|---|
| char | 1 byte | −128 to 127 | %c, %d |
| short | 2 bytes | −32768 to 32767 | %hd |
| int | 4 bytes | −231 to 231−1 | %d |
| long | 4 bytes (32-bit) / 8 bytes (64-bit) | system-dependent | %ld |
| long long | 8 bytes | −263 to 263−1 | %lld |
| float | 4 bytes | ±3.4×1038 | %f |
| double | 8 bytes | ±1.7×10308 | %lf |
| pointer | 4 bytes (32-bit) / 8 bytes (64-bit) | — | %p |
sizeof(char) ≤ sizeof(short) ≤ sizeof(int) ≤ sizeof(long) ≤ sizeof(long long)sizeof(float) ≤ sizeof(double) ≤ sizeof(long double) C vs C++ trap:
C: sizeof('a') = sizeof(int) = 4 (char literal is int in C)
C++: sizeof('a') = sizeof(char) = 1
2. Storage Classes
| Class | Storage | Scope | Lifetime | Default init |
|---|---|---|---|---|
| auto | Stack | Block | Block duration | Garbage |
| register | Register (hint) | Block | Block duration | Garbage |
| static (local) | Data segment | Block | Program lifetime | 0 |
| static (global) | Data segment | File (internal linkage) | Program lifetime | 0 |
| extern | Data segment | File (external linkage) | Program lifetime | 0 |
int counter() {
static int c = 0; // initialised once, persists across calls
return ++c;
}
// counter() returns 1, 2, 3, ... on successive calls static global: visible only within the file — prevents name clashes in multi-file programs.
extern: declares a variable defined in another translation unit — no storage allocation.
3. Operators & Precedence
1. () [] → . (postfix) 2. ++ — (prefix) * & sizeof (unary) 3. * / %
4. + – 5. << >> 6. < <= > >= 7. == !=
8. & (bitwise) 9. ^ 10. | 11. && 12. || 13. ?:
14. = += -= etc. (right-to-left) 15. , (comma)
| Expression | Result | Reason |
|---|---|---|
5 > 3 > 1 | 0 | (5>3)=1, then 1>1=0 |
2 & 3 | 4 | 6 | (2&3)=2, 2|4=6 |
1 << 3 + 1 | 16 | + before <<: 1<<4=16 |
a++ + ++a | UB | Undefined behaviour (modify twice) |
sizeof(int+3.0) | 8 | int+3.0 promotes to double=8B |
!0 == 1 | 1 | !0=1, 1==1=1 |
f() && g() — if f() returns 0, g() is never called.f() || g() — if f() returns non-zero, g() is never called.This affects programs with side effects (printf inside conditions).
4. Control Flow
for(init; cond; update) — update runs AFTER body.while vs do-while: do-while executes body at least once (condition checked at bottom).
switch-case fallthrough:
switch(x) {
case 1: printf("one"); // no break — falls through
case 2: printf("two"); break;
case 3: printf("three");
}
// if x=1: prints "onetwo". Fallthrough is intentional C behaviour. break vs continue vs goto:
break: exits innermost loop/switch. continue: skips to next iteration. goto: unconditional jump (avoid).
Dangling else: else binds to the nearest unmatched if.
if (a) if (b) x=1; else x=2; // else belongs to inner if(b)
5. Preprocessor Directives
#define macro traps:
#define SQ(x) x*x SQ(3+2) → 3+2*3+2 = 11 (not 25!) // Fix: #define SQ(x) ((x)*(x))
#ifdef / #ifndef: Conditional compilation for platform-specific code.
#include “file” searches current directory first; #include <file> searches system paths.
sizeof vs macro:
sizeof is a compile-time operator, not a function — sizeof(int) not sizeof int().
#define MAX(a,b) ((a)>(b)?(a):(b)) evaluates arguments twice — use inline functions instead.
6. Type Conversion & Implicit Promotion
Usual arithmetic conversion (lower → higher):
char → int → unsigned int → long → unsigned long → float → double → long double
Common GATE traps:
char c = 255;
printf("%d", c); // -1 (signed char overflow → implementation-defined)
unsigned int a = 5, b = 10;
printf("%d", a - b); // large positive number! (wraps around in unsigned)
int x = 3, y = 2;
float f = x/y; // f = 1.0, not 1.5 (integer division first)
float g = (float)x/y; // g = 1.5 (correct)7. GATE Examples
#include <stdio.h>
int f(int n) {
static int r = 0;
if (n <= 0) return 1;
if (n > 3) { r = n; return f(n-2) + 2; }
return f(n-1) + r;
}
int main() { printf("%d", f(5)); } Trace: f(5): r=5, f(3)+2. f(3): f(2)+r=f(2)+5. f(2): f(1)+r=f(1)+5. f(1): f(0)+r=1+5=6.
f(2)=6+5=11. f(3)=11+5=16. f(5)=16+2=18. Output: 18
#include <stdio.h>
#define MAX(x,y) (x>y?x:y)
int main() {
int a=2, b=3;
printf("%d", MAX(a++, b++));
} Macro expands to: (a++>b++?a++:b++). a++=2, b++=3, 2>3 is false, so b++ = 4 (post-increment after b was already incremented in condition). Output: 4
void f() {
static int x = 5;
x++;
printf("%d ", x);
}
int main() { f(); f(); f(); } First call: x initialised to 5, incremented to 6. Second: x persists as 6, becomes 7. Third: 8.
Output: 6 7 8
8. Common Mistakes
sizeof('a')in C vs C++: In C, char literals are int (size 4). In C++, char literal is char (size 1). GATE usually specifies C — answer 4.- Macros with side effects:
MAX(a++, b++)increments both arguments — macro arguments are not functions. Use inline functions instead. - Unsigned arithmetic underflow:
unsigned int a=5, b=10; a-bwraps to a large positive number, not −5. Unsigned subtraction is modular. - Integer division:
3/2 = 1in C, not 1.5. Cast at least one operand to float/double for real division. - Dangling else: else always binds to the nearest if, regardless of indentation. Use braces to be explicit.
9. FAQ
- What is the size of int in C?
- The C standard only guarantees int ≥ 16 bits. On modern 32-bit and 64-bit systems, int is almost always 4 bytes (32 bits). GATE questions assume 4 bytes unless stated otherwise. Always use
sizeof(int)in real code to be portable. - What is the difference between static and auto storage class in C?
- auto variables live on the stack, have block scope and lifetime, and are uninitialized (garbage values). static local variables live in the BSS/data segment, have block scope but program lifetime, are initialized to 0 by default, and retain their value across function calls — making them useful for counters and caches.
- What is the output of
printf("%d", sizeof('a'))in C? - In C, character literals like ‘a’ are of type int (not char), so sizeof(‘a’) = sizeof(int) = 4 bytes on most systems. This is a common GATE trap. In C++, ‘a’ is char so sizeof(‘a’) = 1. The question specifies the language — always check.
- What is the difference between #define and const in C?
- #define is a preprocessor macro — text is substituted before compilation, so it has no type, no scope, and can’t be watched in a debugger. const creates a typed, scoped variable that the compiler can check and optimize. In C, const variables are not true compile-time constants (unlike C++), so use enum or #define for array sizes in C.