Mam funkcję, którą chcę przyjąć jako parametr tablicę 2D o zmiennej wielkości.
Do tej pory mam to:
void myFunction(double** myArray){
myArray[x][y] = 5;
etc...
}
I zadeklarowałem tablicę gdzie indziej w moim kodzie:
double anArray[10][10];
Jednak wywołanie myFunction(anArray)
powoduje błąd.
Nie chcę kopiować tablicy po jej przekazaniu. Wszelkie wprowadzone zmiany myFunction
powinny zmienić stan anArray
. Jeśli dobrze rozumiem, chcę przekazać jako argument wskaźnik do tablicy 2D. Funkcja musi także akceptować tablice o różnych rozmiarach. Na przykład [10][10]
i [5][5]
. W jaki sposób mogę to zrobić?
c++
arrays
pointers
multidimensional-array
RogerDarwin
źródło
źródło
func(int* mat, int r, int c){ for(int i=0; i<r; i++) for(int j=0; j<c; j++) printf("%d ", *(mat+i*c+j)); }
. Nazwij to jakint mat[3][5]; func(mat[0], 3, 5);
Odpowiedzi:
Istnieją trzy sposoby przekazania tablicy 2D do funkcji:
Ten parametr to tablica 2D
Parametr jest tablicą zawierającą wskaźniki
Ten parametr jest wskaźnikiem do wskaźnika
źródło
array
zarray[i][j]
:)int (*a)[10]
.int **
.int (*a) [10]
.Naprawiono rozmiar
1. Przekaż przez odniesienie
W C ++ przekazywanie tablicy przez referencję bez utraty informacji o wymiarze jest prawdopodobnie najbezpieczniejsze, ponieważ nie trzeba się martwić, że osoba dzwoniąca przekaże niepoprawny wymiar (flagi kompilatora podczas niedopasowania). Nie jest to jednak możliwe w przypadku tablic dynamicznych (Freestore); działa tylko w przypadku tablic automatycznych ( zwykle stosowych ), tzn. wymiary powinny być znane w czasie kompilacji.
2. Przejdź obok wskaźnika
Odpowiednikiem C poprzedniej metody jest przekazanie tablicy przez wskaźnik. Nie należy tego mylić z przekazywaniem zepsutego typu wskaźnika tablicy (3) , który jest powszechną, popularną metodą, choć mniej bezpieczną niż ta, ale bardziej elastyczną. Podobnie jak (1) , użyj tej metody, gdy wszystkie wymiary tablicy są ustalone i znane w czasie kompilacji. Zauważ, że podczas wywoływania funkcji należy podać adres tablicy,
process_2d_array_pointer(&a)
a nie adres pierwszego elementu przez zanikprocess_2d_array_pointer(a)
.Zmienny rozmiar
Są one dziedziczone z C, ale są mniej bezpieczne, kompilator nie ma możliwości sprawdzenia, gwarantując, że osoba dzwoniąca przekazuje wymagane wymiary. Funkcja opiera się tylko na tym, co przekazujący wywołuje jako wymiar (wymiary). Są one bardziej elastyczne niż powyższe, ponieważ tablice o różnych długościach mogą być do nich przekazywane niezmiennie.
Należy pamiętać, że nie ma czegoś takiego jak przekazywanie tablicy bezpośrednio do funkcji w C [podczas gdy w C ++ można je przekazywać jako odwołanie (1) ]; (2) przekazuje wskaźnik do tablicy, a nie do samej tablicy. Zawsze przekazywanie tablicy bez zmian staje się operacją kopiowania wskaźnika, która jest ułatwiona przez naturę rozkładu tablicy na wskaźnik .
3. Przekaż (wartość) wskaźnik do zepsutego typu
Chociaż
int array[][10]
jest to dozwolone, nie poleciłbym go powyżej powyższej składni, ponieważ powyższa składnia wyjaśnia, że identyfikatorarray
jest pojedynczym wskaźnikiem do tablicy 10 liczb całkowitych, podczas gdy ta składnia wygląda na tablicę 2D, ale jest tym samym wskaźnikiem tablica 10 liczb całkowitych. Znamy tutaj liczbę elementów w jednym wierszu (tj. Rozmiar kolumny, tutaj 10), ale liczba wierszy jest nieznana i dlatego należy ją przekazać jako argument. W tym przypadku istnieje pewne bezpieczeństwo, ponieważ kompilator może oflagować się, gdy zostanie przekazany wskaźnik do tablicy o drugim wymiarze innym niż 10. Pierwszy wymiar jest częścią zmienną i można go pominąć. Zobacz tutaj uzasadnienie, dlaczego można pominąć tylko pierwszy wymiar.4. Przekaż wskaźnik do wskaźnika
Znowu istnieje alternatywna składnia,
int *array[10]
która jest taka sama jakint **array
. W tej składni[10]
znak ignorowany jest, gdy rozpada się na wskaźnik, stając się w ten sposóbint **array
. Być może jest to tylko wskazówka dla wywołującego, że przekazywana tablica powinna mieć co najmniej 10 kolumn, nawet wtedy wymagana jest liczba wierszy. W każdym razie kompilator nie zgłasza żadnych naruszeń długości / rozmiaru (sprawdza tylko, czy przekazany typ jest wskaźnikiem do wskaźnika), dlatego wymaga liczenia wierszy i kolumn, ponieważ parametr ma tutaj sens.Uwaga: (4) jest najmniej bezpieczną opcją, ponieważ prawie nie ma żadnej kontroli typu i jest najbardziej niewygodna. Do tej funkcji nie można legalnie przekazać tablicy 2D; C-FAQ potępia zwykłe obejście,
int x[5][10]; process_pointer_2_pointer((int**)&x[0][0], 5, 10);
ponieważ może potencjalnie prowadzić do nieokreślonego zachowania z powodu spłaszczenia tablicy. Właściwy sposób przekazywania tablicy w tej metodzie prowadzi nas do niewygodnej części, tzn. Potrzebujemy dodatkowej (zastępczej) tablicy wskaźników, z których każdy jej element wskazuje na odpowiedni rząd rzeczywistej tablicy, która ma być przekazana; ten surogat jest następnie przekazywany do funkcji (patrz poniżej); wszystko po to, aby wykonać tę samą pracę, co powyższe metody, które są bezpieczniejsze, czystsze i być może szybsze.Oto program sterownika do testowania powyższych funkcji:
źródło
b[i] = a[i];
, powiedzmy, nab[i] = new int[10];
. Można takżeb
przypisać dynamicznieint **b = int *[5];
i nadal będzie działał bez zmian.array[i][j]
działa w funkcji w 4) ? Ponieważ otrzymał ptr do ptr i nie zna wartości ostatniego wymiaru, co jest konieczne do wykonania przesunięcia dla poprawnego adresowania?array[i][j]
jest po prostu arytmetyką wskaźnika, tj. do wartości wskaźnikaarray
,i
dodawałby i wyłapywał wynik jakoint*
, do któregoj
dodawałby i wyłapywał to położenie, czytającint
. Więc nie, nie musi to mieć żadnego wymiaru. Ale o to chodzi! Kompilator bierze słowo programisty z wiarą, a jeśli programista był niepoprawny, powstaje niezdefiniowane zachowanie. Właśnie dlatego wspomniałem, że przypadek 4 jest najmniej bezpieczną opcją.Modyfikacja pierwszej sugestii shengy, możesz użyć szablonów, aby funkcja zaakceptowała wielowymiarową zmienną tablicową (zamiast przechowywać tablicę wskaźników, którymi trzeba zarządzać i usuwać):
Instrukcje print mają na celu pokazanie, że tablice są przekazywane przez referencję (poprzez wyświetlanie adresów zmiennych)
źródło
%p
do wydrukowania wskaźnika, a nawet wtedy musisz go rzucićvoid *
, w przeciwnym razieprintf()
wywoła niezdefiniowane zachowanie. Ponadto nie powinieneś używać&
operatora addressof ( ) podczas wywoływania funkcji, ponieważ funkcje oczekują argumentu typudouble (*)[size_y]
, podczas gdy obecnie przekazujesz jedouble (*)[10][10]
idouble (*)[5][5]
.Zaskoczony, że nikt jeszcze o tym nie wspominał, ale możesz po prostu szablonować na dowolnej 2D obsługującej [] [] semantykę.
Działa z dowolną
std::vector<std::vector<T>>
strukturą danych typu „tablicowego” 2D, taką jak typ zdefiniowany przez użytkownika, aby zmaksymalizować ponowne użycie kodu.źródło
Możesz utworzyć szablon funkcji w ten sposób:
Następnie masz oba rozmiary wymiarów za pośrednictwem R i C. Dla każdego rozmiaru tablicy zostanie utworzona inna funkcja, więc jeśli twoja funkcja jest duża i wywołujesz ją z różnymi rozmiarami tablicy, może to być kosztowne. Możesz jednak użyć go jako opakowania funkcji takiej jak ta:
Traktuje tablicę jako jednowymiarową i wykorzystuje arytmetykę do ustalenia przesunięć indeksów. W takim przypadku należy zdefiniować szablon w następujący sposób:
źródło
size_t
jest lepszym typem dla indeksów tablic niżint
.anArray[10][10]
nie jest wskaźnikiem do wskaźnika, to ciągły fragment pamięci odpowiedni do przechowywania 100 wartości typu double, który kompilator wie jak zaadresować, ponieważ określono wymiary. Musisz przekazać go do funkcji jako tablicy. Możesz pominąć rozmiar wymiaru początkowego w następujący sposób:Nie pozwoli to jednak na przekazywanie tablic o ostatnim wymiarze innym niż dziesięć.
Najlepszym rozwiązaniem w C ++ jest użycie
std::vector<std::vector<double> >
: jest prawie tak samo wydajny i znacznie wygodniejszy.źródło
Tablica jednowymiarowa rozpada się na wskaźnik wskazujący na pierwszy element w tablicy. Podczas gdy tablica 2D rozpada się na wskaźnik wskazujący pierwszy rząd. Prototypem funkcji powinno być -
Wolałbym
std::vector
surowe tablice.źródło
Możesz zrobić coś takiego ...
Twój wynik będzie następujący ...
źródło
Oto przykład wektora macierzy wektorów
wynik:
źródło
Możemy użyć kilku sposobów przekazania tablicy 2D do funkcji:
Za pomocą pojedynczego wskaźnika musimy typecastować tablicę 2D.
Korzystanie z podwójnego wskaźnika W ten sposób również rzutujemy tablicę 2d
źródło
Jedną ważną rzeczą do przekazywania tablic wielowymiarowych jest:
First array dimension
nie trzeba podawać.Second(any any further)dimension
musi zostać określony.1.Gdy tylko drugi wymiar jest dostępny globalnie (jako makro lub jako stała globalna)
2.Korzystając z jednego wskaźnika : w tej metodzie musimy przerzucić tablicę 2D podczas przekazywania do funkcji.
źródło
Aby to zrobić, możesz skorzystać z narzędzia szablonów w C ++. Zrobiłem coś takiego:
Problem z tym podejściem polega na tym, że dla każdej wartości podanej kolumny, tworzona jest nowa definicja funkcji przy użyciu szablonu. więc,
tworzy szablon dwa razy, aby utworzyć 2 definicje funkcji (jedną, gdzie col = 3 i drugą, gdzie col = 5).
źródło
Jeśli chcesz przejść , musisz
int a[2][3]
wykonaćvoid func(int** pp)
następujące kroki pomocnicze.Ponieważ pierwszy
[2]
można niejawnie określić, można go dodatkowo uprościć jako.źródło
W przypadku, gdy chcesz przekazać do funkcji tablicę 2D o dynamicznym rozmiarze, użycie niektórych wskaźników może być dla Ciebie przydatne.
źródło
Możesz pominąć skrajny lewy wymiar, więc otrzymujesz dwie opcje:
To samo dotyczy wskaźników:
Rozpad N-wymiarowej tablicy na wskaźnik na tablicę wymiarową N-1 jest dozwolony przez standard C ++ , ponieważ możesz stracić skrajny lewy wymiar i nadal być w stanie poprawnie uzyskać dostęp do elementów tablicy z informacjami o wymiarach N-1.
Szczegóły tutaj
Chociaż tablice i wskaźniki nie są takie same : tablica może rozpaść się na wskaźnik, ale wskaźnik nie ma stanu dotyczącego rozmiaru / konfiguracji danych, na które wskazuje.
A
char **
jest wskaźnikiem do bloku pamięci zawierającego wskaźniki znaków , które same wskazują na bloki pamięci znaków. Achar [][]
to pojedynczy blok pamięci, który zawiera znaki. Ma to wpływ na to, jak kompilator przetłumaczy kod i jak będzie wyglądać końcowa wydajność.Źródło
źródło