63

This question was inspired by a similar question: How does delete[] “know” the size of the operand array?

My question is a little different: Is there any way to determine the size of a C++ array programmatically? And if not, why? Every function I've seen that takes an array also requires an integer parameter to give it the size. But as the linked question pointed out, delete[] must know the size of the memory to be deallocated.

Consider this C++ code:

int* arr = new int[256];
printf("Size of arr: %d\n", sizeof(arr));

This prints "Size of arr: 4", which is just the size of the pointer. It would be nice to have some function which prints 256, but I don't think one exists in C++. (Again, part of the question is why it doesn't exist.)

Clarification: I know that if I declared the array on the stack instead of the heap (i.e. "int arr[256];") that the sizeof operator would return 1024 (array length * sizeof(int)).

jww
  • 97,681
  • 90
  • 411
  • 885
Kip
  • 107,154
  • 87
  • 232
  • 265
  • Actually, if you allocated the array on the stack the sizeof operator would return 1024 -- which is 256 (the # of elements) * 4 (the size of an individual element). (sizeof(arr)/sizeof(arr[0])) would give the result 256. – Kevin Oct 13 '08 at 16:03
  • thanks, i overlooked that because i was actually using char[] in my test code (and sizeof(char) == 1) – Kip Oct 13 '08 at 16:20
  • While it is only hypotethical - since it does not work - I have to point out that you should have written `printf("Size of arr: %d\n", sizeof(*arr));` instead of `printf("Size of arr: %d\n", sizeof(*arr));` since you wish to retrieve the size of the dereferenced pointer. – mg30rg Jun 12 '14 at 10:32

20 Answers20

70

delete [] does know the size that was allocated. However, that knowledge resides in the runtime or in the operating system's memory manager, meaning that it is not available to the compiler during compilation. And sizeof() is not a real function, it is actually evaluated to a constant by the compiler, which is something it cannot do for dynamically allocated arrays, whose size is not known during compilation.

Also, consider this example:


int *arr = new int[256];
int *p = &arr[100];
printf("Size: %d\n", sizeof(p));

How would the compiler know what the size of p is? The root of the problem is that arrays in C and C++ are not first-class objects. They decay to pointers, and there is no way for the compiler or the program itself to know whether a pointer points to the beginning of a chunk of memory allocated by new, or to a single object, or to some place in the middle of a chunk of memory allocated by new.

One reason for this is that C and C++ leave memory management to the programmer and to the operating system, which is also why they do not have garbage collection. Implementation of new and delete is not part of the C++ standard, because C++ is meant to be used on a variety of platforms, which may manage their memory in very different ways. It may be possible to let C++ keep track of all the allocated arrays and their sizes if you are writing a word processor for a windows box running on the latest Intel CPU, but it may be completely infeasible when you are writing an embedded system running on a DSP.

Dima
  • 38,860
  • 14
  • 75
  • 115
  • 7
    There are absolutely arrays in C++. Howe else would you explain why with this "char x[4]; size_t sz = sizeof(x);" that 'sz' will be assigned 4? – Kevin Oct 13 '08 at 16:06
  • 2
    Kevin, x is not really an array. It is a pointer to a chunk of memory, which happens to be on the stack. And because it was allocated statically on the stack, the compiler knows its size. If you pass x to a function, it would not know its size. Arrays in C or C++ aren't first-class objects. – Dima Oct 13 '08 at 16:25
  • Just as a side note: sizeof is an operator and not a function, as pointed by the poster that it is not a real function. – xk0der Jan 07 '09 at 11:33
  • 11
    Dima, there are absolutely arrays. arrays are different from pointers. sadly, many teachers confuse it and tell their students they are "just" pointers. nope, they aren't. how else do you explain that this: char const**s = &"bar"; doesn't compile? [...] – Johannes Schaub - litb Jan 07 '09 at 11:41
  • (if you state that "bar" is a pointer to the character array "bar" which is "just" a pointer?). and the fact that arrays aren't first class objects doesn't mean there are "no real arrays". functions aren't first class too. so don't you have "real functions" in c++? – Johannes Schaub - litb Jan 07 '09 at 11:48
  • 8
    litb, the reason char const **s = &"bar"; does not compile is that "bar" is a constant and not an lvalue, so you can't take the address of it. It is the same as int *p = &5; which won't compile either. – Dima Jan 07 '09 at 18:27
  • 1
    By saying no "real" arrays, I meant precisely that C or C++ arrays are not first class objects. You certainly use arrays in C or C++, but a function void foo(int *a), taking a pointer as an argument, has no idea whether a is an array or a pointer to a single integer. – Dima Jan 07 '09 at 18:30
  • 1
    @Dima then please edit the answer to say that. I can write functions that take arrays but not pointers if you'd like more evidence. – Mooing Duck Apr 30 '12 at 06:15
  • also, the program does indeed know how big the allocated array is, else it couldnt delete the proper number before deallocation. – Mooing Duck Apr 30 '12 at 06:16
  • 1
    @MooingDuck, I do believe that my answer is already sufficiently clear. – Dima May 01 '12 at 13:07
  • 4
    It is clear, but almost everything is wrong. There is already a situation where sizeof is runtime instead of compile time, Arrays _do_ exist, and there are ways for the implementation to know the size of all arrays. Even a DSP _must_ keep the size information for allocations. – Mooing Duck May 01 '12 at 14:58
  • 8
    `void foo(int *a);` takes a pointer, `void foo(int (&a)[5]);` takes an array. Array names decay to pointers, which sucks, but it doesn't mean arrays and pointers are the same thing. – Cat Plus Plus May 01 '12 at 16:16
  • 3
    @Dima: You're wrong. Arrays do exist, and `"x"` *is* an lvalue. Array and pointer decay is very bad, but they *are* different things. A function `void foo(int* a)` takes a pointer to a single integer. The fact that this pointer may point to an element of an array, be it the first by pointer decay or any other means, is irrelevant. – Puppy May 01 '12 at 16:18
  • 3
    Anybody who sees this needs to vote it down. Tons and tons of errors here. This is a *common misconception* – std''OrgnlDave May 01 '12 at 16:50
  • Ok, everybody. The original question was about why sizeof(arr) doesn't return the size of the array. I believe I have answered that. – Dima May 01 '12 at 17:03
  • My other point is that C arrays are not first-class objects. Their size must be given at compile time, they must be allocated on the stack, and they decay to pointers. For most practical purposes, if you want to pass an array to a function you must pass the pointer to the first element and the size of the array. Even if you write a library of functions operating on fixed-size vectors or matrices, you would still be passing pointers and sizes around, because you cannot expect the caller to allocate everything on the stack. See "Numerical Recepies" for an example. – Dima May 01 '12 at 17:04
  • 2
    @Dima: When you say "1st-class object" what exactly do you mean? When you declare `int a[4];` there results a contiguous 4-byte chunk of memory. What is missing from this that suggests to you that this is not an array? I'm not trying to pile-on, I'm trying to get clarification. – John Dibling May 01 '12 at 17:20
  • @JohnDibling, a first-class object is an entity that can be constructed at run-time, passed as a parameter, returned from a subroutine, or assigned into a variable. http://en.wikipedia.org/wiki/First-class_object From the same article: In many older languages (for example C) arrays were not first-class: they could not be assigned as objects or passed as a parameter to a subroutine; only their elements could be directly manipulated. – Dima May 01 '12 at 17:50
  • 1
    @Dima: Then are you not arguing that there aren't arrays in C++, just that they aren't "1st class objects" by your definition? – John Dibling May 01 '12 at 17:53
  • @JohnDibling, I said in the answer that there are no "real" arrays in C, and I have later clarified in the comments that I meant that C arrays are not 1st-class objects. And not by my definition, but by a widely accepted definition. – Dima May 01 '12 at 17:56
  • @JohnDibling compare C arrays to Java arrays, for example. Java arrays can be assigned to each other, they can be passed into functions, and you do not have to keep passing their size along with them. – Dima May 01 '12 at 17:58
  • 1
    @Dima: OK. You could have just said "Yes." – John Dibling May 01 '12 at 17:59
  • @JohnDibling, and since you have mentioned C++, then std::vector is effectively a "real" array. – Dima May 01 '12 at 18:02
  • 1
    @Dima `"bar"` is an lvalue, and `&"bar"` is valid. However the latter's type is `char (*)[4]` which is not compatible with `char **`. – M.M Jan 12 '15 at 04:11
  • @Dima, I don't think you answered the question, "If delete[] can access the size of the allocated array, why can't the programmer". Apologies if I missed the point. – Isaac Turner Jun 03 '16 at 11:41
  • Short answer: because this is how new/delete are defined. Long answer: new/delete do internal bookkeeping, hidden from the outside world. How they do it is intentionally not standardized, and it depends on the compiler. One reason for hiding this information, is that new() typically allocates more memory then you ask because of alignment, and because it may use some of the allocated memory for its own bookkeeping. Since that bookkeeping is compiler-dependent, your code should not be relying on it. – Dima Jun 03 '16 at 15:40
  • What is the message of that fragment? You take the address of some int element of some array and ask for the size of the int pointer????????????????????????? – Sam Ginrich Feb 03 '22 at 09:29
  • That's the message. You get the size of the pointer, not the size of the array it points to. – Dima Feb 03 '22 at 21:39
20

Well there is actually a way to determine the size, but it's not "safe" and will be diferent from compiler to compiler.... so it shouldn't be used at all.

When you do: int* arr = new int[256];

The 256 is irrelevant you will be given 256*sizeof(int) assuming for this case 1024, this value will be stored probably at ( arr - 4 )

So to give you the number of "items"

int* p_iToSize = arr - 4;

printf("Number of items %d", *p_iToSize / sizeof(int));

For every malloc, new, whatever before the continuos memory block that you receive, there is also allocated a space reserved with some information regarding the block of memory you were given.

João Augusto
  • 2,285
  • 24
  • 28
  • 11
    Nonetheless, this actually answers the question. – A. Rex Jan 08 '09 at 20:41
  • 1
    interesting, :) as an extra 2 cents, you could overload "new" and implement memory management however you like, you could have it like joao describes, or store every pointer in a map with its corresponding size...in short there are many crazy ways it could be achieved, but i wouldn't use them :p – chrispepper1989 May 23 '13 at 12:53
  • what about char array ? char * arr = new char[100]; – Jai Mar 04 '14 at 07:58
19

No, there is no way to do that in Standard C++.

There is no really good reason why not that I'm aware of. Probably, the size was considered an implementation detail, and best not exposed. Note that when you say malloc(1000), there is no guarantee that the block returned is 1000 bytes --- only that it's at least 1000 bytes. Most likely it's about 1020 (1K minus 4 bytes for overhead). In that case, the "1020" size is the important one for the run-time library to remember. And of course, that would change between implementations.

Which is why the Standards committee added std:vector<>, which does keep track of it exact size.

James Curran
  • 101,701
  • 37
  • 181
  • 258
  • 4
    One thing to note is that new[] does store the number of requested items as well, in order to call the correct number of constructors and destructors for the array. Where this is stored is again implementation specific. Reason to not include a way to get it is beyond my ken. – workmad3 Oct 13 '08 at 15:05
  • 1
    I think the "good reason" is that arrays aren't objects at all. An array is just a raw block of memory. The size is memory-management data, not object data. You could write an Array class that kept track of the memory and the size, but you could just use std::vector and not worry about it. – Herms Oct 13 '08 at 15:09
  • 3
    Aha... Of course. An int* couldn't know if the array it was pointing to was a new'd array or a local array or some spot in the middle of the array. – James Curran Oct 13 '08 at 15:13
  • 2
    @Herms: std::string[10] is definitely not raw memory, but it is an array. – MSalters Oct 14 '08 at 07:31
  • 1
    workmad3, possibly only for items with a nontrivial destructor and for types with a user-defined operator delete which wants the size to be known. for anything else, it suffices not to store the number – Johannes Schaub - litb Jan 07 '09 at 11:49
  • It would be very handy to have a function that returned the allocation size, _especially_ if it was greater than asked for. – Mooing Duck Apr 30 '12 at 06:19
5

Common way to handle this is to either use a vector

int main()
{
   std::vector<int> v(256);
   printf("size of v is %i capacity is %i\n", sizeof(int) * v.size(), sizeof(int) * v.capacity());
}

or predefine the size

const int arrSize = 256;
int main()
{
    int array[arrSize];
    printf("Size of array is %i", sizeof(int) * arrSize);
}
Doug T.
  • 64,223
  • 27
  • 138
  • 202
4

C++ decided to add new to do a typesafe malloc, than new must know both size e numbers of elements for calling ctors, so delete for calling dtors. In the early days you have to actually pass to delete the numbers a objects you passed to new.

string* p = new string[5];
delete[5] p;

However they thought that if use new<type>[] the overhead of a number was small. So they decided that new[n] must remember n and pass it to delete. There are three main ways to implement it.

  1. keep a hash-table of pointer to size
  2. wrote it directly near the vector
  3. do something completely different

Maybe is possible to obtain the size like that:

size_t* p = new size_t[10];
cout << p[-1] << endl;
// Or
cout << p[11] << endl;

Or hell none of those.

Mykelyk
  • 186
  • 2
  • 7
3

Depending on your application, you could create a "sentinel value" at the end of your array.

The sentinel value must have some unique property.

You can then either process the array (or do a linear search) for the sentinel value, counting as you go. Once you reach the sentinel value, you have your array count.

For a simple C string, the terminating \0 is an example of a sentinel value.

bearvarine
  • 661
  • 6
  • 10
3

Some magic:

template <typename T, size_t S>
inline
size_t array_size(const T (&v)[S]) 
{ 
    return S; 
}

And this is how we do it in C++11:

template<typename T, size_t S>
constexpr 
auto array_size(const T (&)[S]) -> size_t
{ 
    return S; 
}
Zingam
  • 4,498
  • 6
  • 28
  • 48
  • Very useful and beautiful solution. Just one thing: I would use size_t as a second template parameter instead. – besworland Aug 10 '16 at 19:06
2

That's because your variable arr is only a pointer. It holds the address of a particular location in memory, without knowing anything about it. You declare it to be int*, which gives the compiler some indication of what to do when you increment the pointer. Other than that, you could be pointing into the beginning or the end of the array or into the stack or into invalid memory. But I agree with you, not being able to call sizeof is very annoying :)

QuantumPete

Peter Kühne
  • 3,224
  • 1
  • 20
  • 24
2

There is no portable way of determining the size of a dynamically-allocated array in C++ given only its pointer. C++ is made to be very flexible and to give power to the user. For example, the standard does not define how memory allocators must work, e.g. by adding a required size header. Not requiring a header allows for a lot more flexibility.

As one example, consider a string implemented as a char * array. It's common to use pointers into the middle of the array to pick out substrings. As an example, see the strtok function in the standard C library. If some header were required to be embedded just before each array, you'd need to trash portions of the array before the substring.

An alternative way to handle the headers would be to have array headers in one block of memory and have them point to the raw array memory elsewhere. In many situations, this would require two pointer lookups for each reference, which would be a big drag on performance. There are ways of overcoming these deficiencies, but they add complexity and reduce implementation flexibility.

The std::vector template is my favorite way of keeping the size of an array bound to the array itself.

C is portable assembly language with a better syntax.

Mr Fooz
  • 109,094
  • 6
  • 73
  • 101
2

Now there is std::array, an efficient compile-time wrapper around a constant-size array:

#include <array>

int main (int argc, char** argv)
{
    std::array<int, 256> arr;
    printf("Size of arr: %ld\n", arr.size());
}

The parameters are <type, #elements>.

You also get a few other niceties, like iterators, empty(), and max_size().

Brent Faust
  • 9,103
  • 6
  • 53
  • 57
1

No, there isn't any way to do this, you have to keep track of how big it is externally. Classes like std::vector do this for you.

Greg Rogers
  • 35,641
  • 17
  • 67
  • 94
1

You can't, fundamentally:

void foo(int* arr);

int arr[100] = {0};

foo(arr+1); // Calls foo with a pointer to 100-1 elements.

A C++ array is nothing more than a collection of objects which are stored in a contiguous memory region. Since there are no holes betweeen them (padding is inside objects), you can find the next element of an array by simply incerementing the pointer. At CPU level, this is a simple adjustment. C++ only inserts a sizeof(element) multiplier.

Note that implementations may choose to implement "fat pointers" which contain array bounds. They'd need to be twice as big, as you'd need to link to some kind of "array bound descriptor". As a side effect, on such implementations you could be able to call delete [] (1+new int[5]);

MSalters
  • 173,980
  • 10
  • 155
  • 350
1

Unfortunately, this is not possible. In C and C++, it is the responsibility of the programmer to remember of the length of an array since array length is not stored anywhere. Delete[] and free() does remember the size of the allocated block but they might allocate more memory than requested so their internal data structures storing the sizes of allocated memory blocks might not give you the exact size of the your array.

Note that C++ STL vectors, which are basically arrays wrapped in a class with some helper functions, do store the length of the array so if you really need this functionality, you could just use vectors.

Tamas Czinege
  • 118,853
  • 40
  • 150
  • 176
1

In general, no. Arrays in C and C++ are just blocks of memory with no bookkeeping information attached. Without storing the length of the array in memory, and adding overhead to do so, it is impossible in the general case.

There is an exception for arrays that are statically allocated. For instance, if you declare: int a[50] then sizeof(a) will work. This is possible because the [50] is part of the static type of the array: it is known to the compiler. sizeof is interpreted at compile time.

However, if you create a pointer: int *p = a, then sizeof(p) will return the size of the pointer as you mention, not the size of the array, because the compiler does not know what p points to.

James Rose
  • 245
  • 1
  • 4
0

When you create array pointers (Create wrapper with template to pointers) you can't but when you create array of object, You can get the size of the array like that:

char* chars=new char[100];
printf("%d",*((int*)chars-1));

The delete[] function need to deconstruct all the objects in it. to do it the new[] keyword puts the number of elements behind all of the array.

The body of array is like that:

int count;
ObjectType* data; //This value is returned when using new[]
MessyCode
  • 333
  • 1
  • 2
  • 9
0

Is there any way to determine the size of a C++ array programmatically? And if not, why?

  1. No, unless you keep track of it yourself.
  2. Because if the compiler doesn't have to tell anyone besides itself about that information it constrains the compiler less. Whether that is desirable or not is up to debate.
MSN
  • 53,214
  • 7
  • 75
  • 105
0

@Dima,

How would the compiler know what the size of p is?

The compiler has to know the size of p; otherwise, it cannot implement delete[]. The compiler doesn't need to tell anyone else how it figures that out.

For a fun way to verify this, compare the pointer returned by operator new[] to the pointer returned by new[].

MSN
  • 53,214
  • 7
  • 75
  • 105
0

the way I do that is by dividing the size of the array by the size of the first element

int intarray[100];
printf ("Size of the array %d\n", (sizeof(intarray) / sizeof(intarray[0]));

It prints 100

  • 3
    Please re-read the question (last line), the author is well aware of that. this is not what is being asked. – Mat Jan 12 '15 at 03:07
0

The compiler can't know that

char *ar = new char[100] 

is an array of 100 characters because it doesn't create an actual array in memory it just creates a pointer to 100 uninitialized bytes in memory.

If you want to know the size of the given array just use std::vector. std::vector is a better array simply.

SMeyers
  • 768
  • 3
  • 9
  • 20
-1

You could just create an extra element of the array and then apply the most unlikely number that will be stored in the array. Then you can determine the number of elements through some function by passing that number.

In the case of declaring and initializing an array at the moment of creation, you can then scan it and then generate a number that does not match any of the elements of the array. But if you then modify one of the elements, you will not know if that element stores the same value as the last element, so you will then have to generate a new number to store in the last element.. Going through all that, you might as well just store the total number of elements at the moment of creation in a variable. And that will probably be the case if you only use the array within a function.

PER
  • 11
  • 4
    That is both vague and impractical, while also interfering with the thing to be measured. not a serious answer. – itsbruce Oct 26 '12 at 15:36
  • 1
    Introducing a magic string ('most unlikely number') into one's data is an anti-pattern for a reason. What happens when that most unlikely number actually occurs due to reasons unforeseen by the programmer? – ouflak Jul 25 '13 at 09:54