tags:

views:

588

answers:

6

I was just reading the section of the C FAQ on pointers.

It discusses not being able to use void * pointers to hold function pointers because pointers to data and pointers to functions may have differing sizes on some platforms and void * is only guaranteed be large enough to hold pointers to data.

Can anyone give an example of a platform where pointers to data and pointers to functions actually have differing sizes?

+5  A: 

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.

Michael
Old DOS and early Windows environments were like this, with some compilers.
Novelocrat
@Pavel - The question is directly about function pointers and data pointers, not between different data types.
Michael
Eh, I need new eyeglasses :(
Pavel Minaev
A: 

It's a "depends" situation. In C++ I recall that member function pointers are actually two pointers in size, but that may be purely an implementation detail.

In some of the really old pre-PC systems you could also have pointer size depend on what was being referenced (but then you could also have 11bit characters :D )

olliej
It's virtually impossible to implement a member function pointer in C++ in less than 2 machine words; the reason being that 1) dispatch via member pointer has to be virtual, so you can't just store address of method and 2) it is legal to declare member pointers to forward-declared classes, so you don't know if the class has any virtual methods in advance.
Pavel Minaev
+1  A: 

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.

Stephen Canon
It might be better to clarify why this is the case rather than just linking to Wikipedia.
Chris Lutz
Fair enough; I think the Wikipedia text is pretty informative and thought it was clear enough on its own, but reasonable people may differ.
Stephen Canon
While this is a good pointer (HA!) for the OP, a Harvard architecture does not necessitate a difference in pointer sizes, the size of the address space does. You can have 2 separate physical memories embedded in a single address space, in which case you may not need to distinguish between the size of void *s and size of other pointers. But I agree that a Harvard architecture machine is a likely place to begin looking for a machine with separate data and function pointers.
Falaina
Oh, it certainly doesn't *require* that they have different pointer sizes, it just makes it far more likely, and explains why you might expect a standard to support such things. The only actual examples of this that I have seen are [modified] harvard architectures.
Stephen Canon
+2  A: 
> 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

pmg
I'm assuming this is on an X86 machine?
Robert S. Barnes
... x86 16-bit real mode
Clifford
I think it is X86 ... I'm not sure. I run DOS inside a virtual machine on a x86-64 Linux host.
pmg
"Turbo C Version 2.01" is the give away. A circa-1989 antique compiler.
Clifford
The exe files are dated from 1989-11-05 02:01am -- it's a great compiler!
pmg
+1  A: 

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.

Clifford
+1  A: 

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.

Jonathan Leffler