Rozumiem, że członkowskie przypisywanie tablic nie jest obsługiwane, więc następujące elementy nie będą działać:
int num1[3] = {1,2,3};
int num2[3];
num2 = num1; // "error: invalid array assignment"
Po prostu zaakceptowałem to jako fakt, stwierdzając, że celem języka jest zapewnienie otwartej struktury i pozwolenie użytkownikowi zdecydować, jak zaimplementować coś, na przykład kopiowanie tablicy.
Jednak działa:
struct myStruct { int num[3]; };
struct myStruct struct1 = {{1,2,3}};
struct myStruct struct2;
struct2 = struct1;
Tablica num[3]
jest przypisywana według elementów członkowskich z jej instancji w struct1
do jej instancji w struct2
.
Dlaczego przypisywanie tablic według elementów członkowskich jest obsługiwane w przypadku struktur, ale nie ogólnie?
edycja : komentarz Rogera Pate'a w wątku std :: string in struct - Problemy z kopiowaniem / przypisaniem? wydaje się wskazywać na ogólny kierunek odpowiedzi, ale nie wiem na tyle, aby samemu to potwierdzić.
edycja 2 : Wiele doskonałych odpowiedzi. Wybrałem Luther Blissett , ponieważ głównie zastanawiałem się nad filozoficznymi lub historycznymi uzasadnieniami tego zachowania, ale odniesienie Jamesa McNellisa do powiązanej dokumentacji specyfikacji było również przydatne.
memcpy()
czegoś podobnego.boost::array
( boost.org/doc/libs/release/doc/html/array.html ), a terazstd::array
( en.cppreference.com/w/cpp/container/array ) są kompatybilnymi z STL alternatywami dla niechlujne stare tablice C. Obsługują przydział kopii.Odpowiedzi:
Oto moje spojrzenie na to:
Rozwój języka C oferuje pewien wgląd w ewolucję typu tablic w języku C:
Spróbuję zarysować tablicę:
Prekursory C, B i BCPL, nie miały odrębnego typu tablicy, deklaracja taka jak:
auto V[10] (B) or let V = vec 10 (BCPL)
zadeklarowałby V jako (bez typu) wskaźnik, który jest zainicjowany, aby wskazywać na nieużywany obszar 10 „słów” pamięci. B już używany
*
dla wskaźnika dereferencing i miał[]
zapis krótkiej strony,*(V+i)
oznaczałoV[i]
, tak jak dzisiaj C / C ++. JednakV
nie jest tablicą, to nadal jest wskaźnikiem, który musi wskazywać na jakąś pamięć. Spowodowało to problemy, gdy Dennis Ritchie próbował rozszerzyć B za pomocą typów struktur. Chciał, aby tablice były częścią struktur, tak jak dzisiaj w C:struct { int inumber; char name[14]; };
Ale z koncepcją B, BCPL tablic jako wskaźników, wymagałoby to,
name
aby pole zawierało wskaźnik, który musiałby być zainicjowany w czasie wykonywania do obszaru pamięci 14 bajtów w strukturze. Problem inicjalizacji / układu został ostatecznie rozwiązany przez nadanie tablicom specjalnego traktowania: kompilator śledziłby położenie tablic w strukturach, na stosie itp. Bez konieczności materializacji wskaźnika do danych, z wyjątkiem wyrażeń, które obejmują tablice. To traktowanie pozwoliło na to, aby prawie cały kod B nadal działał i jest źródłem reguły „tablice konwertowane na wskaźnik, jeśli na nie spojrzysz” . Jest to hack kompatybilności, który okazał się bardzo przydatny, ponieważ pozwalał na tablice o otwartym rozmiarze itp.A oto moje przypuszczenie, dlaczego nie można przypisać tablicy: Ponieważ tablice były wskaźnikami w B, możesz po prostu napisać:
auto V[10]; V=V+5;
aby zmienić bazę „tablicy”. To było teraz bez znaczenia, ponieważ podstawa zmiennej tablicowej nie była już lwartością. Więc to przypisanie było niedozwolone, co pomogło wyłapać kilka programów, które dokonały tego ponownego bazowania na zadeklarowanych tablicach. I wtedy to pojęcie utknęło: ponieważ tablice nigdy nie były zaprojektowane tak, aby były pierwszej klasy cytowane w systemie typu C, były one głównie traktowane jako specjalne bestie, które stały się wskaźnikami, jeśli ich użyjesz. I z pewnego punktu widzenia (który ignoruje fakt, że tablice C to nieudany hack), zakazanie przypisywania tablicy nadal ma sens: otwarta tablica lub parametr funkcji tablicy są traktowane jako wskaźnik bez informacji o rozmiarze. Kompilator nie ma informacji do wygenerowania przypisania tablicy dla nich, a przypisanie wskaźnika było wymagane ze względu na zgodność.
/* 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 }
Nie zmieniło się to, gdy wersja C w 1978 roku dodała przypisanie struktury ( http://cm.bell-labs.com/cm/cs/who/dmr/cchanges.pdf ). Mimo że rekordy były odrębnymi typami w C, nie było możliwe przypisanie ich we wczesnym K&R C. Trzeba było je kopiować według elementów członkowskich za pomocą memcpy i można było przekazywać do nich tylko wskaźniki jako parametry funkcji. Przypisanie (i przekazywanie parametrów) zostało teraz po prostu zdefiniowane jako memcpy surowej pamięci struktury, a ponieważ nie mogło to zepsuć istniejącego kodu, zostało łatwo zaadoptowane. Jako niezamierzony efekt uboczny, to pośrednio wprowadziło jakiś rodzaj przypisania tablicy, ale miało to miejsce gdzieś wewnątrz struktury, więc nie mogło to wprowadzić problemów ze sposobem używania tablic.
źródło
int[10] c;
aby np . Sprawić, by lvaluec
zachowywała się jak tablica dziesięciu elementów, a nie jako wskaźnik do pierwszego elementu tablicy dziesięciopozycyjnej. Istnieje kilka sytuacji, w których przydatna jest możliwość utworzenia typu typedef, który przydziela miejsce, gdy jest używany dla zmiennej, ale przekazuje wskaźnik, gdy jest używany jako argument funkcji, ale brak możliwości posiadania wartości typu tablicowego jest znaczącą słabością semantyczną w języku.Jeśli chodzi o operatory przypisania, standard C ++ mówi, co następuje (C ++ 03 §5.17 / 1):
Tablica nie jest modyfikowalną lwartością.
Jednak przypisanie do obiektu typu klasy jest definiowane specjalnie (§5.17 / 4):
Dlatego szukamy, co robi niejawnie zadeklarowany operator przypisania kopii dla klasy (§12.8 / 13):
Tak więc dla obiektu typu klasy tablice są kopiowane poprawnie. Zwróć uwagę, że jeśli podasz zadeklarowany przez użytkownika operator przypisania kopiowania, nie możesz z tego skorzystać i będziesz musiał skopiować tablicę element po elemencie.
Rozumowanie jest podobne w C (C99 §6.5.16 / 2):
Oraz w §6.3.2.1 / 1:
W C przypisanie jest znacznie prostsze niż w C ++ (§6.5.16.1 / 2):
Aby przypisać obiekty typu strukturalnego, lewy i prawy operand muszą mieć ten sam typ, więc wartość prawego operandu jest po prostu kopiowana do lewego operandu.
źródło
=
wymaga rvalue na RHS, a tablica nie może być rvalue ! Konwersja lwartości do rwartości jest zabroniona dla tablic i jest zastępowana lwartością do wskaźnika.static_cast
nie jest lepszy w tworzeniu wartości r, ponieważ jest zdefiniowana w tych samych warunkach.W tym linku: http://www2.research.att.com/~bs/bs_faq2.html znajduje się sekcja dotycząca przypisywania tablicy:
Oto dwa podstawowe problemy z tablicami
Myślę, że to jest podstawowa różnica między tablicami i strukturami. Zmienna tablicowa to element danych niskiego poziomu o ograniczonej wiedzy o sobie. Zasadniczo jest to kawałek pamięci i sposób na indeksowanie.
Tak więc kompilator nie może odróżnić int a [10] i int b [20].
Struktury nie mają jednak tej samej dwuznaczności.
źródło
sizeof(a)
Vs.sizeof(b)
lub przechodząca
dovoid f(int (&)[20]);
.Wiem, każdy, kto odpowiedział, jest ekspertem w C / C ++. Ale pomyślałem, to jest główny powód.
num2 = num1;
Tutaj próbujesz zmienić adres bazowy tablicy, co jest niedopuszczalne.
i oczywiście struct2 = struct1;
W tym przypadku obiekt struct1 jest przypisany do innego obiektu.
źródło
num2 = num1
zachowywałby się doskonale. Elementy ofnum2
miałyby taką samą wartość jak odpowiedni elementnum1
.Innym powodem, dla którego nie podjęto dalszych wysiłków w celu ulepszenia tablic w C, jest prawdopodobnie to, że przypisanie tablicy nie byłoby tak przydatne. Mimo że można to łatwo osiągnąć w C, opakowując go w strukturę (a adres struktury można po prostu rzutować na adres tablicy lub nawet na adres pierwszego elementu tablicy do dalszego przetwarzania), ta funkcja jest rzadko używana. Jednym z powodów jest to, że tablice o różnych rozmiarach są niekompatybilne, co ogranicza korzyści wynikające z przypisania lub, powiązanego, przekazywania do funkcji według wartości.
Większość funkcji z parametrami tablicowymi w językach, w których tablice są typami pierwszej klasy, jest napisanych dla tablic o dowolnym rozmiarze. Następnie funkcja zwykle wykonuje iterację po zadanej liczbie elementów, czyli informacjach dostarczanych przez tablicę. (W języku C idiom to oczywiście przekazanie wskaźnika i oddzielnej liczby elementów). Funkcja, która akceptuje tablicę tylko jednego określonego rozmiaru, nie jest potrzebna tak często, więc niewiele brakuje. (Zmienia się to, gdy można pozostawić kompilatorowi wygenerowanie oddzielnej funkcji dla dowolnego występującego rozmiaru tablicy, tak jak w przypadku szablonów C ++; to jest powód, dlaczego
std::array
jest przydatny).źródło