What are Pointer Types and Sizes – Learn C Programming Online Free

Need Help or More Information?

Questions about this free C programming course, embedded C training or career guidance? Get in touch.

Free C Programming Course · Pointers · Lecture 7

Pointer Types and Sizes

Why every pointer needs a base type, how many bytes a dereference reads, and how to measure it with sizeof in embedded C programming.

What You Will Learn

In this part of our free C programming course you will discover why a pointer is declared with a type, even though the pointer itself is only an address. This free C tutorial explains how the base type controls the number of bytes touched by the indirection operator, why all pointers on one machine have the same size, and how this knowledge helps in embedded C programming when you inspect raw memory.

  • The two numbers attached to every pointer: its own size and the size of what it points to
  • How the base type decides how many bytes *p reads
  • Measuring both sizes with the sizeof operator
  • Viewing the bytes of a 32-bit value through a byte pointer
  • What endianness means for the byte order you see

Prerequisites

You should understand declaring pointers and the dereference operator from the previous lecture of this free embedded C course, and basic data types such as char, int, float and double.

Why a Pointer Needs a Base Type

A pointer stores one address, and an address identifies exactly one byte: the first byte of an object. That is not enough information to read the object. Is it a one-byte character, a four-byte integer or an eight-byte double? The compiler answers this question from the pointer’s declared base type. When you write *p, the compiler generates code that reads as many bytes, starting at the stored address, as the base type occupies.

This is the reason you cannot declare a pointer without saying what it points to. The type is not decoration, it is the instruction that tells the machine how wide the access is and how to interpret the bits.

Bytes read when dereferencing at the same start address

address+0+1+2+3+4+5+6+7
char *read–––––––
int *readreadreadread––––
double *readreadreadreadreadreadreadread

Suggested image 2: a memory strip with highlighted bytes for char, int and double reads. ALT text: “free embedded C course pointer base type decides bytes read”.

Pointer Size vs Pointed-To Size

Two different sizes are involved, and mixing them up is a classic mistake:

  • sizeof(p) is the size of the pointer variable. It must be able to hold any address on the machine, so it is the same for every pointer type: typically 8 bytes on a 64-bit PC and 4 bytes on a 32-bit microcontroller such as an ARM Cortex-M.
  • sizeof(*p) is the size of the object being pointed to. It follows the base type: 1 for char, usually 4 for int and float, 8 for double.

Older textbooks print pointer sizes of 2 bytes because they target 16-bit compilers. Modern PCs and the microcontrollers you will use today behave differently, which is why you should always measure with sizeof instead of memorising numbers.

Program: Measure Pointer Sizes with sizeof

Save the following as ptr_sizes.c. It also shows how a byte pointer can examine the individual bytes of a 32-bit value.

#include <stdio.h>
#include <stdint.h>

int main(void)
{
    char   c = 'x';
    int    i = 12;
    float  f = 12.4f;
    double d = 18.3;
    char   *pc = &c;
    int    *pi = &i;
    float  *pf = &f;
    double *pd = &d;

    printf("%-8s %-14s %-14s\n", "pointer", "sizeof(ptr)", "sizeof(*ptr)");
    printf("%-8s %-14zu %-14zu\n", "char *",   sizeof pc, sizeof *pc);
    printf("%-8s %-14zu %-14zu\n", "int *",    sizeof pi, sizeof *pi);
    printf("%-8s %-14zu %-14zu\n", "float *",  sizeof pf, sizeof *pf);
    printf("%-8s %-14zu %-14zu\n", "double *", sizeof pd, sizeof *pd);

    uint32_t word = 0x11223344u;
    uint32_t *pw = &word;
    unsigned char *pb = (unsigned char *)&word;
    printf("\n*pw  = 0x%08X (4 bytes read)\n", (unsigned)*pw);
    printf("*pb  = 0x%02X (1 byte read)\n", *pb);
    for (size_t k = 0; k < sizeof word; k++)
        printf("byte[%zu] = 0x%02X\n", k, pb[k]);
    return 0;
}
$ gcc -Wall -Wextra -std=c11 -o ptr_sizes ptr_sizes.c
$ ./ptr_sizes
pointer  sizeof(ptr)    sizeof(*ptr)
char *   8              1
int *    8              4
float *  8              4
double * 8              8

*pw  = 0x11223344 (4 bytes read)
*pb  = 0x44 (1 byte read)
byte[0] = 0x44
byte[1] = 0x33
byte[2] = 0x22
byte[3] = 0x11

The first column of numbers is always the same, the second follows the base type. On a 32-bit board the first column would read 4 instead of 8, but the second column would stay the same.

What the Byte View Teaches You

The same address was used to read the value through a 4-byte pointer and through a 1-byte pointer. The wide pointer returned the whole number 0x11223344, while the byte pointer returned only 0x44, the byte stored first. The least significant byte comes first in memory on this machine, which is called little-endian order. Almost all ARM Cortex-M and x86 systems are little-endian, while some network protocols use big-endian order. Reading memory through an unsigned char pointer is the standard, well-defined way to look at an object’s raw bytes.

Rule of thumb

Pointer type decides how many bytes and how to interpret them. Pointer size decides how far the machine can address.

Pointer Types in Embedded Systems

SituationPointer to useReason
Dump a buffer in a debugger or UART logconst unsigned char *Byte-wise access, no alignment problems
Access a 32-bit peripheral registervolatile uint32_t *Width must match the register, and access must not be optimised away
Walk an array of samplesint16_t * or matching typeElement step equals the sample size
Accept any kind of data in a generic APIvoid *Carries an address but cannot be dereferenced until cast

A void * has no base type, so the compiler does not know how many bytes to read and refuses a direct dereference. You must cast it to a concrete pointer type first.

Common Mistakes

  • Assuming pointers are 2 bytes. Always use sizeof, since the answer depends on the target.
  • Using sizeof on a pointer to get an array length. sizeof(p) gives the pointer size, not the number of elements. Pass the length separately.
  • Casting a pointer to a wider type and dereferencing it. Reading 4 bytes where only 1 exists touches neighbouring memory, and it may also be misaligned, which faults on some cores.
  • Printing addresses with %d or %u. Use %p with a (void *) cast so 64-bit addresses are not truncated.
Alignment. Many microcontrollers fault when a 32-bit access starts at an address that is not a multiple of four. Prefer byte access, or memcpy, when you decode data from a raw buffer.

Best Practices

  • Choose the pointer type that matches the object, not the one that is convenient.
  • Use fixed-width types from stdint.h for registers and protocol fields.
  • Use size_t and %zu for sizes you print.
  • Use unsigned char for byte views, because plain char may be signed.

Key Takeaways

  • A pointer holds one address, and its base type says how many bytes to read from it.
  • sizeof(pointer) is the same for all object pointers on a machine, sizeof(*pointer) follows the type.
  • A byte pointer lets you inspect memory and reveals the machine’s byte order.
  • void * cannot be dereferenced until it is cast.

Conclusion

Pointer types look like a small detail, yet they control every memory access your program makes. Knowing the difference between the size of a pointer and the size of its target will save you from subtle bugs in embedded C programming. Next, in this free C programming course, we use the base type again to understand pointer arithmetic: why adding 1 to an int pointer moves four bytes.

Frequently Asked Questions

Why do all pointers have the same size?

Each must hold a full machine address. The type of the target does not change how wide an address is.

How many bytes does a pointer take on an ARM Cortex-M?

Four bytes, because the core uses 32-bit addresses.

What does sizeof(*p) mean when p is not initialised?

It is fine. sizeof looks only at the type, it does not evaluate the dereference, so no memory is read.

What is little-endian?

A byte order where the least significant byte of a multi-byte value is stored at the lowest address.

Why is void * not dereferenceable?

It has no base type, so the compiler cannot know the access width.

Should I use char or unsigned char to view bytes?

Use unsigned char so values 128 to 255 print without sign extension.

Keep Learning for Free

Run the program on your own computer, then try adding a short and a long long pointer to the table and compare the results.

Need Help or More Information?

Questions about this free C programming course, embedded C training or career guidance? Get in touch.

1 Comment

Leave a Reply

Your email address will not be published. Required fields are marked *