Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Once an array-of-T has decayed into a pointer-to-T, can it ever be made into an array-of-T again?

So let us say I have an array:

int a[3] = { 1, 2, 3 };

Now if I were to check the type of 'a', on my machine I get:

cout<<typeid(a).name(); // prints 'A3_i'

Now if I take the address of 'a', then dereference that address, the type does not change (which I really like, because in my mind 'taking the address' and 'dereferencing' are inverse operations):

cout<<typeid(*&a).name(); // also prints 'A3_i'

However if I dereference 'a' first, then take the address of that, the type does change (which I admit I have a hard time not liking, because when I dereferenced the array I should get an int, and when I take the address of that int, I should get a pointer-to-int, and it turns out I do):

cout<<typeid(&*a).name(); // prints 'Pi'

So here are my two questions:

1) Once an array-type has decayed into a pointer-type, is there anyway to get it back to an array-type?

I tried the obvious strategy of casting-like-you-just-don't-care:

cout<<typeid( (int[3]) &*a).name(); // does not compile
// "error: ISO C++ forbids casting to an array type `int [3]'"

Is there another cast that would work? Or it is this type of conversion strictly off-limits?

2) Whether or not you can ever get back to the array-type, exactly what information is sliced and lost in the decay-to-pointer proccess?

I understand that a pointer-type and an array-type are not equivalent. I assume that the array-type is a strict superset of the information stored in the pointer-type. Does this sound right?

I have read in other questions that the extra information in the array-type is: knowledge of whether or not the array is on the stack, and also its size (it must know the size of the array somehow, because it is part of the type, right?). Is there any other information hidden in the array-type?

like image 888
Jimmy Avatar asked Jul 27 '11 01:07

Jimmy


People also ask

What does it mean to decay an array to a pointer?

The loss of type and dimensions of an array is known as decay of an array. This generally occurs when we pass the array into function by value or pointer.

Can you pass an array to a pointer?

A whole array cannot be passed as an argument to a function in C++. You can, however, pass a pointer to an array without an index by specifying the array's name. In C, when we pass an array to a function say fun(), it is always treated as a pointer by fun().

When an array of pointers is passed through a function what actually is passed?

When we pass the array in by its name, we are passing the address of the first array element. So, the expected parameter is a pointer. Example: // This function receives two integer pointers, which can be names of integer arrays.

Why is an array not a pointer?

sizeof(pointer) is always the same, regardless of the number of elements the pointer addresses, or the type of those elements. sizeof(array depends on both the size of the array, and the element type. Arrays cannot have zero length.


2 Answers

I'm not sure if this is quite what you're looking for, but you can use typecasting to get back an object with the same type as the original array. The idea is to use the little-known types pointer-to-array and reference-to-array to recover the information. For example:

char arr[137];
cout << sizeof(arr) << endl; // Prints 137
cout << sizeof(arr[0]) << endl; // Prints 1
cout << sizeof(&arr[0]) << endl; // Prints 4 (on my system)
cout << sizeof(*&arr[0]) << endl; // Prints 1
cout << sizeof((char (&) [137]) *&arr[0]) << endl; // Prints 137

The idea is that we typecast the reference created by using *&arr[0] to type char (&)[137], a reference to an array of 137 characters. Now that the reference has this type, the sizeof operator knows that it should print 137, since the size of an array of 137 characters is indeed 137.

However, this only works if you typecast to the correct type! For example, this is perfectly legal:

char arr[137];
cout << sizeof((char (&) [42]) *&arr[0]) << endl; // Prints 42

So you can recover the information, but you can easily get that information wrong and lead to a case where you've recovered the incorrect information.

Again, I'm not sure if this is what you were looking for, but it shows that you can indeed use casting to get back the array size information.

like image 154
templatetypedef Avatar answered Oct 22 '22 06:10

templatetypedef


This is not a complete answer to your questions, but here's what I have to offer -

I don't think there is a way to get back to an array type once it has decayed to a pointer.

The original array type is T[N], where N is the number of elements in the array. Once it has decayed to a pointer the size information is lost for good. As @templatetypdef's answer shows, you can cast the pointer back to array type, but then he goes on to prove that by casting you're just telling the compiler what the size is, it doesn't actually have a way of deducing that information from the pointer.

To preserve the original type information you'd have to pass the array by reference instead of by pointer.

#include <iostream>
#include <typeinfo>

template<size_t N>
void ByRef( int(&array)[N] )
{
  std::cout << typeid(array).name() << std::endl;
}

void ByPointer( int *array )
{
  std::cout << typeid(array).name() << std::endl;
  std::cout << typeid((int(&)[4])array).name() << std::endl;
  std::cout << typeid((int(&)[42])array).name() << std::endl;
}

int main()
{
  int a[4] = {1,2,3,4};

  ByRef( a );
  ByPointer( a );

  return 0;
}

The output of the above is

A4_i
Pi
A4_i
A42_i
like image 40
Praetorian Avatar answered Oct 22 '22 07:10

Praetorian