Как только массив-т упал в указатель-на-т, может ли он когда-либо снова превратиться в массив-Т?

Итак, скажем, у меня есть массив:

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

Теперь, если я должен проверить тип "a" , на моей машине я получаю:

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

Теперь, если я беру адрес "a" , а затем разыскиваю этот адрес, тип не изменяется (что мне действительно нравится, потому что в моем сознании "обращение к адресу" и "разыменование" являются обратными операциями):

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

Однако если я сначала разыщу "a" , а затем возьму адрес этого, тип изменится (я признаю, что мне тяжело не нравится, потому что когда я разыменовал массив, я должен получить int, а когда я возьмите адрес этого int, я должен получить указатель на int, и, оказывается, я это делаю):

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

Итак, вот мои два вопроса:

1) Как только тип массива распался на тип указателя, все равно нужно вернуть его к типу массива?

Я пробовал очевидную стратегию кастинга как-вы-просто-не-забота:

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

Есть ли другой бросок, который будет работать? Или этот тип преобразования строго запрещен?

2) Можете ли вы когда-нибудь вернуться к типу массива, точно, какая информация нарезана и потеряна в процессе разложения на указатель?

Я понимаю, что тип указателя и тип массива не эквивалентны. Я предполагаю, что тип массива является строгим надмножеством информации, хранящейся в типе указателя. Правильно ли это звучит?

Я читал в других вопросах, что дополнительная информация в массиве-типа: знание того, находится ли массив в стеке, а также его размер (он должен каким-то образом знать размер массива, поскольку он часть типа, правильно?). Есть ли какая-либо другая информация, скрытая в массиве?

Ответы

Ответ 1

Я не уверен, что это то, что вы ищете, но вы можете использовать typecasting для возврата объекта с тем же типом, что и исходный массив. Идея состоит в том, чтобы использовать малоизвестные типы pointer-to-array и reference-to-array для восстановления информации. Например:

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

Идея заключается в том, что мы создаем ссылку, созданную с помощью *&arr[0], чтобы напечатать char (&)[137], ссылку на массив из 137 символов. Теперь, когда эта ссылка имеет этот тип, оператор sizeof знает, что он должен печатать 137, так как размер массива из 137 символов действительно равен 137.

Однако, это работает, только если вы выберете правильный тип! Например, это совершенно легально:

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

Таким образом, вы можете восстановить информацию, но вы можете легко получить эту информацию и привести к случаю, когда вы восстановили неверную информацию.

Опять же, я не уверен, что это то, что вы искали, но это показывает, что вы действительно можете использовать кастинг, чтобы получить информацию о размере массива.

Ответ 2

Это не полный ответ на ваши вопросы, но вот что я могу предложить -

Я не думаю, что есть способ вернуться к типу массива, когда он распался на указатель.

Исходный тип массива - T [N], где N - количество элементов в массиве. Как только он заглох на указатель, информация о размере теряется навсегда. Как показывает @templatetypdef answer, вы можете вернуть указатель обратно к типу массива, но затем он продолжает доказывать, что путем кастинга вы просто сообщаете компилятору, что размер есть, у него фактически нет способа вывести эту информацию из указателя.

Чтобы сохранить исходную информацию о типе, вам нужно передать массив по ссылке вместо указателя.

#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;
}

Вышеуказанный результат

A4_i
Pi
A4_i
A42_i

Ответ 3

Не уверен, что вы получаете, но как только вы берете & ничего, все, что у вас есть, это адрес. А простые структуры объектов, не относящихся к объектам, не содержат дескрипторов, поэтому нет способа, с помощью адреса, получить дополнительную информацию по всем адресам. Даже тот факт, что он адресован, скажем, char, передается только по типу указателя - если вы отбрасываете на void*, тогда эта информация теряется (в той мере, в которой она когда-либо существовала).

Я понимаю, что тип указателя и тип массива не эквивалентны. Я предполагаю, что тип массива является строгим надмножеством информации, хранящейся в типе указателя. Правильно ли это звучит?

Информация не хранится в типе. Информация (если она существует во время выполнения) хранится в переменных. В C/С++ нет динамической типизации для не-объектов, поэтому не только информация не "сохраняется" в типах, но информация типа не "сохраняется" нигде. Информация типа является чисто концепцией времени компиляции, и она "течет" из одной точки в другую в программе, как результат статического анализа компилятором. То есть, это удобная выдумка.