1

这个问题是关于 C++

我一直认为C++中的数组名只是一个指针,所以我认为

int ar[10];
cout << sizeof(ar);

会给我一样的sizeof(int *)。但它给出了 40 - 所以它是整个数组的实际大小。当数组大小由变量给出时,它也给出 40:

int n = 10;
int ar[n]

我想复制一个包含数组的类。如果它将与运算符一起分配,new那么我应该在复制构造函数中手动复制该数组。但是恒定大小的数组呢?类是否仅包含指向数组的指针,还是包含整个数组?这里简单memcpy(...)安全吗?

编辑:另一个例子:

int n;
cin >> n;
int ar[n];
cout << sizeof(ar);

它打印n * 4。我在 Linux 上使用 g++。

我什至试过这个:

class Test {
public:
    int ar[4];
};

Test a, b, c;
a.ar[0] = 10;
b = a;
memcpy(&c, &a, sizeof(a));
a.ar[0] = 20;
cout << "A: " << a.ar[0] << endl;
cout << "B: " << b.ar[0] << endl;
cout << "C: " << c.ar[0] << endl;

它给出了:

A: 20
B: 10
C: 10

所以数组存储为类的一部分,可以用 memcpy 复制。但它安全吗?

4

6 回答 6

8

数组不是指针。当/如果您将数组的名称作为参数传递给函数时,数组的名称“衰减”为指针 - 但sizeof它是语言内置的运算符,而不是函数,因此sizeof(array)只要数组的实际大小是应用于实际数组(而不是作为参数传递的数组的名称,然后使用sizeof()它在传递给函数时衰减到的指针。

至于复制一个包含数组的类,如果它真的是一个数组,比如:

class X { 
    int x[10];
};

然后你不需要做任何事情来复制它——编译器可以/将生成一个复制构造函数来复制数组的内容。如果(且仅当)您实际上有一个指针,并自己分配空间,您是否需要编写一个复制 ctor 来执行“深度复制”(即在新对象中分配空间,并复制指向 TO 的数据指针)。但是,您通常不应该这样做,而应该使用 an std::vector,它会在内部完成所有这些操作,因此您不必担心。

于 2009-12-09T15:27:10.760 回答
5

一次服用这些:

我想复制一个包含数组的类。

好的,例如:

class Foo
{
    int arr[20];
};

如果它将与 operator new 一起分配,那么我应该在复制构造函数中手动复制该数组。

好的,现在混乱开始了。在上面的例子中,数组实际上是对象的一部分。 如果是 4 个字节sizeof(Foo),会给你 80 。int

另一种方法是有一个指向数组的指针,如果数组需要更改大小,这很有用:

class Bar
{
    int *arr;
};

在那种情况下,sizeof(Bar)是指针的大小(通常是 4 或 8 个字节),并且复制对象会复制指针。这称为“浅拷贝”。如果你想要一个“深拷贝”,即一个拷贝会复制数组的内容而不仅仅是对象,那么你需要一个拷贝构造函数。

第三种选择是使用vector,正如wheaties 建议的那样:

class Bob
{
    std::vector<int> arr;
};

这在内部与Bar案例相同,并且vector可以调整大小,但vector模板会为您处理深层副本,因此您不需要复制构造函数。

如果您需要一个固定大小的数组,我会推荐这种Foo情况,其中大小在编译时是已知的,Bob否则就是这种情况。这个Bar案子几乎只是在重新发明轮子。

简单的 memcpy(...) 在这里安全吗?

Foo. Bar如果你想要一个浅拷贝是安全的。不安全Bob

这个故事的寓意是,将变量存储在对象中就像将其存储在函数块或全局中一样:如果您指定一个数组([N]而不是*),则最好在编译时确定大小,然后您将获得存储放在那里。

于 2009-12-09T15:32:53.570 回答
2

注释:在标准 C (C89)中,您应该只能使用文字或 const 初始化数组。所以编译器不会混淆。

回答:如果您以“堆栈方式”初始化数组——即,不使用new 或malloc。该类将封装数组的大小。数组的基础是指针,因为编译器在内部使用指针算法来解析访问运算符[]

如果使用 new 或 malloc,则必须使用用于分配内存的变量作为分配内存大小的最终度量。您可以memcpy()将数组基指针与数组基指针一起使用,因为无论分配在何处,它仍然是指向数组基内存位置的指针。

答案 2:问题编辑后

是的,使用相同类型和相同大小的 const 大小的数组执行此操作是完全安全的,但不要使用动态分配的资源执行此操作,除非您正在跟踪它们的类型和大小。只需避免您采用的方法并使用容器类即可。

注意:我已经回避了您sizeof对堆分配内存操作员的看法,因为我无法说出行为是什么。我是老派,我永远不会在动态资源上使用 sizeof,因为它取决于运行时机器,谁知道不同编译器供应商包括哪些运行时机器。

但是,我知道这一点,当一个数组在 C++ 中由 new (在大多数编译器上)分配时,它会在数组的基础上放置一个整数 - 物理上 - 。这个整数表示后面的数组的大小。这就是为什么您必须使用delete []运算符的原因,此运算符与标准运算符不同,delete因为它会导致编译器吐出一个循环,该循环迭代地调用您项目上的析构函数。如果 C++ 编译器供应商在数组的底部放置一个整数,那么他们可能会在运行时使用它来为 sizeof 运算符提取它。我不知道该标准对此有何规定,我怀疑它是否以这种方式工作。

测试:Visual Studio 2008

#include "stdafx.h"
#include <iostream>


int _tmain(int argc, _TCHAR* argv[])
{
    int x;
    int *y=new int[5];
    int z[5];
    std::cout << sizeof(x);
    std::cout << " " <<  sizeof(y);
    std::cout << " " << sizeof(z);
    return 0;
}

输出:

4 4 20

如果数组是硬编码的,则只能在运行时确定数组的大小——即,就像 z 的情况一样。x 是一个 int 并且 y 是一个指针,两者的行为都是如此。

于 2009-12-09T15:10:51.140 回答
2

实际上ar一个数组,这就是 sizeof() 找到实际大小的原因。C/C++ 中的数组有一个指针(指针表达式或右值)。

int ar[10];
int *p;
p = ar;  // OK
ar = p;  // error 
于 2009-12-09T15:16:50.443 回答
1

sizeof(ar)仅有效,因为此时ar编译器可以看到 of 的声明。
数组大小不会存储,并且在运行时永远不可用,如果你传递ar给一个函数并在sizeof(ar)那里你会得到4. 所以基本上你的编译器足够聪明,可以看上面几行。

sizeof是一个编译器关键字,它将在编译时传递一个大小。因此编译器必须能够知道或推断出您正在查看的 var 的大小以提供适当的大小。

REEDIT: :sizeof是一个编译时常量 [C99 可变长度数组除外],编译器可以在其中添加运行时计算。然而,这是 C++ 的编译器扩展,因为 C99 不是 C++ 标准的一部分。

于 2009-12-09T15:10:39.670 回答
1

你有没有研究过 STL 的vector。它的行为类似于数组,具有可调整的大小,并包含一个返回数组大小的函数“size()”。

于 2009-12-09T15:18:36.937 回答