4

编译以下代码时:

void DoSomething(int Numbers[])
{
    int SomeArray[] = Numbers;
}

VS2005 编译器报错 C2440: 'initializing' : cannot convert from 'int []' to 'int []'

我知道它实际上是在尝试将指针投射到一个不起作用的数组。但是你如何向学习 C++ 的人解释这个错误呢?

4

5 回答 5

12

说有类型和不完整类型:

struct A;

是结构体的不完整类型,称为 A。而

struct A { };

是一个名为 A 的结构的完整类型。第一个的大小尚不清楚,而第二个的大小是已知的。

有像上面的结构这样的不完整的类类型。但也有不完整的数组类型:

typedef int A[];

那是一个不完整的数组类型,称为 A。它的大小尚不清楚。您不能从中创建数组,因为编译器不知道数组有多大。但是您可以使用它来创建一个数组,前提是您立即对其进行初始化:

A SomeArray = { 1, 2, 3 };

现在,编译器知道该数组是一个具有 3 个元素的 int 数组。如果您尝试使用指针初始化数组,编译器将不会比以前更聪明,并且会拒绝,因为这不会给它要创建的数组的大小。

于 2009-02-11T07:06:14.283 回答
8

在试图使错误消息更有帮助时,编译器实际上是在混淆事情。即使Numbers参数被声明为一个数组,C/C++ 并不(不能)实际上传递一个数组——Numbers参数实际上是一个指针。

所以错误真的应该说"cannot convert from 'int *' to 'int []'"

但随后会有混乱——“嘿,int*表达中没有涉及”,有人可能会说。

出于这个原因,最好避免使用数组参数——将它们声明为指针,因为无论如何这就是你真正得到的。对学习 C/C++ 的人的解释应该让他们明白数组参数是虚构的——它们实际上是指针。

于 2009-02-11T09:12:14.933 回答
6

您需要向您要帮助的人解释三件事:

  1. 数组不能按值传递给 C++ 中的函数。 要执行您想做的事情,您需要将数组的起始地址传递给DoSomething(),以及在单独的int(好吧size_t,但我不会这么说)参数中传递数组的大小。myArray您可以使用表达式获取某个数组的开始地址&(myArray[0])。由于这是很常见的事情,C++ 允许您只使用数组的名称——例如myArray——来获取其第一个元素的地址。(这可能是有用的还是令人困惑的,这取决于你看它的方式。)为了让事情变得更加混乱,C++ 允许你指定一个数组类型(例如int Numbers[]) 作为函数的参数,但它暗中将该参数视为声明为指针(int *Numbers在本例中)——您甚至可以Numbers += 5在内部进行操作DoSomething()以使其指向从第六个位置开始的数组!

  2. 当你SomeArray在 C++ 中声明一个数组变量时,你必须要么提供一个明确的大小,要么提供一个“initialiser list”,它是一个逗号分隔的大括号之间的值列表。编译器不可能根据您尝试初始化它的另一个数组来推断数组的大小,因为...

  3. 您不能将一个数组复制到另一个数组中,或者在 C++ 中从另一个数组初始化一个数组。 因此,即使参数Numbers实际上是一个数组(例如大小为 1000)而不是指针,并且您指定了大小SomeArray(再次说为 1000),该行int SomeArray[1000] = Numbers;也是非法的。


要做你想做的事DoSomething(),首先问问自己:

  1. 我需要更改 中的任何值Numbers吗?
  2. 如果是这样,我是否要阻止调用者看到这些更改?

如果任何一个问题的答案都是“否”,那么您实际上不需要首先制作副本Numbers——只需按原样使用它,而无需制作单独的SomeArray数组。

If the answer to both questions is "Yes", you will need to make a copy of Numbers in SomeArray and work on that instead. In this case, you should really make SomeArray a C++ vector<int> instead of another array, as this really simplifies things. (Explain the benefits of vectors over manual dynamic memory allocation, including the facts that they can be initialised from other arrays or vectors, and they will call element constructors when necessary, unlike a C-style memcpy().)

于 2009-02-11T09:46:39.313 回答
4

当我试图解释某事时,我总是试图降低到最低水平,然后从那里建立起来。这就是我喜欢学习的方式,我发现如果你从他们所知道的基础开始,然后从那里开始,人们会更自在。

在这种情况下,我可能会从以下内容开始:

编译器正在尝试进行赋值,因为您编写了一个赋值操作。在 C++ 中,您不能直接分配给数组,因为它没有内置的赋值运算符(任何类型的,数组只支持初始化器和索引)。因为 C++ 支持类型的重载运算符,所以编译器会为“assigned-to”类型寻找一个重载的赋值运算符,该类型将“assigned-from”类型作为其参数。由于 int[] 也没有将 int[] 作为参数的重载运算符,因此编译器在该行出错,并且错误告诉您为什么编译器无法处理该行。

是的,这可能有点过头了,而只是说一些关于大小、不完整类型等的知识。我意识到它也不完整(例如:没有讨论初始化程序分配与正常分配等)。但是,我的目标通常是让人们自己找到下一个答案,为此,您通常需要列出得出答案的思考过程。

于 2009-02-11T07:35:12.780 回答
3

也许你的答案可能是,“因为编译器不知道数组有多大。”

如果有明确的数组大小(为了清楚起见,可能使用 typedef),您的示例可能会起作用,然后您可以在引入可变大小分配时解释指针。

于 2009-02-11T07:00:12.257 回答