1) What is a data type?
A data type tells the compiler:
-
how much memory to reserve for a value, and
-
how to interpret the bits stored there (integer vs real vs character, etc.).
A variable is a named memory location of a particular data type that holds a value.
2) Built-in (fundamental) data types in C
Common built-in types and what they represent:
-
char β a single character (1 byte).
-
int β integer (typical βnaturalβ integer for the machine).
-
short (short int) β smaller-range integer.
-
long (long int) β larger-range integer.
-
long long (C99) β at least 64-bit integer.
-
float β single-precision floating-point (approx 6β7 decimal digits).
-
double β double-precision floating-point (approx 15β16 decimal digits).
-
long double β extended precision (implementation-dependent).
-
void β no value / incomplete type (used in
voidfunctions orvoid *).
Note: exact sizes (bytes) and ranges are implementation dependent. On many modern 64-bit Unix-like systems using the LP64 model (e.g., GCC on Linux x86_64) typical sizes are:
char= 1 byte
short= 2 bytes
int= 4 bytes
long= 8 bytes
long long= 8 bytes
float= 4 bytes
double= 8 bytes
long double= 16 bytes (often)Always confirm with
sizeof(type)for your target platform.
Safer fixed-width types: prefer <stdint.h> types when exact widths are needed:
-
int8_t,uint8_t,int16_t,uint16_t,int32_t,uint32_t,int64_t,uint64_t.
3) Signed vs Unsigned
-
Signed integers store negative and positive values (two's complement on almost all modern systems). Example types:
int,short,long. -
Unsigned integers store only non-negative values, giving a larger positive range for the same number of bits. Example:
unsigned int,uint32_t.
Example ranges (conceptual) for an 8-bit type:
-
signed char: β128 .. 127 -
unsigned char: 0 .. 255
Important behaviours:
-
Mixing signed and unsigned in expressions can lead to surprising promotions β unsigned often βwinsβ, causing negative values to convert to large positive values.
-
Avoid mixing signed and unsigned without explicit casts.
4) Short, Long, (and long long)
Modifiers adjust the width of integer types:
-
short(usually smaller thanint) βshort intorshort. -
long(usually >=int) βlong intorlong. -
long long(at least 64 bits) β added in C99.
Example:
Use suffix L, LL, U as needed in integer literals: 1234U, 1234L, 1234LL.
5) Float & Double (floating point)
-
floatβ 32-bit single precision (approx. 6β7 decimal digits). -
doubleβ 64-bit double precision (approx. 15β16 digits). -
long doubleβ extended precision (implementation-dependent).
Floating point is approximate: beware rounding & precision errors. Use double by default for better accuracy; float is used when memory/performance constraints require it (e.g., large arrays on embedded devices, GPUs).
Example:
6) Declaration vs Initialization
-
Declaration tells the compiler the variable name and type.
-
Initialization gives the variable its first value at point of declaration.
Always initialize variables before use β uninitialized variables contain indeterminate values and cause undefined behaviour.
Multiple declarations:
7) Type Modifiers, Qualifiers, and Storage Classes
Type modifiers (adjust base type):
-
signed,unsignedβ signedness -
short,long,long longβ width modifiers
Type qualifiers (specify properties of access/optimization):
-
constβ value cannot be changed after initialization (compile-time enforcement). -
volatileβ tells compiler value may change externally (hardware, ISR), so do not optimize away reads/writes. -
restrict(C99) β pointer qualifier promising no aliasing through that pointer (optimization hint).
Storage class specifiers (lifetime & linkage):
-
autoβ automatic local variable (default for local variables). Rarely written explicitly. -
staticβ for local: keeps value between function calls (internal linkage for globals if at file scope). -
externβ declares a global variable defined elsewhere (linker will resolve). -
registerβ hint to store variable in a CPU register (mostly ignored by modern compilers).
8) Common examples (with printf format specifiers)
Use the correct format specifiers:
-
%d/%iforint -
%uforunsigned int -
%hdforshort -
%ldforlong -
%lldforlong long -
%fforfloat/double(butprintfpromotesfloattodouble) -
%cforchar -
%sfor strings
For <stdint.h> types use printf macros from <inttypes.h> like PRId32, PRIu64 for portability:
9) Promotion and Conversion rules (brief)
-
Integer promotion: smaller integer types (
char,short) are promoted tointin expressions. -
When mixing types (e.g.,
intanddouble), usual arithmetic conversions will convert the narrower to the wider type (e.g., integer β floating point). -
Be careful with signed β unsigned mixing β cast explicitly when you mean it.
10) Memory & Scope
Heap (dynamic allocation):
Global / static area:
11) Use cases & when to choose what
-
Use
intfor general-purpose integer arithmetic (unless you need exact width). -
Use
unsignedwhen you know value is non-negative and you need the larger positive range (but be cautious with arithmetic). -
Use
shortfor memory-constrained arrays of small integers (embedded). -
Use
long/long longfor large integers (timestamps, big counters). -
Use
<stdint.h>fixed-width types (int32_t,uint64_t) when you need portability and predictable sizes (file formats, network protocols). -
Use
size_tfor sizes/indices (result ofsizeof, used by memory functions). -
Use
floatfor memory/compute constrained numerical arrays; usedoublefor general numeric computations. -
Use
long doublefor extra precision in scientific computing when supported.
12) Best practices & style rules
-
Always initialize variables.
-
Prefer
intfor general integers andsize_tfor sizes and indexing. -
Use fixed-width types (
<stdint.h>) for binary formats/networking. -
Avoid unnecessary use of
unsignedfor arithmetic calculations to prevent surprising conversions. -
Use
constfor values that should not change β it documents intent and enables compiler optimizations. -
Limit variable scope β declare variables in the smallest scope needed (inside loops/functions).
-
Use descriptive names (
student_count,buffer_len) nota,b. -
Check for overflow/underflow especially when converting between types or performing arithmetic on boundaries.
-
Use correct printf/scanf format specifiers or the
inttypes.hmacros for fixed-width types. -
Use compiler warnings (
-Wall -Wextra) and static analyzers to catch risky type usage. -
Document assumptions (e.g., endianness, expected ranges).
-
Prefer
doubleoverfloatunless memory/speed constraints demandfloat. -
Use
volatileonly when necessary (hardware registers, shared variables modified in signal handlers).
13) Common pitfalls (watch out)
-
Using uninitialized variables β undefined behaviour.
-
Mixing signed and unsigned values in comparisons.
-
Assuming
intis 32 bits on all platforms β use fixed-width types if needed. -
Using
floatexpecting exact decimal arithmetic β floating point is approximate. -
Overflow of signed integers (undefined behaviour) versus unsigned overflow (wraps modulo 2βΏ).
-
Using wrong
printfspecifier β causes runtime errors or incorrect output.
14) Quick reference table (typical / common sizes β confirm with sizeof)
| Type | Typical size (bytes) on x86_64 LP64 | Notes |
|---|---|---|
| char | 1 | character, often signed or unsigned by default depends on implementation |
| short | 2 | short int |
| int | 4 | general integer |
| long | 8 | long int |
| long long | 8 | at least 64 bits (C99) |
| float | 4 | single-precision |
| double | 8 | double-precision |
| long double | 16 (often) | extended precision; platform dependent |
| size_t | 8 | unsigned type for sizes (from <stddef.h>) |
15) Small practical examples
Safe: fixed-width integer for binary data
Avoid mixing signed/unsigned
Floating point precision
Use tolerance when comparing floats:
Final quick checklist
-
sizeof(type)to confirm on your compiler/target. -
Use
<stdint.h>for portability where required. -
Initialize variables.
-
Prefer
constto document immutability. -
Avoid signed/unsigned mixing.
-
Use
doubleby default for floating calculations. -
Limit scope, pick descriptive names, and enable compiler warnings.