Mam tablicę, int arr[5]
która jest przekazywana do funkcji fillarr(int arr[])
:
int fillarr(int arr[])
{
for(...);
return arr;
}
- Jak mogę zwrócić tę tablicę?
- Jak mam z niego korzystać, powiedzmy, że zwróciłem wskaźnik, w jaki sposób mam uzyskać do niego dostęp?
std::vector
.5
W podpisie funkcja jest odrzucana przez kompilator.Odpowiedzi:
W takim przypadku zmienna tablicowa
arr
może być faktycznie traktowana jako wskaźnik do początku bloku tablicy w pamięci, poprzez niejawną konwersję. Ta składnia, której używasz:To rodzaj po prostu cukru syntaktycznego. Naprawdę możesz go zastąpić tym i nadal będzie działać:
W tym samym sensie to, co chcesz zwrócić z funkcji, to tak naprawdę wskaźnik do pierwszego elementu w tablicy:
I nadal będziesz mógł używać go tak jak normalnej tablicy:
źródło
array
i&array
są zamienne w wielu przypadkach.a
INint a[10]
jestint[10]
. Można powiedzieć, że tablice „rozpadają się” na wskaźniki do ich pierwszego elementu. (Jest to niejawna konwersja tablic na wskaźniki). Wtedy twoja odpowiedź byłaby zgodna z moimi. Jeśli edytujesz swoją odpowiedź, aby rozróżnić tablice, konwersję tablic na wskaźniki i wskaźniki, usunę moją odpowiedź, ponieważ będą miały te same podstawowe informacje, a ty będziesz pierwszy.Funkcje C ++ nie mogą zwracać tablic typu C według wartości. Najbliższą rzeczą jest zwrócenie wskaźnika. Ponadto typ tablicy na liście argumentów jest po prostu konwertowany na wskaźnik.
Możesz to poprawić, korzystając z odwołań do tablicy argumentu i return, co zapobiega rozpadowi:
W przypadku Boost lub C ++ 11 przekazywanie przez odniesienie jest tylko opcjonalne, a składnia jest mniej wymagająca:
array
Szablon po prostu generujestruct
zawierający tablicę C-style, dzięki czemu można zastosować semantykę obiektowych jeszcze zachowują pierwotną prostotę tablicy użytkownika.źródło
typedef int array[5]; array& foo();
Ale ty nawet nie trzeba typedef jeśli zależy Ci napisać to:int (&foo())[5] { static int a[5] = {}; return a; }
, przykład w pytaniu będzie:int (&foo( int (&a)[5] ))[5] { return a; }
. Proste, prawda?error: function returning array is not allowed
który pojawia się, jeśli pominiesz zewnętrzne pareny w składni nietypowej. Na szczęście dzisiaj sprawdziłem zasadę prawa-lewa dla innego pytania i udało mi się skonstruować właściwą rzecz… po tym, jak powiedziałeś, że to możliwe… zanim zobaczyłeś, że podałeś kod: vP.W C ++ 11 możesz wrócić
std::array
.źródło
(...) you can consider the array returned arr2, totally another array (...)
8,3,5 USD / 8 stanów
„Funkcje nie powinny mieć zwracanej tablicy typów lub funkcji, chociaż mogą mieć zwracany wskaźnik typu lub odwołanie do takich rzeczy. Nie będzie tablic funkcji, chociaż mogą istnieć tablice wskaźników funkcji.”
źródło
int
, a nie do tablicy.odpowiedź może nieco zależeć od tego, jak zamierzasz korzystać z tej funkcji. Dla najprostszej odpowiedzi, zdecydujmy, że zamiast tablicy, naprawdę chcesz wektor. Wektory są ładne, ponieważ wygląd całego świata jest nudny, zwykłe wartości, które można przechowywać w regularnych wskaźnikach. Przyjrzymy się innym opcjom i dlaczego chcesz je później:
To zrobi dokładnie to, czego oczekujesz. Zaletą jest to
std::vector
dba się o to, aby wszystko było traktowane w sposób czysty. Minusem jest to, że kopiuje to bardzo dużą ilość danych, jeśli twoja tablica jest duża. W rzeczywistości kopiuje każdy element tablicy dwukrotnie. najpierw kopiuje wektor, aby funkcja mogła go użyć jako parametru. następnie kopiuje go ponownie, aby zwrócić go dzwoniącemu. Jeśli potrafisz samodzielnie zarządzać wektorem, możesz robić rzeczy o wiele łatwiej. (może skopiować go po raz trzeci, jeśli dzwoniący musi zapisać go w jakiejś zmiennej, aby wykonać więcej obliczeń)Wygląda na to, że tak naprawdę próbujesz wypełnić tylko kolekcję. jeśli nie masz konkretnego powodu, aby zwrócić nową instancję kolekcji, nie rób tego. możemy to zrobić w ten sposób
w ten sposób otrzymujesz odwołanie do tablicy przekazanej do funkcji, a nie jej prywatną kopię. wszelkie zmiany dokonane w parametrze są widoczne dla dzwoniącego. Możesz zwrócić odwołanie, jeśli chcesz, ale to nie jest naprawdę świetny pomysł, ponieważ sugeruje, że dostajesz coś innego niż to, co przekazałeś.
Jeśli naprawdę potrzebujesz nowej instancji kolekcji, ale chcesz uniknąć umieszczenia jej na stosie (i całego związanego z tym kopiowania), musisz utworzyć umowę dotyczącą sposobu obsługi tej instancji. najprostszym sposobem na to jest użycie inteligentnego wskaźnika, który utrzymuje referencyjną instancję tak długo, jak długo ktoś ją trzyma. Znika czysto, jeśli wykracza poza zakres. Tak by to wyglądało.
W przeważającej części za pomocą
*myArr
działa identycznie jak zwykły wektor waniliowy. Ten przykład modyfikuje również listę parametrów poprzez dodanieconst
słowa kluczowego. Teraz otrzymujesz referencję bez kopiowania, ale nie możesz jej modyfikować, więc osoba dzwoniąca wie, że będzie taka sama, jak zanim funkcja do niej dotarła.Wszystko to jest spuchnięte, ale idiomatyczne c ++ rzadko działa z kolekcjami jako całością. Zwykle będziesz używać iteratorów w tych kolekcjach. wyglądałoby to mniej więcej tak
Używanie go wygląda trochę dziwnie, jeśli nie jesteś przyzwyczajony do tego stylu.
foo teraz „wskazuje” na początek zmodyfikowanego
arr
.Naprawdę fajne jest to, że działa równie dobrze na wektorze, jak na zwykłych tablicach C i wielu innych typach kolekcji, na przykład
Które teraz wygląda okropnie jak zwykłe przykłady wskaźników podane w innym miejscu tego pytania.
źródło
&
symbol musi pojawić się po typie:void fillarr(std::vector<int> & arr)
To:
jest traktowany tak samo jak:
Teraz, jeśli naprawdę chcesz zwrócić tablicę, możesz zmienić tę linię na
Tak naprawdę to nie zwraca tablicy. zwracasz wskaźnik na początek adresu tablicy.
Pamiętaj jednak, że kiedy przekazujesz tablicę, przekazujesz tylko wskaźnik. Więc kiedy modyfikujesz dane tablicy, tak naprawdę modyfikujesz dane, na które wskazuje wskaźnik. Dlatego zanim przejdziesz do tablicy, musisz zdać sobie sprawę, że masz już na zewnątrz zmodyfikowany wynik.
na przykład
Sugeruję, abyś mógł rozważyć umieszczenie długości w swojej funkcji fillarr w ten sposób.
W ten sposób możesz użyć długości do wypełnienia tablicy do jej długości bez względu na to, co to jest.
Aby właściwie go używać poprawnie. Zrób coś takiego:
Jeśli wszystko, co chcesz zrobić, to ustawić tablicę na niektóre wartości domyślne, rozważ użycie wbudowanej funkcji memset.
coś takiego: memset ((int *) & arr, 5, sizeof (int));
Chociaż jestem na ten temat. Mówisz, że używasz C ++. Zobacz wektory stl. Twój kod prawdopodobnie będzie bardziej niezawodny.
Istnieje wiele samouczków. Oto jeden, który daje wyobrażenie o tym, jak z nich korzystać. http://www.yolinux.com/TUTORIALS/LinuxTutorialC++STL.html
źródło
std::copy
ponadmemset
, jest bezpieczniejsze i łatwiejsze. (I tak samo szybko, jeśli nie szybciej.)aby zwrócić tablicę z funkcji, zdefiniujmy tę tablicę w strukturze; Wygląda to mniej więcej tak
Teraz stwórzmy zmienne o strukturze typu.
Możemy przekazać tablicę do funkcji w następujący sposób i przypisać jej wartość:
Możemy również zwrócić tablicę. Aby zwrócić tablicę, zwracany typ funkcji powinien być typu struktury, tj. Znaczników. Jest tak, ponieważ w rzeczywistości przekazujemy strukturę zawierającą tablicę. Tak więc końcowy kod może wyglądać tak.
źródło
To dość stare pytanie, ale zamierzam włożyć moje 2 centy, ponieważ jest wiele odpowiedzi, ale żadne nie pokazuje wszystkich możliwych metod w jasny i zwięzły sposób (nie jestem pewien co do zwięzłego fragmentu, ponieważ otrzymałem bit poza kontrolą. TL; DR 😉).
Zakładam, że OP chciał zwrócić tablicę, która została przekazana bez kopiowania, jako pewien sposób bezpośredniego przekazania tego do programu wywołującego, który zostanie przekazany do innej funkcji, aby kod wyglądał ładniej.
Jednak użycie tablicy takiej jak ta pozwala jej rozpadnąć się na wskaźnik i pozwolić kompilatorowi traktować ją tak tablicę. Może to powodować subtelne błędy, jeśli przejdziesz przez tablicę podobną do, z funkcją oczekującą, że będzie ona miała 5 elementów, ale twój rozmówca faktycznie poda inną liczbę.
Jest kilka sposobów, aby lepiej sobie z tym poradzić. Przekaż
std::vector
lubstd::array
(nie jestem pewien, czystd::array
było w 2010 roku, kiedy zadano pytanie). Następnie możesz przekazać obiekt jako odniesienie bez kopiowania / przenoszenia obiektu.Jeśli jednak nalegasz na grę tablicami C, użyj szablonu, który zachowa informacje o tym, ile elementów w tablicy.
Tyle że wygląda brzydko i jest bardzo trudny do odczytania. Teraz używam czegoś, aby pomóc w tym, czego nie było w 2010 roku, którego używam również do wskaźników funkcji:
Zmienia to typ, w którym można się spodziewać, czyniąc to znacznie bardziej czytelnym. Oczywiście użycie szablonu jest zbędne, jeśli nie zamierzasz używać niczego poza 5 elementami, więc możesz oczywiście go na stałe zapisać:
Jak powiedziałem, moja
type_t<>
sztuczka nie zadziałałaby w chwili, gdy pytanie zostało zadane. Najlepszym, na co wtedy mogłeś mieć nadzieję, było użycie typu w strukturze:Które zaczyna znów wyglądać dość brzydko, ale przynajmniej jest jeszcze bardziej czytelne, chociaż wtedy
typename
mogły być opcjonalne w zależności od kompilatora, w wyniku czego:I oczywiście mógłbyś podać konkretny typ, zamiast używać mojego pomocnika.
Wówczas darmowe funkcje
std::begin()
istd::end()
nie istniały, choć można je było łatwo wdrożyć. Pozwoliłoby to na iterację po tablicy w bezpieczniejszy sposób, ponieważ mają one sens w przypadku tablicy C, ale nie wskaźnika.Jeśli chodzi o dostęp do tablicy, możesz albo przekazać ją do innej funkcji, która przyjmuje ten sam typ parametru, albo utworzyć alias do niej (co nie miałoby większego sensu, ponieważ masz już oryginał w tym zakresie). Dostęp do odwołania do tablicy jest jak dostęp do oryginalnej tablicy.
lub
Podsumowując, najlepiej nie dopuścić do rozpadu tablicy na wskaźnik, jeśli zamierzasz iterować po nim. To po prostu zły pomysł, ponieważ powstrzymuje kompilator przed ochroną przed postrzeleniem się w stopę i utrudnia odczytanie kodu. Zawsze staraj się pomóc kompilatorowi w utrzymaniu typów tak długo, jak to możliwe, chyba że masz bardzo dobry powód, aby tego nie robić.
Edytować
Aha, i dla kompletności, możesz pozwolić, aby zdegradowała się do wskaźnika, ale to oddziela tablicę od liczby elementów, które zawiera. Odbywa się to często w C / C ++ i zwykle jest łagodzone przez przekazywanie liczby elementów w tablicy. Jednak kompilator nie może ci pomóc, jeśli popełnisz błąd i podasz niewłaściwą wartość do liczby elementów.
Zamiast przekazać rozmiar, możesz przekazać wskaźnik końca, który wskaże jeden koniec końca tablicy. Jest to przydatne, ponieważ pozwala uzyskać coś, co jest bliższe algorytmom std, które przyjmują wskaźnik początku i końca, ale to, co powrócisz, jest teraz tylko czymś, o czym musisz pamiętać.
Alternatywnie możesz udokumentować, że ta funkcja zajmie tylko 5 elementów i masz nadzieję, że użytkownik tej funkcji nie zrobi nic głupiego.
Zauważ, że zwracana wartość straciła swój pierwotny typ i jest zdegradowana do wskaźnika. Z tego powodu jesteś teraz sam, aby upewnić się, że nie zamierzasz przekroczyć tablicy.
Możesz przekazać
std::pair<int*, int*>
, którego możesz użyć na początku i na końcu, i przekazać dalej, ale wtedy naprawdę przestaje wyglądać jak tablica.lub
Zabawne, jest to bardzo podobne do
std::initializer_list
działania (c ++ 11), ale nie działają w tym kontekście.źródło
najprostszym sposobem na to jest zwrócenie go przez referencję, nawet jeśli nie napiszesz symbolu „&”, jest on automatycznie zwracany przez referencję
źródło
Nadal możesz użyć wyniku jak
źródło
Jak wyżej wspomniane ścieżki są poprawne. Ale myślę, że jeśli po prostu zwrócimy lokalną zmienną tablicową funkcji, czasami zwraca ona wartości śmieci jako elementy.
w celu uniknięcia tego musiałem dynamicznie utworzyć tablicę i kontynuować. Które jest coś takiego.
źródło
źródło
Źródło: https://www.tutorialspoint.com/cplusplus/cpp_return_arrays_from_functions.htm
C ++ nie pozwala na zwrócenie całej tablicy jako argumentu funkcji. Można jednak zwrócić wskaźnik do tablicy, podając jej nazwę bez indeksu.
Stosując te reguły do bieżącego pytania, możemy napisać program w następujący sposób:
Dane wyjściowe będą:
źródło
a co z:
źródło
W rzeczywistości, gdy przekazujesz tablicę wewnątrz funkcji, wskaźnik do oryginalnej tablicy jest przekazywany w parametrze funkcji, a zatem zmiany dokonane w tablicy wewnątrz tej funkcji są faktycznie dokonywane na oryginalnej tablicy.
Uruchom go, a zobaczysz zmiany
źródło
y
jest przekazywana jako jej kopia, ale ponieważ jest wskaźnikiem, będziesz bezpośrednio operować na tablicy. Edytuj swoją odpowiedź.Oto pełny przykład tego rodzaju problemu do rozwiązania
źródło
Po prostu zdefiniuj typ [] jako wartość zwracaną, na przykład:
. . . wywołanie funkcji:
źródło