【问题标题】:Casting a (pointer to a variable sized array) to (pointer to pointer)将(指向可变大小数组的指针)转换为(指向指针的指针)
【发布时间】:2014-07-22 18:13:00
【问题描述】:

当我将 [pointer to a variable sized array] 转换为 [pointer to pointer] 时,究竟会发生什么?

int ar[r][c];
int **ptr = (int**)ar;  // implicit casting not allowed
ptr[0][0] = 100;

上面的代码给出了运行时错误。

将可变大小的数组转换为指针按预期工作:

int ar[c];
int *ptr = ar;
ptr[0] = 100;

这里ar 衰减为指向第一个元素的指针。

但是当将int(*)[c] 转换为int** 时,内部会发生什么?为什么读取/写入int**变量时会导致运行时错误?

【问题讨论】:

  • 你能在你的帖子中描述你的“运行时错误”吗?
  • 应用程序只是挂起。我猜它会得到一个 SIGSEGV
  • int ar[r][c]; 与带有修改访问器的int ar[r * c]; 大致等价。
  • “究竟发生了什么”——未定义的行为。
  • 演员阵容内部没有什么特别的事情发生。你告诉编译器“相信我,你认为是数组地址的东西实际上是指针的地址”,它相信你。然后你崩溃。因为你撒谎了。

标签: c++ pointers semantics


【解决方案1】:

问题是ptr[0] 或*ptr 应该是一个指针,但它不是。也就是说,ptr[0] 或 *ptr 不包含有效指针。在这个地址有数组 ar 的第一个元素。所以当你使用表达式ptr[0][0] 时你会得到一个运行时错误,一般情况下程序行为是未定义的。

【讨论】:

    【解决方案2】:

    数组不是指针。

    数组是一块连续的数据块,所有相同类型的数据都打包在一起。碰巧的是,如果您有一个指向第一个元素的指针,并且您知道数据的类型,那么您可以使用指针执行许多与数组相同的操作。

    foo[5] 在数组上获得第 5 个元素,在指向第一个元素的指针上也获得第 5 个元素。

    其实你可以将数组转换为foo类型隐式转换为指向第一个元素的指针。

    现在,你正在做的是完全不同的事情。指向第一个元素的指针是指向int[5] 的指针——整个数组。

    假设您有一个长度为 5 的指向int* 的指针数组。它们中的每一个都可以指向不同的int[5],并且您可以将int*[6] 用作二维数组。但是你会注意到这里我们有一个指向int* 的指针数组,而不是int[5] 的数组。由于数组不是指针,所以它们是不同的东西。

    现在,我们可以解决这个问题了。

    template<unsigned...>struct indexes{typedef indexes type;};
    template<unsigned Max, unsigned...Is> struct make_indexes:make_indexes<Max-1, Max-1, Is...>{};
    template<unsigned...Is> struct make_indexes<0, Is...>:indexes<Is...>{};
    template<unsigned Max> using make_indexes_t = typename make_indexes<Max>::type;
    
    template<typename T, unsigned N, unsigned M, unsigned... Is>
    std::array<T*, M> as_array_of_pointers( indexes<Is...>, T(&arr)[M][N] ) {
      return { arr[Is]... };
    };
    template<typename T, unsigned N, unsigned M>
    std::array<T*, M> as_array_of_pointers( T(&arr)[M][N] ) {
      return as_array_of_pointers( make_indexes_t<M>{}, arr );
    }
    

    以上是花哨的C++11写法:

    std::array<int*, 5> arr = { ar[0], ar[1], ar[2] };
    

    现在你可以把你的ar 变成一个指针数组。如果你有一个使用int** 的函数,你可以调用as_array_of_pointers 并获取一个指向第一个元素的显式指针,并依靠临时生命周期来完成这项工作:

    void foo( int** x ) {}
    
    int main() {
      int a[5][3] = {0};
      foo( &(as_array_of_pointers(a)[0]) );
    }
    

    这需要 C++11。您可以改为在 C++03 中手动完成。

    您看到的崩溃(通过未定义的行为)可能是由于将数组的第一个元素重新解释为指向int 的指针,而不是一个或多个int(取决于相对大小指针和int 在您的系统上)。

    live example

    【讨论】:

    • 顺便说一句,我认为最终(tmp)数组的生命周期不够长。
    • @Jarod42 它将持续与它所在的代码行一样长,这足以让foo 运行。 (经验法则是临时对象持续“直到;”,或者如果绑定直接到块的末尾)
    • 我看到您能够将数组数组转换为指针数组。虽然我不明白怎么做(那个代码对我来说太复杂了):-)
    【解决方案3】:

    当您声明 int **ptr 时,它指的是一个 int 指针数组。但是你声明了一个 int 数组的数组。

    这就是语言不为此提供任何隐式转换的原因,因为这两种类型实际上并不相关。

    像你一样操作转换的结果有未定义的行为。

    【讨论】:

    • int **ptr 具有类型 (int**)。 int 指针数组的类型为 int *ptr[c]。它们有点不同
    • 不,它们并没有什么不同,int* ptr[] 衰减为 int**,其实是一回事。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-09-10
    • 1970-01-01
    • 2017-05-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多