Как вы понимаете причину ошибки:не удается преобразовать из 'int []' в 'int []'

StackOverflow https://stackoverflow.com/questions/535713

Вопрос

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

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

компилятор VS2005 выдает ошибку C2440:"инициализация" :не удается преобразовать из 'int []' в 'int []'

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

Это было полезно?

Решение

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

  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 a C++ vector<int> вместо другого массива, так как это действительно все упрощает.(Объясните преимущества векторов по сравнению с ручным динамическим распределением памяти, включая факты, что они может могут быть инициализированы из других массивов или векторов, и они будут вызывать конструкторы элементов при необходимости, в отличие от C-стиля memcpy().)

Другие советы

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

struct A;

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

struct A { };

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

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

typedef int A[];

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

A SomeArray = { 1, 2, 3 };

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

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

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

Но тогда возникло бы замешательство - "Эй, там нет int* вовлечен в выражение", - мог бы сказать кто-то.

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

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

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

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

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

Возможно, ваш ответ мог бы быть таким: "Потому что компилятор не знает, насколько велик массив".

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top