Question
I am confused about size_t in C. I know that it is the type returned by the sizeof operator, but what exactly is it? Is it a built-in data type?
For example, consider this loop:
for (i = 0; i < some_size; i++)
Should i be declared as int i; or size_t i;?
Short Answer
By the end of this page, you will understand what size_t is in C, why sizeof returns it, and when it is better to use size_t instead of int. You will also learn how size_t behaves in loops, array sizes, and memory-related code, plus the common mistakes beginners make when mixing signed and unsigned integer types.
Concept
size_t is an integer type used in C to represent sizes and counts of objects in memory. It is not a keyword like int or char, but a type alias provided by the standard library.
In practice:
sizeofreturns a value of typesize_t- Functions related to memory and arrays often use
size_t - It is designed to be able to store the size of the largest object the system can handle
A common definition looks conceptually like this:
typedef /* some unsigned integer type */ size_t;
The exact underlying type depends on the platform and compiler. On one system it might be unsigned int, and on another it might be unsigned long or something else.
Why does C use size_t?
C needs a type specifically for sizes because:
- sizes cannot be negative
- memory sizes may be larger than what a plain
intcan hold - the best integer type depends on the machine architecture
That is why size_t exists: it gives C a portable way to talk about memory sizes and element counts.
Is size_t a data type?
Yes. It is a real type you can declare variables with:
size_t n = 10;
More precisely, it is a typedef name for an unsigned integer type.
Should loop variables use int or size_t?
If you are iterating over something whose size is represented by size_t, then using size_t for the index is usually the best choice.
Example:
size_t i;
for (i = 0; i < some_size; i++) {
/* ... */
}
This matches the type of some_size, avoids signed-versus-unsigned comparison warnings, and works correctly for large sizes.
However, if your loop variable must go negative, then size_t is the wrong choice because it is unsigned.
Mental Model
Think of size_t as the language's official measuring tape for memory.
intis a general-purpose number typesize_tis the type C uses when measuring how big something is
If you ask, "How many bytes does this object use?" C answers with a size_t.
If you ask, "How many elements are in this memory block?" size_t is usually the right type.
So if int is a regular calculator number, size_t is the ruler used for memory-related measurements.
Syntax and Examples
Basic syntax
You commonly see size_t after including a standard header such as:
#include <stddef.h>
It is also made available by other headers like stdio.h, stdlib.h, and string.h in many common situations.
Declaring a size_t
#include <stddef.h>
size_t count = 25;
Using sizeof
#include <stdio.h>
int main(void) {
int numbers[5];
size_t bytes = sizeof(numbers);
size_t elements = sizeof(numbers) / sizeof(numbers[0]);
printf("bytes: %zu\n", bytes);
printf("elements: %zu\n", elements);
return 0;
}
Explanation
sizeof(numbers)returns the total size of the array in bytes- that result has type
size_t - dividing by
sizeof(numbers[0])gives the number of elements %zuis the correctprintfformat specifier forsize_t
Using size_t in a loop
#include <stddef.h>
int main(void) {
int values[] = {10, 20, 30, 40};
size_t count = sizeof(values) / sizeof(values[0]);
for (size_t i = 0; i < count; i++) {
/* process values[i] */
}
return 0;
}
This is a good pattern because:
countis a sizeiis used as an index from0up tocount - 1- both sides of
i < counthave the same type
Step by Step Execution
Consider this example:
#include <stdio.h>
#include <stddef.h>
int main(void) {
int values[] = {5, 8, 13};
size_t count = sizeof(values) / sizeof(values[0]);
for (size_t i = 0; i < count; i++) {
printf("i = %zu, value = %d\n", i, values[i]);
}
return 0;
}
Step by step
valuesis an array with 3 integers.sizeof(values)returns the total byte size of the whole array.- If
intis 4 bytes, this would be12.
- If
sizeof(values[0])returns the byte size of one element.- That would be
4.
- That would be
12 / 4gives3, socountbecomes3.- The loop starts with
i = 0. - Condition:
i < countbecomes0 < 3, which is true. - It prints
values[0], which is5. ibecomes1.- Condition:
1 < 3, still true. - It prints
values[1], which is8. ibecomes2.- Condition:
2 < 3, still true. - It prints
values[2], which is13. ibecomes3.- Condition:
3 < 3, false. - The loop stops.
Output
i = 0, value = 5
i = 1, value = 8
i = 2, value = 13
Real World Use Cases
size_t appears frequently in real C programs because many tasks involve sizes, lengths, and indexes.
Common use cases
- Array lengths
- counting how many elements are in an array
- Memory allocation
- passing byte counts to
malloc,calloc, andrealloc
- passing byte counts to
- String and buffer handling
- working with lengths returned by functions like
strlen
- working with lengths returned by functions like
- File and network buffers
- tracking how many bytes were read or written
- Looping over collections
- iterating through arrays or buffers safely
Example: dynamic memory
#include <stdlib.h>
#include <stddef.h>
int main(void) {
size_t count = 100;
int *data = malloc(count * sizeof(int));
if (data == NULL) {
return 1;
}
/* use data */
free(data);
return 0;
}
Here count * sizeof(int) is naturally a size calculation, so size_t is the correct type.
Example: string length
#include <stdio.h>
#include <string.h>
int main(void) {
const char *name = "Alice";
size_t len = strlen(name);
printf("Length: %zu\n", len);
return 0;
}
strlen returns size_t because string lengths are sizes.
Real Codebase Usage
In real codebases, developers use size_t anywhere a value represents a size, length, capacity, or non-negative index.
Common patterns
Validation before allocation
size_t count = get_count();
if (count == 0) {
return;
}
This makes intent clear: count is a size, not a general signed number.
Matching library function types
size_t len = strlen(input);
if (len >= buffer_size) {
return -1;
}
Using size_t avoids unnecessary conversions because standard library functions already return it.
Buffer processing loops
for (size_t i = 0; i < buffer_len; i++) {
buffer[i] = 0;
}
This is very common in parsing, file processing, and systems code.
Capacity and count fields in structs
typedef struct {
int *items;
size_t count;
size_t capacity;
} IntArray;
This is a standard pattern for dynamic arrays and containers.
Why teams prefer it
- It expresses intent clearly
- It matches the standard library
- It reduces type mismatch warnings
- It is safer for large memory sizes on modern systems
Common Mistakes
1. Using int for values that come from sizeof
Broken example:
int n = sizeof(double) * 100;
This may work for small values, but int is not the type returned by sizeof.
Better:
size_t n = sizeof(double) * 100;
2. Comparing int with size_t
Broken example:
size_t count = 10;
int i;
for (i = 0; i < count; i++) {
/* ... */
}
This often produces a signed/unsigned comparison warning.
Better:
size_t count = 10;
for (size_t i = 0; i < count; i++) {
/* ... */
}
3. Assuming size_t can hold negative values
Broken example:
size_t n = -1;
printf("%zu\n", n);
Because size_t is unsigned, assigning -1 does not store negative one in the usual sense. It wraps to a very large value.
Avoid using size_t when negative numbers have meaning.
4. Looping backward carelessly
Broken example:
for (size_t i = 10; i >= 0; i--) {
/* infinite loop risk */
}
Why this fails:
size_tis unsigned- after
ireaches0, decrementing it wraps to a huge value i >= 0is always true for unsigned types
Safer pattern:
for (size_t i = 10; i > 0; i--) {
printf("%zu\n", i - 1);
}
5. Printing size_t with the wrong format specifier
Broken example:
size_t n = 42;
printf("%d\n", n);
Better:
printf("%zu\n", n);
6. Forgetting that size_t is implementation-dependent
Do not assume it is always unsigned int.
Bad assumption:
unsigned int x = sizeof(long long);
Better:
size_t x = sizeof(long long);
Comparisons
size_t vs int
| Feature | size_t | int |
|---|---|---|
| Purpose | Sizes, lengths, counts, indexes | General integer values |
| Signed or unsigned | Unsigned | Usually signed |
Returned by sizeof | Yes | No |
| Can represent negative values | No | Yes |
| Good for array sizes | Yes | Sometimes, but less ideal |
| Good for arithmetic that may go below zero | No | Yes |
When to choose each
- Use
size_tfor:- array lengths
- buffer sizes
- memory allocation sizes
- indexes that range from
0upward
- Use
intfor:- values that may be negative
- status codes like
-1,0,1 - arithmetic where signed values make sense
size_t vs other unsigned integer types
| Type | Best use |
|---|---|
size_t | Sizes of objects and array indexes |
unsigned int | General unsigned arithmetic when size semantics are not the focus |
uint32_t | Exactly 32-bit unsigned values when fixed width matters |
size_t is about meaning as much as size. It tells readers: this value is a size or count.
Cheat Sheet
Quick reference
#include <stddef.h>
What size_t is
- An unsigned integer type
- Used for sizes and counts
- Returned by
sizeof - Often returned by library functions like
strlen
Basic usage
size_t n = sizeof(array);
size_t count = sizeof(array) / sizeof(array[0]);
Loop pattern
for (size_t i = 0; i < count; i++) {
/* ... */
}
Print with printf
printf("%zu\n", n);
Use size_t when
- measuring bytes
- storing array length
- storing string length
- indexing arrays from
0upward - passing sizes to allocation functions
Avoid size_t when
- negative values are meaningful
- you need countdown logic without care for unsigned wraparound
Important edge case
This is dangerous:
for (size_t i = n - 1; i >= 0; i--)
Use a safer reverse-loop pattern instead.
Good rule of thumb
If the value comes from sizeof, strlen, or represents a size/capacity/count, size_t is usually the right type.
FAQ
Is size_t built into the C language?
size_t is a standard type name provided by the C library headers, not a keyword like int.
Why does sizeof return size_t?
Because sizeof reports the size of an object in bytes, and C uses size_t specifically for object sizes.
Should I always use size_t for loops?
Not always. Use it when looping over sizes, array indexes, or buffer lengths. If the loop variable needs to become negative, use a signed type instead.
Is size_t always the same as unsigned int?
No. Its exact underlying type depends on the platform and compiler.
Can size_t hold negative numbers?
No. It is an unsigned type.
What header defines size_t?
<stddef.h> is the standard header most directly associated with size_t, though other standard headers may also provide it.
What format specifier should I use for size_t in printf?
Use %zu.
Why do I get signed/unsigned comparison warnings?
This usually happens when you compare an int loop variable with a size_t value. Matching the types often fixes the warning.
Mini Project
Description
Build a small C program that analyzes an integer array using size_t for lengths and loop indexes. This project demonstrates the most common real use of size_t: working safely with array sizes and indexes that come from sizeof.
Goal
Create a program that calculates the number of elements in an array, prints each element, and computes the sum using size_t correctly.
Requirements
- Define an integer array with several values.
- Compute the number of elements using
sizeof. - Use a
forloop with asize_tindex to print every element. - Calculate the total sum of the array values.
- Print the element count with the correct
printfformat specifier.
Keep learning
Related questions
2D Array Loop Order and Cache Performance in C
Learn why swapping nested loops changes 2D array performance in C, using row-major memory layout, cache locality, and practical benchmarks.
Array-to-Pointer Conversion in C and C++ Explained
Learn what array-to-pointer conversion means in C and C++, how array decay works, and how it differs from a pointer to an array.
Building More Fault-Tolerant Embedded C++ Applications for Radiation-Prone ARM Systems
Learn practical C++ and compile-time techniques to reduce soft-error damage in embedded ARM systems exposed to radiation.