When using member-function-pointers I noticed that they have a size of 8Bytes each. However when I ask the compiler it says it is only 4Bytes. Having a couple of objects in you program which have member-function-pointers and are created dynamically with new will allways crash your program.
Please refer to the following source-code and documentation:
#include <StdIO.h> #include <Reg164ci.h> /* A simple external object */ class cCalculator { public: int calculate(); }; int cCalculator::calculate(){ return 27; } /* The description of the member-function-pointer */ typedef int (cCalculator::*fptr)(); /* An object with an array of such member-function-pointers which is able to call these functions by an index. */ class cAnObject { public: cAnObject(); ~cAnObject(); fptr fptr_array[2]; // 2 x 8 bytes = 16 bytes (see watchwindow) int member1; // 2 bytes int member2; // 2 bytes // -------------------------------------- // total: 20bytes = 0x14bytes }; /* The constructor */ cAnObject::cAnObject() { member1 = 1; member1 = 2; } /* The destructor */ cAnObject::~cAnObject() { member1 = 0; member1 = 0; } int main (void) { // now we have the external object cCalculator calc1; cCalculator calc2; // and a couple of objects containing some function pointes cAnObject test1; cAnObject test2; cAnObject test3; fptr memberfunction = &(cCalculator::calculate); test1.fptr_array[0] = memberfunction; // here we get the size of our objects (see watch window) volatile unsigned long object_addr1 = (unsigned long) &test1; volatile unsigned long object_addr2 = (unsigned long) &test2; volatile unsigned long object_sizeA = object_addr2 - object_addr1; volatile unsigned long object_sizeB = sizeof( test1 ); //... // please notice that the static objects have a distance // which is different from its size ? why? // count the bytes and you see the sizeof() is wrong !! // now we simply create the dynamic object (with the wrong size), why ? compiler ? // which here leads to an -access violation-, due to // the wrong calculated size of the function pointers cAnObject * p_test4; p_test4 = new cAnObject; // and set the function p_test4->fptr_array[0] = memberfunction; // calc the size volatile unsigned long objectSize4 = sizeof( (*p_test4) ); // ---- // now the function call itself cCalculator * p_calc; // here we select our target object p_calc = &calc1; // do the static object call by an index == 0 fptr p_fct1 = test1.fptr_array[0]; int x = ( (p_calc)->*( p_fct1 ))( ); // now use the other object p_calc = &calc2; // do the dynamic object call by an index == 0 fptr p_fct4 = p_test4->fptr_array[0]; int y = ( (p_calc)->*( p_fct4 ))( ); // if there is any time please notice that there is a second bug: // try to remove the line "fptr p_fct4 = ..." // and put it into one line like " int y = ( (p_calc)->*( p_test4->fptr_array[0] ))( ); " // and your whole program will crash ! // just print the result printf("%d = %d \r", x, y ); while (1); }
// please notice that the static objects have a distance // which is different from its size ? why?
Why not?
// count the bytes and you see the sizeof() is wrong !!
You have nowhere near enough proof to back such an outrageous claim. There's no rule forcing the tools to locate individual variables in memory like you assume it must.
Sven, Some of your assumptions are unfounded. As Hans-Bernhard Broeker noted, you cannot assume anything about the placement of objects - it depends of quite some factors, including the optimination level. In addition to that, do remember that compiler tend to patch structues/objects to have a size that is a power of 2, so that iterations offsets can be easily calculated using shifts. This and other factor may contribute to the observations you made. Please refer to your map file for complete details.
"compiler tend to patch structues/objects to have a size that is a power of 2, so that iterations offsets can be easily calculated using shifts."
Thrue - but not just any old "power of 2", and not (directly) for computing offsets:
Processors with a Word size larger than one tend to have specific preferences or even requirements on the alignment of objects - especially multi-byte objects.
Therefore, the compiler will tend to produce code that follows those preferences - which may involve "padding" in structures and between variables.
I understand the preceeding comments from Hans-Bernhard Broeker and Tamir Michael as question of how can I be sure that the linker puts both objects into the memory as I assume it does.
(1) As mentioned, you can count both objects byte-size and you figure out its distance is the correct size. But this is not the point!
(2) You can create two objects dynamically with new at once, where you get two pointers which actually point to the correct starting of the two objects (note that there is an addtional space for the mem-manager of 6bytes -> Kernighan & Ritchie). Then write to the second-object member-variables "member1" and "member2" and the "fptr_array" and watch the contents of the first object. You will see your written variable-data in the first object not in the seconed as you did!! Coincidence? And there are rules of how dynamic memory is allocated and manged -> Kernighan & Ritchie. There are no rules that allow modifying one object which automatically manipulates another. (As far as I know).
(3) This behavior shows up only if you use member-function-pointers or arrays of it in objects which you create dynamically. (note the -access violation- while creating the object.)
As I noticed, the normal function pointer has a size of 4bytes however the member-function-pointer has a size of 8bytes. When you rise your array size count from nr=2 (fptr fptr_array[2]) to anything else you will see there is allways a difference of 4bytes x nr not 8bytes x n.
... what from my point of view proofs that the internal sizeof() function calculates the wrong size for my objects with member-function-pointers.
What is the sizeof for obj1, and array below?
cAnObject obj1; cAnObject array[2];
or possibly:
cAnObject c; cAnObject *p0 = &c; cAnObject *p1 = p0+1;
What is the value of (char*)p1 - (char*)p0?
Whatever padding the compiler does, n*sizeof(obj) must be the amount of memory to allocate for an n-sized array, and incrementing an object-pointer must move the pointer forward exactly sizeof() bytes.
Note that your assumptions that an allocated memory block is always 6 bytes larger is not true. That depends completely on the memory manager. All you know is that the pointer you receive has the requested number of usable bytes, and has the best alignment needed for any data type. If for example the processor requires that double-precision floating point numbers are aligned on an n*8 address, then malloc() will return a pointer that is _at least_ aligned to that requirement.
Allright. But this is not the point. The point is that malloc gives me a pointer to an object and whenever I write to its member-variables it writes to an address which is beyond the allowed space. What only could be a reason for a wrong sizeof()! (see -access violation- )
Again this usually works, but not if any member is a member-function-pointer or an array of it.
... If malloc gave me to much space due to some padding-stuff ,... who cares? But it gives me not enough memory!
Please report back the results of the test cases I described.
testresults1:
// test from Per Westermark cAnObject obj1; cAnObject array[2]; volatile unsigned long size_obj1 = sizeof(obj1); >> 0xC volatile unsigned long size_array = sizeof(array); >> 0x18
testresults2:
cAnObject c; cAnObject *p0 = &c; cAnObject *p1 = p0+1; >> p0 = 0x10fc0 >> p1 = 0x10fac
You are exactly right this is may problem!
is that a typo?
>> p1 = 0x10fac
should it be
>> p1 = 0x10fcc
You are right this was what I also expected but it is not a typo!. This is really what I was looking for. Note that this only happens when your objects have members that are member-function-pointers or arrays of it.
You are on it!
(By the way I can send a screenshot)
As this has come now to a final comon understanding of what is actually wrong with the sizeof(), do you see any way of how to deal with it?
As this has come now to a final comon understanding
It hasn't. You've made all kinds of claims without much back-up fact.
To go all the way back: how did you arrive at the conclusion that static member function pointers occupy 8 bytes? The compiler, which really should know, clearly thinks otherwise. It stores your object in 12 bytes, and an array[2] of them in 24, when you said one of them must be 20 bytes. I.e. if something's wrong here, it's not sizeof() as such. It goes deeper.
You made wild claims about malloc() storage overheads --- but nowhere in you code do you even call malloc(). Nor do the books by K&R have any relevance to C++'s operator 'new'.
You stated completely wrong things about distances of unrelated variables in memory, too. Please be aware that inspecting values of (presumably) unused variables in a debugger generally proves diddly-squat.
What you really need to do is isolate this into a minimal, broken testcase, and present it to Keil tech support.
Well then:
Arriving in the debugger watchwindow I can see 8bytes for each member-function-pointer in the array, what is for me proof enough. If the debugger says it is 8bytes then "I assume" it is correct.
Now we can come to the malloc. As far as I know a simply new is in EC++ this:
void *operator new(size_t size) { void *ptr; if (size == 0) size = 1; ptr = (void *) malloc (size); if (ptr == NULL) { __abort_execution (ec_outofmemory); } return (ptr); }
--Basically a malloc with a given size. And malloc() is actually Standard C or is this only an assumption?
As malloc doesn't know anything about the object to be created it relys on the given size. This is why size needs to be calculated the correct way. In my example my object has a size of (sizeof()) 0xc.
Whenever I take a pointer p0 as proposed by "Per Westermark" and then take the next object (*p1 = p0+1) the compiler itself calculates the offset for "+1". As seen in the test results between p0 and p1 is a distance of 0x14. Although malloc would give 0xc the compiler would expect the next object after 0x14. Depending on whatever is after 0xc in the memory it is going to be overwritten.
@Per Westermark: You are right "Kernighan & Ritchie" only stated that there is management overhead for a value of the current object size and a pointer to the next empty memory-space. But I noticed that in C166 it is 6bytes. What can be evaluated by simply getting two pointers from malloc.
As shown with the testcases above we have a simple testcase without an answere.