这个问题是关于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进行复制。但它安全吗?
答案 0 :(得分:8)
数组不是指针。当/如果将它作为参数传递给函数时,数组的名称“衰减”到指针 - 但是sizeof
是构建在语言中的运算符,而不是函数,因此sizeof(array)
得到数组的实际大小,只要它应用于实际数组(与作为参数传递的数组的名称相反,然后在指针上使用sizeof()
,当它传递给它时,它会衰减到功能
就复制包含数组的类而言,如果它实际上是一个数组,例如:
class X {
int x[10];
};
然后你不需要做任何事情来复制它 - 编译器可以/将生成一个拷贝构造函数来复制数组的内容。如果(并且仅当)你实际上有一个指针,并自己分配空间,你是否需要写一个副本ctor来做一个“深层复制”(即在新对象中分配空间,并复制由指针)。但是,您应该使用std::vector
而不是这样做,而是在内部执行所有操作,因此您不必担心它。
答案 1 :(得分:5)
一次采取这一个:
我想复制一个包含数组的类。
好的,例如:
class Foo
{
int arr[20];
};
如果将使用operator new分配,那么我应该在复制构造函数中手动复制此数组。
好的,现在混乱开始了。在上面的例子中,数组实际上是对象的一部分。如果sizeof(Foo)
是4个字节,int
会给你80个。
另一种方法是使用指向数组的指针,如果数组需要更改大小,这将非常有用:
class Bar
{
int *arr;
};
在这种情况下,sizeof(Bar)
是指针的大小(通常为4或8个字节),复制对象会复制指针。这被称为“浅拷贝”。如果你想要一个“深层复制”,即副本会复制数组的内容而不仅仅是对象,那么你需要一个复制构造函数。
第三种方法是使用vector
,因为小麦推荐:
class Bob
{
std::vector<int> arr;
};
这内部与Bar
案例的工作方式相同,vector
可以调整大小,但vector
模板会为您处理深层复制,因此您不需要需要一个复制构造函数。
如果你需要一个固定大小的数组,我建议使用Foo
的情况,其中大小在编译时是已知的,否则是Bob
的情况。 Bar
案例几乎只是重新发明轮子。
简单的memcpy(...)在这里安全吗?
Foo
的安全。如果您想要浅色副本,请Bar
安全。 Bob
不安全。
故事的寓意是在对象中存储变量就像将其存储在功能块或全局中一样:如果指定数组([N]
而不是*
),则大小最好在编译时确定,然后将存储放在那里。
答案 2 :(得分:2)
注释:您应该只能使用文字或常量来初始化数组。所以编译器没有混淆。
答案:如果在“堆栈方式”中初始化数组 - 即不使用new或malloc。该类将封装数组的大小。数组的基数是指针,因为编译器在内部使用指针算法来解析访问运算符[]
。
如果使用new或malloc,则必须使用用于分配内存的变量作为分配内存大小的确定度量。您可以将memcpy()
与数组基指针一起使用,无论它在何处分配,它仍然是指向数组基本内存位置的指针。
回答2:问题编辑后
是的,使用相同类型且大小相同的const大小的阵列完全安全,但除非您跟踪其类型和大小,否则不要使用动态分配的资源。只要避免使用的方法,就可以使用容器类。
注意:我已经在堆分配内存上的sizeof
运算符上回避了你的观点,因为我无法说出行为是什么。我是老派,我永远不会在动态资源上使用sizeof,因为它取决于运行时机制,谁知道不同编译器供应商包含的运行时机制。
但是,我知道这一点,当一个数组由new(大多数编译器)在C ++中分配时,它会在数组的基础上放置一个整数 - 物理 - 这个整数表示随后数组的大小。这就是你必须使用delete []
运算符的原因,这个运算符与标准delete
不同,因为它会导致编译器吐出一个循环,它会在你的项目上调用析构函数。如果C ++编译器供应商在数组的基础上放置一个整数,那么它们可能会在运行时使用它来为sizeof运算符提取它。 我不知道该标准对此有何看法,我怀疑它是否有效。
TEST: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是一个指针,都是这样的。
答案 3 :(得分:2)
实际上ar
是一个数组,这就是sizeof()找到实际大小的原因。 C / C ++中的数组有一个指针 value (指针表达式或rvalue)。
int ar[10];
int *p;
p = ar; // OK
ar = p; // error
答案 4 :(得分:1)
sizeof(ar)
只能起作用,因为此时编译器可以看到ar
的声明
数组大小不会存储,并且在运行时永远不可用,如果您将ar
传递给函数并执行sizeof(ar)
,那么您将获得4
。所以基本上你的编译器足够聪明,看起来好几行。
sizeof
是一个编译器关键字,它将在编译时中提供大小。因此,编译器必须能够知道或推断出您正在查看的var的大小,以便给出适当的大小。
REEDIT::sizeof
是编译时常量[C99可变长度数组除外],编译器可以在其中添加运行时计算。然而,这是C ++的编译器扩展,因为C99不是C ++标准的一部分。
答案 5 :(得分:1)
你有没有看过STL的vector。它的行为类似于数组,具有可调整的大小,并包含一个返回数组大小的函数“size()”。