Как вы понимаете ошибку: не можете преобразовать из 'int []' в 'int []' - PullRequest
4 голосов
/ 11 февраля 2009

При компиляции следующего кода:

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

компилятор VS2005 сообщает об ошибке C2440: «инициализация»: невозможно преобразовать из «int []» в «int []»

Я понимаю, что на самом деле он пытается привести указатель к массиву, который не будет работать. Но как вы объясните ошибку тому, кто изучает C ++?

Ответы [ 5 ]

12 голосов
/ 11 февраля 2009

Скажите, что есть типы и неполные типы:

struct A;

Является неполным типом структуры с именем A. В то время как

struct A { };

Является полным типом структуры, называемой A. Размер первого еще не известен, а размер второго известен.

Существуют неполные типы классов, такие как приведенная выше структура. Но есть и неполные типы массивов:

typedef int A[];

Это неполный тип массива с именем A. Его размер еще не известен. Вы не можете создать массив из него, потому что компилятор не знает, насколько большой массив. Но вы можете использовать для создания массива, только , если вы сразу его инициализируете:

A SomeArray = { 1, 2, 3 };

Теперь компилятор знает, что массив является массивом с тремя элементами. Если вы попытаетесь инициализировать массив указателем, компилятор не будет более умным, чем раньше, и откажется, потому что это не даст ему размер создаваемого массива.

8 голосов
/ 11 февраля 2009

Пытаясь сделать сообщение об ошибке более полезным, компилятор на самом деле запутывает вещи. Даже если параметр Numbers объявлен как массив, C / C ++ фактически не (не может) передавать массив - параметр Numbers на самом деле является указателем.

Так что ошибка действительно должна сказать "cannot convert from 'int *' to 'int []'"

Но тогда возникнет путаница - "эй, в выражении нет int*, кто-то может сказать.

По этой причине действительно лучше избегать параметров массива - объявляйте их как указатели, так как это то, что вы действительно получаете в любом случае. И объяснение тому, кто изучает C / C ++, должно научить их тому, что параметры массива - выдумка - они действительно указатели.

5 голосов
/ 11 февраля 2009

Есть три вещи, которые вы должны объяснить человеку, которому вы пытаетесь помочь:

  1. Массивы нельзя передать по значению функции в C ++. Чтобы сделать то, что вы пытаетесь сделать, вам нужно передать адрес начала массива в DoSomething(), а также размер массива в отдельном int (ну, size_t, но я бы не стал так говорить) аргументе. Вы можете получить адрес начала некоторого массива myArray с выражением &(myArray[0]). Поскольку это обычное дело, C ++ позволяет вам использовать только имя массива - например, myArray - получить адрес своего первого элемента. (Что может быть полезным или запутанным, в зависимости от того, как вы на это смотрите.) Чтобы сделать вещи еще более запутанными, C ++ позволяет вам указать тип массива (например, int Numbers[]) в качестве параметра функции, но тайно он обрабатывает этот параметр, как если бы он был объявлен как указатель (int *Numbers в данном случае) - вы даже можете сделать Numbers += 5 внутри DoSomething(), чтобы он указывал на массив, начинающийся с шестой позиции!

  2. Когда вы объявляете переменную массива, такую ​​как SomeArray в C ++, вы должны либо указать явный размер, либо «список инициализаторов» , который представляет собой список значений, разделенных запятыми, между брекеты. Компилятор не может определить размер массива на основе другого массива, с которым вы пытаетесь его инициализировать, потому что ...

  3. Вы не можете скопировать один массив в другой или инициализировать один массив из другого в C ++. Таким образом, даже если параметр Numbers действительно был массивом (скажем, размера 1000) и не указатель, и вы указали размер SomeArray (опять же, как, скажем, 1000), строка int SomeArray[1000] = Numbers; будет недопустимой.


Чтобы сделать то, что вы хотите сделать в DoSomething(), сначала спросите себя:

  1. Нужно ли менять какие-либо значения в Numbers?
  2. Если это так, хочу ли я, чтобы вызывающий абонент не видел эти изменения?

Если ответом на любой вопрос является «Нет», вам на самом деле не нужно делать копию Numbers, во-первых, просто используйте ее как есть и забудьте о создании SomeArray массив.

Если ответ на оба вопроса «Да», вам нужно будет сделать копию Numbers в SomeArray и поработать над этим. В этом случае вы должны действительно сделать SomeArray C ++ vector<int> вместо другого массива, поскольку это действительно упрощает вещи. (Объясните преимущества векторов по сравнению с ручным динамическим распределением памяти, включая факты, что они могут быть инициализированы из других массивов или векторов, и при необходимости они будут вызывать конструкторы элементов, в отличие от стиля C * memcpy(). )

4 голосов
/ 11 февраля 2009

Когда я пытаюсь что-то объяснить, я всегда стараюсь опускаться до самого низкого уровня и оттуда наращивать. Именно так мне нравится изучать вещи, и я нахожу, что людям удобнее, если вы начнете с основ, которые они знают, и начнете строить оттуда.

В этом случае я бы, вероятно, начал что-то вроде:

Компилятор пытается сделать назначение, потому что вы написали операция присваивания. В C ++ вы не может напрямую назначить массиву, потому что он не имеет встроенного назначения оператор (любого типа, только инициализатор и индексация поддерживается для массивов). Потому что C ++ поддерживает перегруженные операторы для типов, Затем компилятор ищет перегруженный оператор присваивания для тип «назначен», который принимает тип «назначенный из» в качестве аргумента. Так как нет перегруженных оператор для int [], который принимает int [] в качестве аргумента, ошибки компилятора на линия, и ошибки говорят вам почему компилятор не может обработать строку.

Да, это, вероятно, излишне, просто говорить что-то о знании размера, неполных типов и т. Д. Я понимаю, что это также не завершено (например: нет обсуждения назначения инициализатора против обычного назначения и т. Д.). Однако моя цель, как правило, состоит в том, чтобы люди могли сами найти следующий ответ, и для этого обычно требуется продумать процесс мышления для получения ответа.

3 голосов
/ 11 февраля 2009

Возможно, ваш ответ может быть следующим: «Поскольку компилятор не знает, насколько велик массив».

Ваш пример мог бы работать, если бы были явные размеры массива (возможно, с typedef для ясности), и тогда вы можете объяснить указатели, вводя распределение переменных размеров.

...