Я понимаю, что поэлементное присваивание массивов не поддерживается, поэтому следующее не будет работать:
int num1[3] = {1,2,3};
int num2[3];
num2 = num1; // "error: invalid array assignment"
Я просто принял это как факт, полагая, что цель языка - предоставить открытый фреймворк и позволить пользователю решать, как реализовать что-то вроде копирования массива.
Однако следующее работает:
struct myStruct { int num[3]; };
struct myStruct struct1 = {{1,2,3}};
struct myStruct struct2;
struct2 = struct1;
Массив num[3]
присваивается по элементам из экземпляра в struct1
экземпляре в struct2
.
Почему для структур поддерживается поэлементное присвоение массивов, но не в целом?
edit : Комментарий Роджера Пейта в потоке std :: string в структуре - Проблемы с копированием / назначением? кажется, указывает на общее направление ответа, но я не знаю достаточно, чтобы подтвердить это сам.
редактировать 2 : много отличных ответов. Я выбрал Лютера Блиссетта , потому что меня больше всего интересовало философское или историческое обоснование такого поведения, но ссылка Джеймса Макнеллиса на соответствующую документацию по спецификациям также была полезной.
memcpy()
или подобное.boost::array
( boost.org/doc/libs/release/doc/html/array.html ) и теперьstd::array
( en.cppreference.com/w/cpp/container/array ) - это STL-совместимые альтернативы грязные старые массивы C. Они поддерживают копирование-назначение.Ответы:
Вот мой взгляд на это:
Развитие языка C дает некоторое представление об эволюции типа массива в C:
Попробую обрисовать массив:
Предшественники C B и BCPL не имели отдельного типа массива, такого объявления как:
auto V[10] (B) or let V = vec 10 (BCPL)
объявит V как (нетипизированный) указатель, который инициализируется, чтобы указывать на неиспользуемую область из 10 «слов» памяти. B уже использовался
*
для разыменования указателей и имел[]
сокращенную нотацию, что*(V+i)
означаетV[i]
, как в C / C ++ сегодня. ОднакоV
это не массив, это все же указатель, который должен указывать на некоторую память. Это вызвало проблемы, когда Деннис Ричи попытался расширить B с помощью типов структур. Он хотел, чтобы массивы были частью структур, как в C сегодня:struct { int inumber; char name[14]; };
Но с концепцией B, BCPL массивов как указателей, это потребовало бы, чтобы
name
поле содержало указатель, который должен был быть инициализирован во время выполнения в область памяти размером 14 байтов внутри структуры. Проблема инициализации / компоновки была в конечном итоге решена путем особого обращения с массивами: компилятор отслеживал расположение массивов в структурах, в стеке и т. Д., Фактически не требуя материализации указателя на данные, за исключением выражений, которые включают массивы. Эта обработка позволила почти всему B-коду продолжать работать и является источником правила «массивы преобразовываются в указатель, если вы посмотрите на них» . Это средство обеспечения совместимости, которое оказалось очень удобным, поскольку позволяло использовать массивы открытого размера и т. Д.И вот моя догадка, почему нельзя присвоить массив: поскольку массивы были указателями в B, вы могли просто написать:
auto V[10]; V=V+5;
перебазировать "массив". Теперь это было бессмысленно, потому что основание переменной массива больше не было lvalue. Таким образом, это задание было запрещено, что помогло отловить несколько программ, которые выполняли этот перебазинг на объявленных массивах.. А потом застряло это понятие: поскольку массивы никогда не создавались для первоклассного использования системы типов C, они в основном рассматривались как особые звери, которые становятся указателями, если вы их используете. И с определенной точки зрения (которая игнорирует тот факт, что C-массивы - это неудачная попытка взлома), запрет присваивания массивов все еще имеет смысл: открытый массив или параметр функции массива обрабатываются как указатель без информации о размере. У компилятора нет информации для генерации им присвоения массива, а присвоение указателя требовалось по соображениям совместимости.
/* Example how array assignment void make things even weirder in C/C++, if we don't want to break existing code. It's actually better to leave things as they are... */ typedef int vec[3]; void f(vec a, vec b) { vec x,y; a=b; // pointer assignment x=y; // NEW! element-wise assignment a=x; // pointer assignment x=a; // NEW! element-wise assignment }
Это не изменилось, когда версия C в 1978 году добавила назначение структур ( http://cm.bell-labs.com/cm/cs/who/dmr/cchanges.pdf ). Несмотря на то, что записи были разными типами в C, было невозможно назначить их в ранних версиях K&R C. Вам приходилось копировать их поэлементно с помощью memcpy, и вы могли передавать на них только указатели в качестве параметров функции. Присвоение (и передача параметров) теперь просто определялось как memcpy необработанной памяти структуры, и, поскольку это не могло сломать существующий код, оно было легко адаптировано. В качестве непреднамеренного побочного эффекта это неявно вводило какое-то присваивание массива, но это происходило где-то внутри структуры, поэтому это не могло действительно создать проблемы с тем, как массивы использовались.
источник
int[10] c;
чтобы lvaluec
вел себя как массив из десяти элементов, а не как указатель на первый элемент массива из десяти элементов. Есть несколько ситуаций, когда полезно иметь возможность создать typedef, который выделяет пространство при использовании для переменной, но передает указатель при использовании в качестве аргумента функции, но невозможность иметь значение типа массива является значительной семантической слабостью на языке.Что касается операторов присваивания, стандарт C ++ говорит следующее (C ++ 03 §5.17 / 1):
Массив не является изменяемым lvalue.
Однако присвоение объекту типа класса определяется специально (§5.17 / 4):
Итак, мы посмотрим, что делает неявно объявленный оператор присваивания копии для класса (§12.8 / 13):
Итак, для объекта типа класса массивы копируются правильно. Обратите внимание: если вы предоставите объявленный пользователем оператор присваивания копии, вы не сможете воспользоваться этим, и вам придется копировать массив поэлементно.
Рассуждения аналогичны в C (C99 §6.5.16 / 2):
И §6.3.2.1 / 1:
В C назначение намного проще, чем в C ++ (§6.5.16.1 / 2):
Для присвоения объектов типа структуры левый и правый операнды должны иметь один и тот же тип, поэтому значение правого операнда просто копируется в левый операнд.
источник
=
требует rvalue на RHS, а массив не может быть rvalue ! Преобразование lvalue-to-rvalue запрещено для массивов, заменено lvalue-to-pointer.static_cast
не лучше в создании rvalue, потому что он определяется в тех же терминах.По этой ссылке: http://www2.research.att.com/~bs/bs_faq2.html есть раздел о назначении массивов:
Две основные проблемы с массивами заключаются в том, что
И я думаю, что это фундаментальное различие между массивами и структурами. Переменная массива - это элемент данных низкого уровня с ограниченным знанием себя. По сути, это кусок памяти и способ индексировать его.
Итак, компилятор не может отличить int a [10] от int b [20].
Однако структуры не обладают такой двусмысленностью.
источник
sizeof(a)
vs.sizeof(b)
или передатьa
вvoid f(int (&)[20]);
.Знаю, все ответившие - знатоки C / C ++. Но я подумал, что это основная причина.
число2 = число1;
Здесь вы пытаетесь изменить базовый адрес массива, что недопустимо.
и, конечно же, struct2 = struct1;
Здесь объект struct1 назначается другому объекту.
источник
num2 = num1
было бы отлично. Элементыnum2
будут иметь то же значение, что и соответствующий элементnum1
.Еще одна причина , не были сделаны дальнейшие усилия по усилению массивов в C, вероятно , что назначение массив не будет , что полезно. Несмотря на то, что это может быть легко достигнуто в C, заключив его в структуру (и адрес структуры можно просто привести к адресу массива или даже к адресу первого элемента массива для дальнейшей обработки), эта функция редко используется. Одна из причин заключается в том, что массивы разных размеров несовместимы, что ограничивает преимущества присваивания или, связанного с этим, перехода к функциям по значению.
Большинство функций с параметрами массива на языках, где массивы являются первоклассными типами, написаны для массивов произвольного размера. Затем функция обычно выполняет итерацию по заданному количеству элементов - информации, которую предоставляет массив. (В C идиома, конечно же, заключается в передаче указателя и отдельного количества элементов.) Функция, которая принимает массив только одного определенного размера, нужна не так часто, поэтому многое упускается. (Это меняется, когда вы можете предоставить компилятору возможность генерировать отдельную функцию для любого размера массива, как в случае с шаблонами C ++; вот почему
std::array
это полезно.)источник