2

I've been lately experimenting with dynamically allocated arrays. I got to conclusion that they have to store their own size in order to free the memory.

So I dug a little in memory with pointers and found that 64 bytes directly before and 14 bytes directly after array don't change upon recompilation (aren't random garbage).

I represented these as unsigned int types and printed them out in win console:

Here's what I got: Screenshot (array's content is between fdfdfdfd uints in representation)

So I figured out that third unsigned int directly before the array's first element is the size of allocated memory in bytes.

However I cannot find any information about rest of them.

Q: Does anyone know what the memory surrounding array's content means and care to share?

The code used in program:

#include <iostream>

void show(unsigned long val[], int n)
{
    using namespace std;

    cout << "Array length: " << n <<endl;
    cout << "hex: ";
    for (int i = -6; i < n + 1; i++)
    {
        cout << hex << (*(val + i)) << "|";
    }
    cout << endl << "dec: ";
    for (int i = -6; i < n + 1; i++)
    {
        cout << dec << (*(val + i)) << "|";
    }
    cout << endl;
}

int main()
{
    using namespace std;

    unsigned long *a = new unsigned long[15]{ 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14 };
    unsigned long *b = new unsigned long[15]{ 0 };
    unsigned long *c = new unsigned long[17]{ 0 };

    show(a, 15);
    cout << endl;
    show(b, 15);
    cout << endl;
    show(c, 17);
    cout << endl;

    cout << endl;
    system("PAUSE");

    delete[] a;
    delete[] b;
    delete[] c;
}
Kosmo
  • 78
  • 6
  • 3
    Memory surrounding an array's content is undefined. – AndyG Mar 01 '17 at 21:15
  • 6
    This is going to be implementation defined. You need to detail what implementation you are using. – NathanOliver Mar 01 '17 at 21:16
  • 1
    Your image link is broken. Dont link images, include them in the questions. – Fantastic Mr Fox Mar 01 '17 at 21:17
  • 6
    Visual Studio's debug C++ library adds 0xfdfdfdfd around a memory allocation to detect buffer underflow/overflow. This is implementation-specific and likely disappears if linked to the release C++ library. – Mark Tolonen Mar 01 '17 at 21:23
  • 1
    [c++ arrays](http://stackoverflow.com/questions/4810664/how-do-i-use-arrays-in-c/4810668) – JGroven Mar 01 '17 at 21:35
  • @NathanOliver ITYM it will depend on the implementation. "implementation defined" means that the C++ Standard requires the compiler to document the behaviour -- which is not the case here – M.M Mar 01 '17 at 21:54
  • @M.M You are correct. *implementation defined* was not correct. – NathanOliver Mar 01 '17 at 22:22

2 Answers2

3

It typically means that you carried out your experiments using a debugging configuration of the project and debugging version of the standard library. That version of the library uses some pre-defined bit-patterns to mark the boundaries of each allocated memory block ("no man's land" areas). Later, it checks if these bit-patterns survived intact (e.g. at the moment of delete[]). If they did not, it implies that someone wrote beyond the boundaries of the memory block. Debug version of the library will issue a diagnostic message about the problem.

If you compile your test program in release (optimized) configuration with release (optimized) version of the standard library, these "no man's land" areas will not be created, these bit-patterns will disappear from memory and the associated memory checks will disappear from the code.


Note also the the memory layout you observed is typically specific for arrays of objects with no destructors or with trivial destructors (which is basically the same thing). In your case you were working with plain unsigned long.

Once you start allocating arrays of objects with non-trivial destructors, you will observe that it is not just the size of memory block (in bytes) that's stored by the implementation, but the exact size of the array (in elements) is typically stored there as well.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
0

"I got to conclusion that they have to store their own size in order to free the memory." No they don't.

Array does not free it's memory. You never get an array from new/malloc. You get a pointer to memory under which you can store an array, but if you forget size you have requested you cannot get it back. The standard library often does depend on OS under the hood as well.

And even OS does not have to remember it. There are implementations with very simple memory management which basically returns you current pointer to the free memory, and move the pointer by the requested size. free does nothing and freed memory is forgotten.

Bottom line, memory management is implementation defined, and outside of what you get nothing is guaranteed. Compiler or OS can mess with it, so you need to look documentation specific for the environment.

Bit patterns that you talk about, are often used as safe guards, or used for debugging. E.g: When and why will an OS initialise memory to 0xCD, 0xDD, etc. on malloc/free/new/delete?

Community
  • 1
  • 1
luk32
  • 15,812
  • 38
  • 62
  • "OS is responsible for doing free/delete" - nope, the C or C++ library is responsible for that, as it is for malloc/new. –  Mar 01 '17 at 21:39
  • "but if you forget size you have requested you cannot get it back" The whole point of new (and malloc) is that you don't need to remember the size - the library does it for you. –  Mar 01 '17 at 21:40
  • I didn't mean making the call, but the implementation. Would a clarification on that help? I don't think those are defined in standard, but I would like to see the source if it's otherwise. – luk32 Mar 01 '17 at 21:41
  • `auto x=new int[10];` followed by `delete[] x;` will free all ten allocated `int` so does somewhere keep that information. – Walter Mar 01 '17 at 21:42
  • @luk It's obviously defined that you don't need to hang on to the size, as neither free nor non-placement delete allow any way of specifying it. –  Mar 01 '17 at 21:43
  • 1
    @Walter No it does not have to. I gave the outline for memory management implementation which does not need to remember this. And it's not required by the standard AFAIK. – luk32 Mar 01 '17 at 21:43
  • @NeilButterworth Ok, then I don't know what you say is wrong with my answer. Regarding the dynamic memory managment c/c++ is not responsible for any new/malloc/free/delete. – luk32 Mar 01 '17 at 21:45
  • "Regarding the dynamic memory managment c/c++ is not responsible for any new/malloc/free/delete." Of course C and C++ are responsible! These are not OS system calls , they are functions in the standard library and/or compiler implementation. –  Mar 01 '17 at 21:46
  • @NeilButterworth But those calls need to get memory from the system/kernel. Don't they? They probably would need to free them too. I might be wrong. I did reword that fragment too. If I am wrong, then most certainly *please* explain how it works (or point to a material), so I can understand it, and avoid such mistakes. – luk32 Mar 01 '17 at 21:56
  • @luk They certainly will need to interact with the OS at some point, but the interaction may be fairly minimal (for example, it's common for free/delete not to attempt to give memory back to the OS) and they are _not_ themselves OS system calls. –  Mar 01 '17 at 21:59
  • "*No they don't. ... if you forget size you have requested you cannot get it back."* You can't get the size indeed, but it has to be stored. Otherwise it'd be impossible for `delete []` to decide *how many destructors* have to be called. – HolyBlackCat Mar 01 '17 at 22:09
  • @HolyBlackCat No it doesn't. No destructors have to be run (these are ints and the compiler knows this) and so can fast path this situation and not remember _at all_ how many items are in the array. It just has to know how to free memory – Mike Vine Mar 01 '17 at 22:21
  • @Mike Which requires knowing the size of the allocated block somehow, any destructors being called is a bit of a red herring, and wouldn't apply to C code. –  Mar 01 '17 at 22:22
  • @NeilButterworth No, there are plenty of allocators which dont know the size of the allocation (just the upper bound) - see low fragmentation heaps (often called bucket heaps). And even if it DOES know the size of the allocation, this doesn't have to be linked at all to the number of items in the array. It just has to be big enough. – Mike Vine Mar 01 '17 at 22:24
  • @MikeVine I've meant that size has to be stored in general case (for types with non-trivial destructors). I really should've said that. – HolyBlackCat Mar 01 '17 at 22:26
  • @Mike " there are plenty of allocators which dont know the size of the allocation (just the upper bound" so it has to remember _something_ but typically this is a size. You can of course write allocators that don't remember anything, but these tend not to be popular in real-world programs, and are not possible in C++, unless you use different allocators for types with and without destructors, which is also not a popular solution. –  Mar 01 '17 at 22:28
  • @NeilButterworth Go and read up on low fragmentation heaps. They do not have any per allocation overhead at all (in the best case) and so do not remember anything at all on a per allocation basis. They traditionally use addresses to segment the heap into buckets and from those know the size < bucket size. Yes, from an allocation the heap could theoritcally tell you something like 'its less than 256 bytes, apart from that I know nothing'. But that's useless in the context of the question. – Mike Vine Mar 01 '17 at 22:33
  • @Mike I never used the word "overhead", and from the first article on LFHs I found: "When the LFH is enabled, the system allocates memory in certain predetermined sizes" so the size is known. I don't wish to carry this on any further, as SO is not a discussion forum. –  Mar 01 '17 at 22:36