【问题标题】:Once an array-of-T has decayed into a pointer-to-T, can it ever be made into an array-of-T again?一旦一个 T 数组衰减为一个指向 T 的指针,它还能再变成一个 T 数组吗?
【发布时间】:2011-07-27 01:36:17
【问题描述】:

假设我有一个数组:

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) 无论您是否可以回到数组类型,究竟哪些信息在衰减到指针的过程中被切分和丢失?

我知道指针类型和数组类型是不等价的。我假设数组类型是存储在指针类型中的信息的严格超集。这听起来对吗?

我在其他问题中读到数组类型中的额外信息是:知道数组是否在堆栈上,以及它的大小(它必须以某种方式知道数组的大小,因为它是类型的一部分,对吧?)。数组类型中是否隐藏了其他信息?

【问题讨论】:

  • 我会观察到 int[3] 不是类型,尽管 int[] 是。
  • @Daniel:“3 个整数的数组”绝对不同于“int[]”,而“int[3]”似乎是前者的合理简写。
  • @Daniel R Hicks- int[3] 是一种类型;它是三个ints 的数组类型。当您使用语法int arr[3] 声明变量时,您正在声明一个名为arr 的变量,其类型为int[3]。这就是为什么声明它们的范围内的数组名称与指向数组第一个元素的指针的工作方式不同的原因。 int[3] 类型与 int* 不同。

标签: c++ arrays pointers


【解决方案1】:

我不确定这是否正是您要寻找的,但您可以使用类型转换来取回与原始数组具有相同类型的对象。这个想法是使用鲜为人知的类型指针到数组和引用到数组来恢复信息。例如:

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

我们的想法是,我们将使用*&amp;arr[0] 创建的引用类型转换为类型char (&amp;)[137],即对137 个字符数组的引用。现在引用具有这种类型,sizeof 运算符知道它应该打印 137,因为 137 个字符的数组的大小确实是 137。

但是,这仅在您将类型转换为正确类型时才有效!例如,这是完全合法的:

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

因此您可以恢复信息,但您很容易错误地获取该信息并导致您恢复了错误信息的情况。

同样,我不确定这是否是您要查找的内容,但它表明您确实可以使用强制转换来取回数组大小信息。

【讨论】:

  • 但是你不是只是告诉演员你想听什么吗?另外,我希望它适用于 &*a 的组合(导致类型从数组类型更改为指针类型),而不是组合 *& (保持数组类型不变)。
  • @Jimmy- 你说得对——这只是告诉数组它的大小是多少,因此“我不确定你在找什么”。对于&amp;*a 的情况,您可以使用转换(T (*)[size])&amp;*a 来告诉数组它的原始大小,顺便说一句。我很确定一般来说这是无法做到的,一旦数组大小消失,它就消失了,但如果你对大小应该有特殊了解,我认为我所拥有的是正确的。
  • 这很好。当我尝试: cout) [3]) &*a ).name()) [3]) &*a ).name()
  • @Jimmy- 类型转换将指针转换为指向原始数组的指针。然后您需要取消引用它以返回原始数组:cout &lt;&lt; typeid( *(int (*) [3]) &amp;*a ).name()&lt;&lt;endl;
  • 这很有道理。我取'a',我尊重它并获得一个int,然后我取它的地址并获得一个指向int的指针,然后我将它转换为一个指向数组的指针(我必须告诉它大小),然后我取消引用它并获取我的整数数组。杰出的!所以,如果我使用了“正确”的大小,那么一切看起来都很酷,但是我是否丢失了原始数组类型中包含的任何信息?
【解决方案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】:

    不知道你在说什么,但一旦你收到任何东西的&amp;,你所得到的只是一个地址。并且简单的非对象数据结构不包含描述符,因此在给定地址的情况下,无法获取任何地址的更多信息。甚至它地址的事实,例如,char 也仅由指针类型传达——如果你转换为 void*,那么该信息就会丢失(在它曾经存在的范围内)。

    我知道指针类型和数组类型是不等价的。我假设数组类型是存储在指针类型中的信息的严格超集。这听起来对吗?

    信息没有“存储”在一个类型中。信息(如果它在运行时存在)存储在变量中。在 C/C++ 中,非对象没有动态类型,因此不仅信息没有“存储”在类型中,而且类型信息也没有“存储”在任何地方。类型信息纯粹是一个编译时概念,它在程序中从一个点“流动”到另一个点,纯粹是编译器静态分析的结果。也就是说,这是一个方便的小说。

    【讨论】:

    • 我相信我明白你的解释。我所说的“类型信息”只是指导编译器正确运行的规范。所以数组类型中的“额外信息”只是在编译时完成的额外检查(即指针类型不一定要进行的编译时验证)?
    • 没有运行时类型信息,除非您为多态类启用它,或者除非您首先计算用于实现多态性的东西。但是,编译时类型信息控制的不仅仅是“额外检查”;数组不是指针。 int x[3],从概念上讲,在堆栈上为 3 个整数的块分配内存,其地址可以按需计算(衰减过程); int* x,概念上在堆栈上为地址分配内存,而不指定它的地址 of
    • 吉米,我想你明白了。在大多数机器上,甚至没有办法判断一组位是否是指针,而不是整数、字符组或其他东西。有几台机器(IBM iSeries 是唯一仍在生产的机器,我相信)是“基于功能的”,它们有办法将指针与其他数据区分开来(此外,它们还有一些关于类型的运行时线索处理的数据),但这些是绝对的例外。
    猜你喜欢
    • 2021-10-17
    • 1970-01-01
    • 1970-01-01
    • 2014-03-21
    • 2013-07-27
    • 2013-11-04
    • 1970-01-01
    • 2017-08-23
    • 2011-11-14
    相关资源
    最近更新 更多