views:

173

answers:

3

In C, I need to know the size of a struct, which has function pointers in it. Can I be guaranteed that on all platforms and architectures:

  • the size of a void* is the same size as a function pointer?
  • the size of the function pointer does not differ due to its return type?
  • the size of the function pointer does not differ due to its parameter types?

I assume the answer is yes to all of these, but I want to be sure. For context, I'm calling sizeof(struct mystruct) and nothing more.

+11  A: 

No, no, no.

C doesn't favour Harvard architectures with different code and data pointer sizes, because ideally when programming for such an architecture you want to store data in program memory (string literals and the like), and to do that you'd need object pointers into the code space. But it doesn't forbid them, so as far as the standard is concerned function pointers can refer to an address space which has a different size from the data address space.

However, any function pointer can be cast to another function pointer type[*] and back without trashing the value, in the same way that any object pointer can be cast to void* and back. So it would be rather surprising for function pointers to vary in size according to their signature. There's no obvious "use" for the extra space, if you have to be able to somehow store the same value in less space and then retrieve it when cast back.

[*] Thanks, schot.

Steve Jessop
More generally: Any function pointer can be converted to any other function pointer type and back. (C99 6.3.2.3/8)
schot
@schot: right, in that case it really would be perverse to make them different sizes, since regardless of size all function pointer types have to represent exactly the same set of values. Namely, the address of every function, plus a null pointer.
Steve Jessop
Excellent answer. My question from the other day illustrates potential pitfalls here: http://stackoverflow.com/questions/3889541/issue-with-null-pointers-on-harvard-architecture-platform
Vicky
"C doesn't really support Harvard architectures"; I think this is a red herring. There are plenty of such architectures that support C!
Oli Charlesworth
So I read you answer as saying 'no, yes, yes'?
Paul Biggar
@Oli: agreed, "support" was the wrong word. You can write a conforming implementation, but you need some ugly hacks if you want (a) object pointers smaller than function pointers, and also (b) to store data in code space as a programmer-controlled optimization. I'll have another go.
Steve Jessop
@Paul: No, it says "no, no, no". "Rather surprising if not" != "guaranteed so". You asked whether it's guaranteed, and it isn't. If you're asking me about all implementations ever, or even just all currently available for download and actively maintained, then I don't know the answer. It might be "yes".
Steve Jessop
@Steve Jessop: I don't think it necessarily needs to be "ugly hacks"! The machine code that dereferences a function pointer will fundamentally be different to code that dereferences an object pointer anyway, so I'm not sure heterogeneous sizes is necessarily a problem. And string literals (etc.) can be squirted into data memory by the runtime at startup, for instance.
Oli Charlesworth
@Oli: sure, you can copy stuff into data space, but then you have (a) but not (b). You're occupying valuable memory, which is what motivates a programmer-controlled optimization. By "ugly hack", I'm thinking of http://www.nongnu.org/avr-libc/user-manual/pgmspace.html. You can't then get a real "object" pointer, e.g. that the program itself could pass to `memcpy`, because memcpy doesn't know how to dereference it. To get that, you'd have to surrender, and just increase the size of object pointers, again costing valuable memory and probably requiring a runtime switch at every memory access!
Steve Jessop
...I've not programmed any such architecture, but I assume what happens in practice is that programmers use the ugly hack in order to temporarily copy any bits of ROM data that they need in RAM, for as long as they need them. This saves RAM compared with just defining all your literals willy-nilly, and ending up with them all copied into data space at startup as you describe.
Steve Jessop
@Steve: I see what you mean, but this is surely true of any language on a Harvard architecture, not just C? But I suppose in a higher-level language, you could abstract this access with a smart pointer.
Oli Charlesworth
@Oli: yes, it's the expectation of efficiency. If you were writing in asm you'd be pulling data out of ROM as required using the specific instructions. C makes data-in-code transparent on von Neumann but not on Harvard. I'm not even sure which higher-level languages you'd want to use on a platform so constrained that you'd be managing it by hand in C. Lisp, maybe.
Steve Jessop
+10  A: 

From C99 spec, section 6.2.5, paragraph 27:

A pointer to void shall have the same representation and alignment requirements as a pointer to a character type. Similarly, pointers to qualified or unqualified versions of compatible types shall have the same representation and alignment requirements. All pointers to structure types shall have the same representation and alignment requirements as each other. All pointers to union types shall have the same representation and alignment requirements as each other. Pointers to other types need not have the same representation or alignment requirements.

So no; no guarantee that a void * can hold a function pointer.

And section 6.3.2.3, paragraph 8:

A pointer to a function of one type may be converted to a pointer to a function of another type and back again; the result shall compare equal to the original pointer.

implying that one function pointer type can hold any other function pointer value. Technically, that's not the same as guaranteeing that function-pointer types can't vary in size, merely that their values occupy the same range as each other.

Oli Charlesworth
+2  A: 

In addition to the other answers, Wikipedia says this:

http://en.wikipedia.org/wiki/Function_pointer

Although function pointers in C and C++ can be implemented as simple addresses, so that typically sizeof(Fx)==sizeof(void *), member pointers in C++ are often implemented as "fat pointers", typically two or three times the size of a simple function pointer, in order to deal with virtual inheritance.

A function pointer is an abstraction. As long as the requirements of the standard are fulfilled, anything is possible. I.e. if you have less than 256 functions in your program, function pointers could be implemented by using a single byte with the value 0 for NULL and the values 1 to 255 as the index into a table with the physical addresses. If you exceed 255 functions, it could be extended to use 2 bytes.

Secure
Downvoter: What I've written is wrong, because...?
Secure
I do not see how this is wrong. This is truly a possibility for a legal C implementation, though I would be surprised if there were an implementation that did this.
nategoose