C: Variables and Data Types
Variables are like labeled boxes — each box has a name and a type, and can only hold the right kind of item. Put the wrong thing in, and things go wrong.
1. What Is a Variable
A variable is a named memory location in your program used to store data. Think of it as a box:
- Variable name: The label on the box, so you can find it
- Data type: What kind of items the box can hold
- Variable value: What's currently inside the box
- Memory address: The box's location in the warehouse
2. Variable Declaration and Initialization
(1) Declaring a Variable
Declaring a variable tells the compiler: "I need a box, here's its name and what type of things it holds."
int age;
float weight;
char blood_type;
After declaration, the variable's value is undefined (a garbage value) — it contains whatever random data was left in that memory location. Never use an uninitialized variable!
(2) Initializing a Variable
Initialization means giving a variable an initial value at the same time you declare it:
int age = 20;
float weight = 65.5f;
char blood_type = 'A';
(3) Declare First, Assign Later
You can also declare first and assign later:
int age;
age = 20;
This has the same effect, but the two-step approach is more spread out, making it easier to accidentally use the variable between declaration and assignment while it's still uninitialized.
(4) Declaring Multiple Variables of the Same Type
int a = 1, b = 2, c = 3;
3. Variable Naming Rules
C has strict rules for variable names:
Must follow:
- Can only contain letters (a-z, A-Z), digits (0-9), and underscores (_)
- Must start with a letter or underscore — cannot start with a digit
- Cannot be a C keyword (like
int,return,if, etc.) - Case-sensitive (
Ageandageare different variables)
Should follow:
- Names should be meaningful: use
student_countinstead ofsc - Use snake_case style (lowercase letters + underscores)
- Avoid names starting with double underscores (like
__count) — compilers use these internally - Avoid excessively long names (over 31 characters may not be supported by all compilers)
| Name | Valid? | Notes |
|---|---|---|
age |
Valid | Short and meaningful |
_count |
Valid | Starts with underscore |
student_name |
Valid | snake_case style |
2nd_place |
Invalid | Starts with a digit |
my-score |
Invalid | Contains hyphen (interpreted as subtraction) |
int |
Invalid | Keyword |
Age |
Valid but not recommended | Capital first letter usually reserved for type names |
4. Basic Data Types
A C data type determines how much memory a variable occupies, the range of values it can hold, and how the data is interpreted. Here are C99's basic data types:
(1) Integer Types
| Type | Size (bytes) | Typical Range | Format Specifier |
|---|---|---|---|
char |
1 | -128 ~ 127 | %c or %d |
unsigned char |
1 | 0 ~ 255 | %u |
short |
2 | -32768 ~ 32767 | %hd |
unsigned short |
2 | 0 ~ 65535 | %hu |
int |
4 | -2147483648 ~ 2147483647 | %d |
unsigned int |
4 | 0 ~ 4294967295 | %u |
long |
4 or 8 | Platform-dependent | %ld |
unsigned long |
4 or 8 | Platform-dependent | %lu |
long long |
8 | -9223372036854775808 ~ 9223372036854775807 | %lld |
int is sufficient. Use long long when you need a larger range, and char for characters. short is rarely used on modern platforms because int is typically just as efficient.
int as 4 bytes, which is the case on most modern platforms. However, the C standard only requires int to be at least 2 bytes. The actual size depends on the platform — use sizeof to confirm.
(2) Floating-Point Types
| Type | Size (bytes) | Significant Digits | Range (absolute value) | Format Specifier |
|---|---|---|---|---|
float |
4 | 6-7 | 1.2e-38 ~ 3.4e38 | %f |
double |
8 | 15-16 | 2.2e-308 ~ 1.8e308 | %lf |
long double |
8 or more | 18+ | Platform-dependent | %Lf |
Floating-point numbers have limited precision and cannot represent all decimals exactly. For example:
float f = 0.1f;
printf("%.20f\n", f);
0.10000000149011611938
0.1 is an infinitely repeating binary fraction, so float can't store it precisely. This is an inherent characteristic of floating-point representation, not a bug.
double by default when you need decimal numbers. It offers higher precision and is no slower than float on modern CPUs. Only prefer float on memory-constrained embedded systems.
(3) The Character Type
The char type is essentially a 1-byte integer, typically used to store characters. Characters are stored in memory as ASCII codes:
char ch = 'A';
printf("%c\n", ch);
printf("%d\n", ch);
A
65
The ASCII code for 'A' is 65. Using %c prints the character, while %d prints the integer value. This shows that characters and integers are interchangeable in C.
'A' is a character (single quotes), while "A" is a string (double quotes, with a trailing \0). They are completely different — never mix them up.
(4) The _Bool Type
C99 introduced the _Bool type, which can only store 0 and 1. Include <stdbool.h> to use bool, true, and false:
#include <stdbool.h>
bool is_valid = true;
bool is_empty = false;
5. The sizeof Operator
sizeof is a C keyword (not a function) that returns the number of bytes a data type or variable occupies:
▶ Example
#include <stdio.h>
int main(void) {
printf("char: %zu bytes\n", sizeof(char));
printf("short: %zu bytes\n", sizeof(short));
printf("int: %zu bytes\n", sizeof(int));
printf("long: %zu bytes\n", sizeof(long));
printf("long long: %zu bytes\n", sizeof(long long));
printf("float: %zu bytes\n", sizeof(float));
printf("double: %zu bytes\n", sizeof(double));
return 0;
}
char: 1 bytes
short: 2 bytes
int: 4 bytes
long: 4 bytes
long long: 8 bytes
float: 4 bytes
double: 8 bytes
sizeof returns a size_t type, whose format specifier is %zu. If your compiler doesn't support %zu (some older versions), you can use %lu instead.
sizeof also works with variables:
int age = 20;
printf("%zu\n", sizeof(age));
6. Formatted Output in Detail
(1) Integer Formatting
int n = 42;
printf("Decimal: %d\n", n);
printf("Octal: %o\n", n);
printf("Hex (lowercase): %x\n", n);
printf("Hex (uppercase): %X\n", n);
printf("Octal with prefix: %#o\n", n);
printf("Hex with prefix: %#x\n", n);
Decimal: 42
Octal: 52
Hex (lowercase): 2a
Hex (uppercase): 2A
Octal with prefix: 052
Hex with prefix: 0x2a
(2) Floating-Point Formatting
double pi = 3.141592653589793;
printf("Default: %f\n", pi);
printf("2 decimal places: %.2f\n", pi);
printf("8 decimal places: %.8f\n", pi);
printf("Scientific notation: %e\n", pi);
printf("Width 12.2f: [%12.2f]\n", pi);
Default: 3.141593
2 decimal places: 3.14
8 decimal places: 3.14159265
Scientific notation: 3.141593e+00
Width 12.2f: [ 3.14]
7. Type Value Ranges
The C standard library provides header files for querying the range of each type:
(1) limits.h (Integer Types)
#include <limits.h>
printf("int min: %d\n", INT_MIN);
printf("int max: %d\n", INT_MAX);
printf("char max: %d\n", CHAR_MAX);
Common macros:
| Macro | Meaning |
|---|---|
INT_MIN |
Minimum value of int |
INT_MAX |
Maximum value of int |
CHAR_MIN |
Minimum value of char |
CHAR_MAX |
Maximum value of char |
LONG_MAX |
Maximum value of long |
LLONG_MAX |
Maximum value of long long |
(2) float.h (Floating-Point Types)
#include <float.h>
printf("float significant digits: %d\n", FLT_DIG);
printf("double significant digits: %d\n", DBL_DIG);
printf("float max value: %e\n", FLT_MAX);
▶ Example
▶ Example
#include <stdio.h>
#include <limits.h>
#include <float.h>
int main(void) {
char ch = 'Z';
int num = 100;
float f = 3.14f;
double d = 2.718281828;
printf("char '%c' = ASCII %d, size = %zu\n", ch, ch, sizeof(ch));
printf("int %d, size = %zu, range = [%d, %d]\n", num, sizeof(num), INT_MIN, INT_MAX);
printf("float %.6f, size = %zu, digits = %d\n", f, sizeof(f), FLT_DIG);
printf("double %.9f, size = %zu, digits = %d\n", d, sizeof(d), DBL_DIG);
return 0;
}
char 'Z' = ASCII 90, size = 1
int 100, size = 4, range = [-2147483648, 2147483647]
float 3.140000, size = 4, digits = 6
double 2.718281828, size = 8, digits = 15
▶ Example
#include <stdio.h>
int main(void) {
char lower = 'a';
char upper = lower - 32;
printf("Lowercase: %c (ASCII %d)\n", lower, lower);
printf("Uppercase: %c (ASCII %d)\n", upper, upper);
printf("\nDigit characters:\n");
char digit = '0';
printf("Character '%c' ASCII code = %d\n", digit, digit);
printf("Character '%c' numeric value = %d\n", digit, digit - '0');
return 0;
}
Lowercase: a (ASCII 97)
Uppercase: A (ASCII 65)
Digit characters:
Character '0' ASCII code = 48
Character '0' numeric value = 0
'0' from a digit character gives its numeric value — this is the basis for converting characters to numbers.
❓ FAQ
📖 Summary
- Variables are named memory locations; specify the type and name when declaring, and always initialize
- Names must start with a letter or underscore, are case-sensitive, and cannot use keywords
- Integer types: char (1 byte), short (2), int (4), long (4 or 8), long long (8)
- Floating-point types: float (4 bytes, 6-7 digits precision), double (8 bytes, 15-16 digits precision)
- sizeof returns the byte size of a type or variable; limits.h and float.h provide value ranges
📝 Exercises
- Write a program that uses sizeof to print the byte sizes of all integer and floating-point types on your computer. Compare with a classmate's results to see if they match.
- Write a program that prints the ASCII codes for letters 'a' through 'z', five per line, using %-5d for alignment.
- Calculate and print the result of INT_MAX + 1 (integer overflow). Compare the output with INT_MIN and think about why this happens.