Data Types, Sizes & Storage Classes in C โ Revision Notes
Data types and storage classes are the single most common C-fundamentals topic in campus written tests and viva rounds. Expect "predict the output" and "what is sizeof(...)" questions, plus tricky ones on static counters and extern linkage. Getting these right signals you actually understand memory, not just syntax.
Primitive types and guaranteed sizes
The C standard fixes only minimum sizes, not exact ones โ actual sizes depend on the compiler/architecture. sizeof(char) is always 1 by definition (it is the unit of sizeof).
| Type | Typical size (64-bit) | Standard minimum |
|---|---|---|
| char | 1 byte | 1 byte (โฅ8 bits) |
| short | 2 bytes | โฅ16 bits |
| int | 4 bytes | โฅ16 bits |
| long | 8 bytes (Linux) / 4 (Windows) | โฅ32 bits |
| long long | 8 bytes | โฅ64 bits |
| float | 4 bytes | โ |
| double | 8 bytes | โ |
Storage classes
A storage class sets a variable's scope, lifetime, linkage, and default value.
| Class | Lifetime | Linkage | Default |
|---|---|---|---|
| auto | block | none | garbage |
| register | block | none | garbage |
| static | whole program | internal | 0 |
| extern | whole program | external | 0 |
Exam Tricks & Tips
- ๐ฏ
sizeof(char)is always 1; every other size is platform-dependent, so never hard-code 4 forintโ usesizeof. - ๐ฏ A
staticlocal variable keeps its value between calls and is initialized only once; default value is 0. - ๐ฏ
registeris just a hint โ you cannot take the address (&) of a register variable. - ๐ฏ
externdeclares but does not define โ no memory is allocated; the definition lives in another file. - ๐ฏ Uninitialized global/static variables are zero-initialized; uninitialized auto (local) variables hold garbage.
- ๐ฏ
signed/unsignedmatters: anunsigned intcan never be negative, sounsigned x = -1wraps to the max value. - โ Common mistake: assuming
intis always 4 bytes โ on a 16-bit embedded target it may be 2 bytes; always verify withsizeof.
Expected pattern
"What will sizeof print?", "difference between static and extern?", or a static-counter loop asking for its value across calls. Output-prediction on unsigned wraparound is a favourite.
Quick recap
sizeof(char)==1 always; standard fixes minimums only. Four storage classes: auto/register (block, garbage), static (persistent, 0, internal linkage), extern (declaration only, external linkage). Globals/statics default to 0, locals to garbage.
Data Types, Sizes & Storage Classes in C โ Flashcards
Cover the answer, recall, then check. 12 cards on C data types and storage classes.
Q1. What is the value of sizeof(char) in C, and why?
A1. Always 1 โ char is the unit of measurement for sizeof by definition, on every platform.
Q2. Does the C standard guarantee int is 4 bytes?
A2. No. It only guarantees int is at least 16 bits (2 bytes). On most modern systems it is 4 bytes, but always use sizeof.
Q3. What are the four storage classes in C?
A3. auto, register, static, and extern.
Q4. What is special about a static local variable?
A4. It retains its value between function calls and is initialized only once; its default value is 0.
Q5. What is the default value of an uninitialized local (auto) variable?
A5. Garbage (indeterminate). Only globals and statics are zero-initialized.
Q6. Why can't you take the address of a register variable?
A6. register requests storage in a CPU register (no memory address); & requires a memory address, so it is illegal.
Q7. What does extern int x; do?
A7. Declares that x exists (defined in another translation unit) without allocating memory โ a declaration, not a definition.
Q8. What is the linkage of a file-scope static variable?
A8. Internal linkage โ it is visible only within its own translation unit (source file).
Q9. What happens with unsigned int x = -1;?
A9. It wraps around to the maximum unsigned value (e.g. 4294967295 for 32-bit), since unsigned types cannot store negatives.
Q10. Which is larger, long on 64-bit Linux or on 64-bit Windows?
A10. Linux (LP64) uses 8-byte long; Windows (LLP64) uses 4-byte long. long long is 8 bytes on both.
Q11. What does the sizeof operator return, and at what time is it evaluated?
A11. The size in bytes (as size_t), computed at compile time for most operands (not runtime).
Q12. Default value and lifetime of a global variable?
A12. Default 0; lifetime is the entire program run (static storage duration).
Data Types, Sizes & Storage Classes in C
Ask "how big is an int?" in an interview and the wrong answer is a confident "4 bytes". The right answer starts with "it depends on the platform" โ and that instinct is what this lesson builds.
Core idea: a type tells the compiler two things โ how many bytes to reserve and how to interpret the bit pattern stored there. A storage class answers three orthogonal questions about a variable: where it lives (storage duration), who can see it (scope), and whether the name links across files (linkage).
How types and sizes work
Beginner โ the built-in types
C ships with char, int, float, double and the modifiers short, long, signed, unsigned. A char is exactly 1 byte by definition, but a byte need not be 8 bits (though it almost always is). The only guarantees the standard makes are relative: sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long) <= sizeof(long long), with minimum ranges (an int holds at least ยฑ32767).
printf("%zu\n", sizeof(int)); // 4 on most 64-bit systems, but NOT guaranteed
Intermediate โ why sizes vary
The compiler and target ABI decide actual widths. On the common LP64 model (Linux/macOS 64-bit): int = 4, long = 8, pointer = 8. On LLP64 (64-bit Windows): long = 4, pointer = 8. Portable code that needs a known width uses <stdint.h>: int32_t, uint64_t. Why it matters: assuming long is 8 bytes and shifting a bitmask by 40 is undefined on Windows.
Advanced โ storage classes
Four keywords cover the axes of duration/scope/linkage:
| Class | Duration | Scope | Linkage |
|---|---|---|---|
auto (default local) |
automatic (stack) | block | none |
static (local) |
whole program | block | none |
static (file-level) |
whole program | file | internal |
extern |
whole program | file | external |
register |
automatic | block | none (hint only) |
A static local keeps its value between calls because it lives in the data segment, not on the stack. A static global is invisible to other translation units โ the classic way to make a file-private function.
Worked example
int counter() {
static int n = 0; // initialised ONCE, at load time
n++;
return n;
}
// counter() called three times returns 1, 2, 3
The static storage means n is not reset on each call. Remove static and every call returns 1, because an auto local is re-created on the stack each time.
Interview & exam relevance
GATE and product-company screens love: "output of sizeof expressions", "what does static before a global mean" (answer: internal linkage, not lifetime here โ globals already live forever), and integer-overflow traps. Knowing LP64 vs LLP64 signals real systems experience.
Tricks & gotchas
staticis overloaded: on a local it changes duration; on a global/function it changes linkage. Same keyword, different job.sizeofreturnssize_t(unsigned) โ print with%zu, and never writesizeof(a) - 1 < 0(unsigned never goes negative).- Mnemonic for storage classes: "A-S-E-R" โ Auto, Static, Extern, Register.
Believing static int n; inside a function re-initialises to 0 on every call. The initialiser runs exactly once (at program load); the variable retains its last value across calls. This bug shows up as counters or caches that "won't reset" โ or, conversely, that unexpectedly persist.
- โ- Type = byte count + interpretation; sizes are platform/ABI dependent, only relative ordering is guaranteed.
- โ- Use
<stdint.h>(int32_tโฆ) when you need a fixed width. - โ-
staticlocal โ persists across calls;staticglobal/function โ internal linkage (file-private). - โ-
externdeclares a name defined elsewhere;registeris a (usually ignored) speed hint. - โ-
sizeofyields unsignedsize_t.
- โA type fixes size and meaning; a storage class fixes lifetime, scope and linkage. Never assume
intis 4 bytes, and rememberstaticwears two hats โ persistence for locals, privacy for globals.
Data Types, Sizes & Storage Classes in C โ Worked Example
Worked Example
Problem: On a typical 64-bit LP64 system (int = 4 bytes, long = 8 bytes), predict the exact output of this program and explain each line.
#include <stdio.h>
int counter(void) { static int n = 0; return ++n; }
int main(void) {
unsigned char c = 250;
c = c + 10; // (A)
printf("A=%d\n", c);
int x = -1;
unsigned int u = 1;
printf("B=%d\n", x < u); // (B)
printf("C=%d %d %d\n", counter(), counter(), counter()); // (C) left-to-right assumed
printf("D=%zu\n", sizeof(3 + 'a')); // (D)
return 0;
}
Solution:
(A) unsigned char holds 0..255 and wraps modulo 256. 250 + 10 = 260, and 260 mod 256 = 4. So A=4.
(B) In the comparison x < u, the int -1 is converted to unsigned (usual arithmetic conversions). -1 becomes UINT_MAX (4294967295), which is NOT less than 1, so the result is 0. This is the classic signed/unsigned trap.
(C) n is a static local, so it persists across calls: successive calls return 1, 2, 3. Assuming left-to-right evaluation, C=1 2 3. (Argument evaluation order is unspecified, so this is compiler-dependent โ a key exam caveat.)
(D) 'a' is an int in C (character constants have type int), so 3 + 'a' has type int. sizeof(int) = 4, and sizeof yields size_t, printed with %zu. So D=4.
Answer: A=4, B=0, C=1 2 3, D=4
- โ- unsigned integer arithmetic wraps modulo 2^N; signed overflow is undefined behaviour.
- โ- Mixing signed and unsigned promotes the signed operand to unsigned โ a frequent bug source.
- โ- static locals keep their value between calls; a character constant has type int, so sizeof('a') == sizeof(int).