Skip to content
VLSI Mentor

C Part I · Module 2 · Values, Variables, Types and Representation

sizeof, size_t and the Size of a Type

Two chapters in this module have deliberately left the same question open. Chapter 2.2 drew an int occupying four bytes and told you not to memorise the number; Chapter 2.5 explained why C refuses to fix type sizes and said to ask your own machine rather than assume. This chapter is how you ask. It introduces the operator that measures storage, the one size C does fix and why that particular one has to be fixed, the unsigned type C uses for every count of bytes, and the small surprise in how the operator treats what you hand it. With those, the picture Module 2 has been building is complete: you can describe an object, say what it holds, say how its value is represented, and now measure the room it takes up.

Foundation12 min readCPart IFundamentalsMemory

Part I · Module 2 · Chapter 2.12 · sizeof, size_t and the Size of a Type

1. Asking Instead of Assuming

sizeof reports how many bytes something occupies. You can hand it a type name in parentheses, or an object:

Azvya Education Pvt. Ltd.VLSI Mentor
measuring this machine
#include <limits.h>
#include <stdio.h>

int main(void)
{
    int count = 0;

    printf("char      %zu byte(s)\n", sizeof(char));
    printf("short     %zu byte(s)\n", sizeof(short));
    printf("int       %zu byte(s)\n", sizeof(int));
    printf("long      %zu byte(s)\n", sizeof(long));
    printf("double    %zu byte(s)\n", sizeof(double));
    printf("count     %zu byte(s)\n", sizeof count);
    printf("bits in a byte: %d\n", CHAR_BIT);
    return 0;
}

On the machine this chapter was written on:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
char      1 byte(s)
short     2 byte(s)
int       4 byte(s)
long      8 byte(s)
double    8 byte(s)
count     4 byte(s)
bits in a byte: 8

Those numbers describe one machine. Run the same program on a small embedded target and several will differ — which is exactly Chapter 2.5's point, and the reason this operator exists rather than a table in the standard.

Two details of the syntax. A type name needs parentheses: sizeof(int). An object does not: sizeof count is valid, though sizeof(count) is also fine and most code writes the parentheses either way.

2. The One Size C Does Fix

Look at the first line of that output again:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
char      1 byte(s)

sizeof(char) is 1. Always, on every implementation, guaranteed.

That is not a measurement that happened to come out at 1 on this machine. It is true by definition, and the reason is worth a moment because it makes the whole set of numbers cohere:

sizeof counts in units of char. A byte is the amount of storage a char occupies.

So sizeof(char) is asking how many chars fit in a char, and the answer cannot be anything but 1. Every other number in §1 is therefore a multiple of the same unit: an int on this machine occupies four times what a char does.

This is why Chapter 2.8 said char is the natural way to talk about raw bytes. It is not a convention — it is the unit the whole measurement system is built on.

3. A Byte Is Not Guaranteed to Be Eight Bits

The last line of §1's program printed something you have not met:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
bits in a byte: 8

CHAR_BIT comes from <limits.h>, like the range names in Chapter 2.5, and it reports how many bits are in a byte on your implementation.

Chapter 2.4 said 8 and flagged it. Here is the precise version: C requires a byte to hold at least 8 bits and permits more. Eight is what you will find on every system you are realistically going to use; it is implementation-defined rather than fixed, which is why CHAR_BIT exists at all.

The practical consequence is narrow. sizeof gives you bytes, not bits, so if you ever need a count of bits it is sizeof(x) * CHAR_BIT and not sizeof(x) * 8. That is the kind of care that matters in the semiconductor work Part II reaches, and almost nowhere else in Part I.

4. size_t and %zu

sizeof does not produce an int. It produces a value of type size_t, and this is worth knowing now because size_t appears throughout C.

size_t is an unsigned integer type — Chapter 2.6's sense — chosen by the implementation to be large enough to hold the size of any object that can exist. C uses it consistently for sizes and counts of bytes, which is a sensible choice: a size cannot be negative.

Being unsigned means Chapter 2.6's warnings apply in full. Subtracting one size_t from a larger one is fine; getting the order wrong does not give you a negative, it gives you an enormous positive. That trap lives wherever size_t does.

Printing it needs its own conversion specifier:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
printf("%zu\n", sizeof(int));

The z says the argument is a size_t. Using %d instead is a mismatch — it may appear to work on a machine where the two happen to be the same size, and stop working elsewhere. Chapter 12.2 covers the rule; here, use %zu for anything sizeof produced.

5. sizeof Does Not Run Its Operand

One genuine surprise, and it follows from what the operator is for.

sizeof needs to know the type of what you hand it. It does not need the value. So it works out the type and stops — the expression you wrote is not evaluated:

Azvya Education Pvt. Ltd.VLSI Mentor
the assignment never happens
int count = 5;
size_t how_big = sizeof (count = 99);

count is still 5 afterwards. The = 99 was inside something whose value was never needed, so it never ran.

Clang says as much if you try it:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
warning: expression with side effects has no effect in an unevaluated context [-Wunevaluated-expression]

You would not write that deliberately. It matters because it tells you what sizeof is: a question about types, answered before the program runs, not a measurement taken while it runs. That is why it costs nothing at run time and why it can appear anywhere a value can.

6. Why This Closes the Module

Put the module together. You can now say, about any object in a C program:

  • What it is — an object: storage, with a type, holding a value (2.1).
  • Where it is — a run of consecutive bytes beginning at an address (2.2).
  • Whether it has a value yet — and why assuming zero is a bug (2.3).
  • How its value is recorded — bits, read in binary or written in hex (2.4, 2.7).
  • What kind of value — which integer type and why, signed or not (2.5, 2.6), a character (2.8), an approximation (2.9), a truth value (2.10).
  • Whether it may change (2.11).
  • How much room it takes — measured, not assumed (this chapter).

That is a complete account of a single object. Module 3 starts combining them, which is where all of this begins to pay for itself.

7. What You Should Be Able to Say Now

  1. sizeof reports how many bytes something occupies. A type name needs parentheses; an object does not.
  2. The numbers it gives are facts about your implementation, not about C.
  3. sizeof(char) is always exactly 1, because sizeof counts in units of char.
  4. CHAR_BIT reports bits per byte. C requires at least 8; 8 is what you will find.
  5. For a count of bits, use sizeof(x) * CHAR_BIT, never * 8.
  6. sizeof produces a size_t — an unsigned type C uses for every size and count of bytes.
  7. Because it is unsigned, Chapter 2.6's subtraction warning applies to it.
  8. Print it with %zu.
  9. sizeof does not evaluate its operand. It is a question about types, answered before the program runs.

8. Exercise

Exercise 1 — measure your own machine. Run §1's program and write down every number. Which differ from the output shown, and which are guaranteed to match no matter where you run it?

Exercise 2 — explain the fixed one. Why is sizeof(char) guaranteed to be 1 on every implementation, when sizeof(int) is not guaranteed to be anything? Answer using §2, without using the word "byte" more than once.

Exercise 3 — predict. What does this print, and what is count afterwards?

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
int count = 5;
printf("%zu\n", sizeof (count = 99));
printf("%d\n", count);

Exercise 4 — correct the specifier. A colleague writes printf("%d\n", sizeof(int));. Say what is wrong, why it may appear to work, and give the fix.

Exercise 5 — count the bits. Write the expression giving the number of bits in an int on the current machine. Explain why sizeof(int) * 8 is not the right answer even though it gives the right number here.

Exercise 6 — spot the risk. Both values are size_t. What happens when used exceeds capacity, and which chapter explained why?

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
size_t capacity = 64;
size_t used = 100;
size_t remaining = capacity - used;

9. Summary

  • sizeof measures storage in bytes, for a type or an object, and reports a fact about your implementation rather than about C.
  • sizeof(char) is always 1 — by definition, because sizeof counts in units of char. Every other size is a multiple of that unit.
  • CHAR_BIT is bits per byte, at least 8 by C's rules and 8 in practice. Use it rather than writing 8.
  • The result is a size_t, an unsigned type used throughout C for sizes and counts — so unsigned subtraction needs the same care Chapter 2.6 described.
  • Print it with %zu.
  • The operand is not evaluated. sizeof is a question about types, settled before the program runs.

Module 2 has described a single object completely — what it is, where it lives, what it holds, how that is recorded, and how much room it takes. Every program here has done almost nothing with those objects except print them, because the tools for combining values have not arrived. That is Module 3: the operators, what each one produces, and — more subtly than it sounds — what type the result has when values of different types meet.

Standards & specifications

Governing standard
ISO/IEC 9899 (the C language standard)(opens ISO/IEC (JTC 1/SC 22/WG 14) in a new tab)

Defines the C language itself — the type system, expression semantics, object model, storage duration and the behaviour categories a conforming implementation must honour. It is a FAMILY of editions (C89/C90, C99, C11, C17, C23) and this curriculum names the edition wherever a claim depends on one. It does not define any foreign-function interface to a hardware description language, and it does not define platform behaviour: what a memory-mapped access does on real silicon is the implementation's and the device's contract, not this standard's. Working drafts published by WG14 are freely available and are what most engineers read day to day; a draft is not the published standard.

This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.

Where this fits

Part of the C Programming curriculum.