Naprawiono rozmiar
1. Przekaż przez odniesienie
template <size_t rows, size_t cols>
void process_2d_array_template(int (&array)[rows][cols])
{
std::cout << __func__ << std::endl;
for (size_t i = 0; i < rows; ++i)
{
std::cout << i << ": ";
for (size_t j = 0; j < cols; ++j)
std::cout << array[i][j] << '\t';
std::cout << std::endl;
}
}
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
void process_2d_array_pointer(int (*array)[5][10])
{
std::cout << __func__ << std::endl;
for (size_t i = 0; i < 5; ++i)
{
std::cout << i << ": ";
for (size_t j = 0; j < 10; ++j)
std::cout << (*array)[i][j] << '\t';
std::cout << std::endl;
}
}
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 zanik process_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
// int array[][10] is just fancy notation for the same thing
void process_2d_array(int (*array)[10], size_t rows)
{
std::cout << __func__ << std::endl;
for (size_t i = 0; i < rows; ++i)
{
std::cout << i << ": ";
for (size_t j = 0; j < 10; ++j)
std::cout << array[i][j] << '\t';
std::cout << std::endl;
}
}
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 identyfikator array
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
// int *array[10] is just fancy notation for the same thing
void process_pointer_2_pointer(int **array, size_t rows, size_t cols)
{
std::cout << __func__ << std::endl;
for (size_t i = 0; i < rows; ++i)
{
std::cout << i << ": ";
for (size_t j = 0; j < cols; ++j)
std::cout << array[i][j] << '\t';
std::cout << std::endl;
}
}
Znowu istnieje alternatywna składnia, int *array[10]
która jest taka sama jak int **array
. W tej składni [10]
znak ignorowany jest, gdy rozpada się na wskaźnik, stając się w ten sposób int **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:
#include <iostream>
// copy above functions here
int main()
{
int a[5][10] = { { } };
process_2d_array_template(a);
process_2d_array_pointer(&a); // <-- notice the unusual usage of addressof (&) operator on an array
process_2d_array(a, 5);
// works since a's first dimension decays into a pointer thereby becoming int (*)[10]
int *b[5]; // surrogate
for (size_t i = 0; i < 5; ++i)
{
b[i] = a[i];
}
// another popular way to define b: here the 2D arrays dims may be non-const, runtime var
// int **b = new int*[5];
// for (size_t i = 0; i < 5; ++i) b[i] = new int[10];
process_pointer_2_pointer(b, 5, 10);
// process_2d_array(b, 5);
// doesn't work since b's first dimension decays into a pointer thereby becoming int**
}