【问题标题】:Why we can directly copy pointers, but we can not directly copy arrays in c++为什么我们可以直接复制指针,但是我们不能在c++中直接复制数组
【发布时间】:2012-10-15 13:34:00
【问题描述】:

定理说我们不能将一个数组初始化为另一个数组的副本。 但是我们可以将指针初始化为指向另一个数组的第一个元素的指针的副本:

int a[] = {0, 1, 2}; 
int a2[] = a;       //error
int *a3 = a;        //OK

为什么int a2[] = a; 是错误的?

【问题讨论】:

  • 因为:数组是 NOT 指针!
  • 长话短说,使用std::array 和复制初始化等。
  • @Als 这本身并不能解释为什么没有(简单的,基于赋值运算符的)语法来复制初始化数组。
  • @jogojapan:如果我的评论不是答案,Q 应该是:“为什么数组是不可修改的左值?” 但 Q 是 “为什么我们可以复制指针,但我们不能复制c++中的数组”,答案在我的评论中(因为数组不是指针)。

标签: c++


【解决方案1】:

在 C++ 中,不能将数组分配给另一个数组对象或从其初始化,因为它们不能在 C 中,而且由于不再真正相关的历史原因,它们不能在 C 中。

在早期的 proto-C 中,像 int a[] = {0}; int b[] = {0}; a = b; 这样的赋值是应该将数组 b 的内容复制到 a,还是重新设置名称 a 以引用 @ 987654325@。与初始化类似,a 应该是 b 的副本还是别名。这种模糊性已经存在了 40 年:很快就清楚,如果允许它,那么 在 C(和 C++)中的合理含义是它应该复制,但 C 中的数组是从未制成“正确”的值类型。

这不可能没有技术原因,例如,您可以分配具有数组作为数据成员的结构类型。该标准根本没有将您的代码定义为正确的 C++。

指针的行为与此没有直接关系。初始化指针(指向数组的第一个元素)与初始化数组及其内容是不同的操作,并且该语言允许在 RHS 上进行不同的操作。

【讨论】:

    【解决方案2】:

    数组不是指针,所以你不能期望像指针一样使用它们而不会遇到麻烦。访问https://blogs.oracle.com/ksplice/entry/the_ksplice_pointer_challenge,参加有助于您了解差异的活动。

    如果将数组包装在结构中,则可以将数组初始化为另一个数组的副本。

    struct myarray {
      int a[3];
    };
    

    如果你的编译器允许 GNU C++ 风格的指定初始化器:

    myarray a = {a: {0, 1, 2}};
    

    这会复制结构体,包括 a 的数组到 a2 的数组。

    myarray a2 = a;
    

    数组将位于内存中的不同位置:

    bool is_different_array = a2.a != a.a; // true
    

    【讨论】:

      【解决方案3】:

      数组不仅仅是一个指针,在您的示例中,数组a[] 表示已在内存中保留了 3 个整数大小的连续内存地址。如果您要尝试“复制”该数组,它还必须保留 3 个整数大小的连续内存地址并将0 1 2 复制到它们。原语的赋值运算符不应该做这么多工作。

      然而,当您创建一个指针时,您只是命名a[] 的位置。

      【讨论】:

      • “原语的赋值运算符不会做这么多工作”——我认为这不太相关,因为数组 不是 原语类型(它们是聚合体)。不过,这种思路可能有助于解释为什么没有人费心在 C 中使数组可赋值。尽管需要尽可能多的复制,但结构是可分配的,因此数组留在中间。
      【解决方案4】:

      可以将一个数组分配给另一个数组——但只能通过将第一个数组的底层数据复制到第二个数组(例如,使用循环)。您问题中的代码行:

      int *a3 = a;
      

      只需将名为a3 的指针分配给指向数组a 的第一个元素的地址,但它不会更改这两个变量所引用的基础数据(在本例中为整数)。

      另外,请查看this 以了解数组和指针之间的关系。

      【讨论】:

      • a 不是指针。它衰减到指向第一个元素的指针,这是分配给a3 的内容。停止这种“数组是指针”的疯狂。
      【解决方案5】:

      让我们以不同的方式来看待它。数组适用于 C/C++ 中的静态分配。所以我们必须确保编译器能够在编译时自己计算出数组的大小,这是数组的强制性条件,因为有很多该死的原因。因此,每当编译器遇到 '[]' 表示法时,它都希望可以从代码本身中计算出内存大小(需要在运行时分配)。

      当我们在 C/C++ 中定义一个数组时,会分配数组的内存(当我说数组时,我指的是要存储数组元素的单元格)。现在当你说喜欢时

      int a[] = {3,4,5,6};
      

      它将分配一些内存,例如 16 个字节用于 4 个元素,这可以由编译器在编译时计算出来。

      现在当你说

      int a2[] = a;
      

      编译器看到数组符号“[]”,但无法确定需要为数组单元分配多少内存,这会导致错误。

      当我们谈论一个数组时,它总是关于由一个常量指针指向的被取消引用的单元格,而当我们谈论一个指针时,它可以不引用任何东西而存在,这就是所谓的悬空指针。希望对您有所帮助。

      【讨论】:

      • 您对编译器如何工作的想法有些不正确。第二个 sn-p 中的a 的类型是int[4],所以编译器没有理由不能推断出绑定a2 的数组。此外,即使您提供了明确的界限,也无法复制数组,因此您错过了问题的重点。
      • @avakar 同意。我试图说明的一点是,当我们说 int a1[] = a 时,我们不能明确指定数组的位置,这由编译器决定。如果我错了,请纠正我
      【解决方案6】:

      如果你这样做

      int *a3 = a; 
      

      你不复制值,只是内存地址,称为指针

      如果你不想复制一个数组的值,你必须定义一个复制构造函数。

      http://en.wikipedia.org/wiki/Copy_constructor

      【讨论】:

        【解决方案7】:

        当你要声明一个将被分配到堆栈上的数组时,编译器需要知道元素的数量,这就是你必须声明的原因:

        int a[] = {x,y,etc..}; 
        

        int a[NUMELEMENTS];
        

        虽然可以接受指向现有数组的指针,但不能从指向现有数组的指针创建新的堆栈数组,这正是int a2[]= a; 试图做的事情。编译器不知道要为 a2 分配多少元素。

        通常,将一个数组复制到另一个数组的最佳方法是使用std::copy

        【讨论】:

        • 数组的大小在编译时确定。所以编译器从根本上知道它有多大(这就是sizeof a 起作用的原因)。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-06-02
        • 2019-02-16
        • 2021-07-13
        • 1970-01-01
        • 2021-06-02
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多