Can the size of pointers vary between data and function pointers?

> type ppp.c
#include <stdio.h>
#include <stdlib.h>

int global = 0;

int main(void) {
    int local = 0;
    static int staticint = 0;
    int *mall;
    int (*fx)(void);

    fx = main;
    mall = malloc(42); /* assume it worked */
    printf("#sizeof pointer to local: %d\n", (int)sizeof &local);
    printf("#sizeof pointer to static: %d\n", (int)sizeof &staticint);
    printf("#sizeof pointer to malloc'd: %d\n", (int)sizeof mall);
    printf("#sizeof pointer to global: %d\n", (int)sizeof &global);
    printf("#sizeof pointer to main(): %d\n", (int)sizeof fx);
    free(mall);
    return 0;
}
> tcc -mc ppp.c
Turbo C  Version 2.01 ...
warnings about unused variables elided ...
Turbo Link  Version 2.0 ...
> ppp
#sizeof pointer to local: 4
#sizeof pointer to static: 4
#sizeof pointer to malloc'd: 4
#sizeof pointer to global: 4
#sizeof pointer to main(): 2
> tcc -mm ppp.c
> ppp
#sizeof pointer to local: 2
#sizeof pointer to static: 2
#sizeof pointer to malloc'd: 2
#sizeof pointer to global: 2
#sizeof pointer to main(): 4

tcc -mc generates code in the "compact" model; tcc -mm generates code in the "medium" model


On real-mode x86, code & data is accessed by segment + offset, each a 16-bit quantity. "Near" pointers were 16-bit only and used the current segment, "Far" pointers were 32-bit and specified the segment and offset. For C compilers, there were several different memory models you could choose, with different defaults of near or far pointers for code and data.

For example, the "Medium" memory model used near pointers for data but far pointers for code by default.

I wouldn't be surprised if some modern embedded processors have similar memory models.


Note that POSIX requires pointers to objects and pointers to functions to be the same size:

2.12.3 Pointer Types

All function pointer types shall have the same representation as the type pointer to void. Conversion of a function pointer to void * shall not alter the representation. A void * value resulting from such a conversion can be converted back to the original function pointer type, using an explicit cast, without loss of information.

Note: The ISO C standard does not require this, but it is required for POSIX conformance.

Consequently, systems that claim POSIX-compliance will be uniform. If you only target such machines, then you do not have to worry about the differences.


Machines that use a Harvard Architecture have separate storage for instructions and data, and correspondingly have separate address spaces for instructions and data. In such an architecture, there is no real reason to have the two address spaces (or physical memory backing them) be the same size.


16-Bit PIC microcontrollers (Microchip PIC24 and dsPIC) are examples of Harvard architecture devices with different data and code space pointer sizes. The separate address spaces differ in size - on chip SRAM has a greater chip-area cost that Flash memory, there is much less of it, so data pointers can be smaller.

This is also true of PIC12, PIC16, and PIC18 architectures also, but dsPIC is what I happen to be using currently.