views:

3449

answers:

14

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)).

+2  A: 

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.
+2  A: 

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

QuantumPete
But the system somehow knows the size of the array, or "delete[] arr" wouldn't work.
Kip
Well, the system knows at runtime, but sizeof is a compile-time call.
QuantumPete
delete[] arr will know the size of the array, but not if the array was allocated on the stack.
Alexander
+1  A: 

No, there isnt 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
+12  A: 

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
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
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
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
@Herms: std::string[10] is definitely not raw memory, but it is an array.
MSalters
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
+38  A: 
Dima
Good explanation Dima.
smink
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
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
Fantastic explanation. :)
mahesh
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
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 = doesn't compile? [...]
Johannes Schaub - litb
(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
litb, the reason char const **s = 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 = which won't compile either.
Dima
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
A: 

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
A: 

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.

DrJokepu
+2  A: 

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
A: 

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
A: 

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
+1  A: 

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
Nonetheless, this actually answers the question.
A. Rex
A: 

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

MSN
A: 

@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

MSN
A: 

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.