incorrect Length of Array

Pages: 12
Hi, i having a code to pass in array as argument, but the length returned is 1. This is not match with the array size.

int Getsize(int Array[])
{
int len = sizeof(Array)/sizeof(int);
cout << len << "\n";
}

int main()
{
int X[] = {45, 12, 54, 83, 41, 36};
getsize(X);
}
sizeof depends on type information (only available at compile time) to do its thing. The type information required by sizeof to obtain the proper size of an array is unavailable everywhere except in the function that declared the array. This is because it's technically impossible to pass an array to a function. int Getsize(int Array[]) is merely syntactic sugar for int Getsize(int *Array). While in main() the identifier X refers directly to an array on the stack, in Getsize() the identifier Array refers to a pointer to an array. Pointers are simply integers. They don't have any information about what they point to, other than where it is.

In general, it's impossible to obtain the size of an array. Under specific cases, its possible to obtain the size of arrays on the stack using template magic:
1
2
3
4
5
6
7
template <typename T, unsigned N>
unsigned size(T (&)[N]){
    return N;
}
//...
unsigned x = size(X);
//x == 6 
Again, this function does NOT work in the general case.
Last edited on
It's not impossible helios, you just did it. ;) Also, an array doesn't have to be on the stack to use template magic.

The problem is that C++ came from C, and C did what helios said for the 1st dimension of an array, it would just chuck the size and degrade decays it down to a pointer when passing it around, presumably because passing an array by value is an expensive operation and nobody would want to do it. However, the type would retain all of the other dimensions if any existed.

C++ sorta kinda did a thing where it actually kept the complete type including the size of the 1st dimension, but it would only be accessible if the array was passed as a reference. This was for backward compatibility with C. Since C doesn't have references, this wouldn't break C code compiled under a C++ compiler. Since then, C and C++ have diverged sufficiently that this actually doesn't matter anymore, (C is no longer just a subset of C++) but legacy remains.

It is unfortunate in this case that C++ has C legacy, making things confusing for newbies (and even veterans). FYI, the magic comes from function templates type deduction if you want to look it up. Also, it's usually a better idea that you use a STL container instead of arrays directly as it manages allocation, knows its own size and is just generally safer (you can also pass an STL container by value if you so wish).
Last edited on
It's not impossible
It is impossible to find size of dynamically allocated array.
1
2
3
4
5
6
7
8
9
//size_t getSize(...
int main()
{
    size_t x;
    std::cin >> x;
    int* arr = new int[x];
    x = 0;
    x = getSize(arr);
}

Templates needs to know their parameters in compile time, so you cannot use them here.
Pass by refence will work only if you specify complete type (void f(int (&x)[5]) instead of void f(int (&x)[])), where you already know size of array.

I do not understand, why C++ does not have a method to access size of array. I mean, delete[] does know how many memory it should deallocate, subscript operator knows size and aligment of one element. Why cannot we have access to that as well?
Last edited on
The subscript operator knows the size of the type, which is compile time. It just adds this size*index to the pointer you give it.

It is impossible to find size of dynamically allocated array.

IIRC, you could read it from the beginning of the array yourself if you know how the meta data is organized?
IIRC, you could read it from the beginning of the array yourself if you know how the meta data is organized?
What if it is stored as a hash table?
Do you know how is it stored on consumers compiler? Will it stay the same way in the next compiler version?

C++ really needs some way to get allocated memory (or at least array) size.
Last edited on
No, it doesn't. You're the one who allocated the memory, don't you know how much you requested?
helios wrote:
You're the one who allocated the memory
What if I'm not one?
helios wrote:
No, it doesn't.
1
2
3
4
5
6
//MyCoolLibrary.h
void doStuffWithArray(int arr[])
{
   //How can I know size of arr here
   //and not be relying on user to pass array size?
}
Last edited on
I do not understand, why C++ does not have a method to access size of array. I mean, delete[] does know how many memory it should deallocate, subscript operator knows size and aligment of one element. Why cannot we have access to that as well?

Not all arrays are created with new. If the array is created on the stack or the array is part of another object the size of the array doesn't have to be stored anywhere in memory.
Yes, you have point here, but program still needs to know it's size to poperly deallocate it in destructor/at the end of scope or access another variables stored besides it (for example difference between offsets from object pointer for member array and next member.)
to poperly deallocate it in destructor/at the end of scope
Destructors don't know the size of the memory you pass to them. A::~A() will assume that the actual size of the memory this points to equals sizeof(A). In other words,
1
2
A *p=/*...*/;
delete (char *)p;
is incorrect.

access another variables stored besides it (for example difference between offsets from object pointer for member array and next member.)
This is done with type information.
You could declare the function parameter as pointer to the array.:) For example

int Getsize(int ( *Array )[6])
{
int len = sizeof(*Array)/sizeof(int);
cout << len << "\n";
}

int main()
{
int X[] = {45, 12, 54, 83, 41, 36};
getsize(&X);
}
Simplified it a little :)
1
2
3
4
5
int Getsize(int ( *Array )[6])
{//           v------------^
    int len = 6
    cout << len << "\n";
}
You can also specify it as a reference to an array. And using templates you can extract the array size also like so:
1
2
3
4
5
6
7
8
9
10
11
template<typename T, size_t N>
size_t get_array_size(T(&array)[N])
{
    return N;
}

int main()
{
    int X[] = {45, 12, 54, 83, 41, 36};
    get_array_size(X);
} 


Also, you can keep the array size info of a dynamically allocated array like so:

1
2
3
4
5
6
int main()
{
    auto& X = *new int[1][5]{{45, 12, 54, 83, 41, 36}};
    get_array_size(X);
    delete[](X);
}

helios wrote:
Again, this function does NOT work in the general case.
AdrianH wrote:
It's not impossible helios, you just did it. ;) Also, an array doesn't have to be on the stack to use template magic.


1
2
3
4
5
6
7
8
9
#include <cstdlib>
#include <ctime>

int main()
{
    std::srand(std::time(0)) ;
    unsigned size = std::rand() ;
    int * x = new int[size] ;
}


Let's see the template magic that works in the general case.
I do not understand, why C++ does not have a method to access size of array.

In a sense it does - by supplying container classes in the standard libraries, which manage their own storage and include public methods to query the size.
helios wrote:
Again, this function does NOT work in the general case.
cire wrote:
Let's see the template magic that works in the general case.

I think that I may have interpreted this differently on the first read. So long as the important type information is not lost and the code is valid, it works. In order not to loose the important array type info on a dynamically allocated array, you need to do as I stated, as in:

1
2
3
4
5
6
int main()
{
    auto& X = *new int[1][5]{{45, 12, 54, 83, 41, 36}};
    get_array_size(X);
    delete[](X);
}

The line marked isn't valid:
1
2
3
4
5
6
7
8
9
10
#include <cstdlib>
#include <ctime>

int main()
{
    std::srand(std::time(0)) ;
    unsigned size = std::rand() ;
    new int[size] ;     // this line is valid
    new int[1][size] ;  // even this line is invalid
}

That is because type information needs to be known at compile time. Variable `size` is not a compile time constant, making that line invalid. So for dynamic arrays, you're right, it doesn't work for the general case. It only works in the special case where the type info can be known (and not lost) at compile time.

Still if you want runtime allocation size, it is better to use a vector as it deals with the implementation details for a variable runtime dynamically allocated array and you can get the size of it easily.
Last edited on
The lines marked aren't valid:

Perhaps you can quote the line in the standard that requires one to use a compile time constant when using new (and then, perhaps, you can point out one compiler that is compliant with respect to that line.)

Oops, looks like I misread the compiler output. I was wondering why that wasn't working. :/ Yes, the first one is valid. Only the 2nd one isn't. I've modified my answer to reflect this.
Last edited on
This line is valid: new int[size] ;

This line is not valid: new int[1][size] ;

This, on the other hand, is valid: new int[size][1];
Pages: 12