Historia pytania
Tablice wielowymiarowe w języku C były początkowo zaprojektowane w celu uproszczenia pracy z tabelami i macierzami. Klasyczna tablica dwuwymiarowa to tablica tablic, z możliwością pracy z elementami danych tabelarycznych za pomocą prostego składni. Z czasem podejście ewoluowało, szczególnie podczas pracy z tablicami o zmiennej długości.
Problem
Niewłaściwa deklaracja i inicjalizacja tablic wielowymiarowych prowadzi do błędów kompilacji lub logicznych awarii. Podczas przekazywania tablicy wielowymiarowej do funkcji wielu programistów traci orientację w związku z wymaganiami specyfikacji — należy wyraźnie podać rozmiary wszystkich wymiarów oprócz pierwszego.
Rozwiązanie
Deklaracja tablicy dwuwymiarowej:
int matrix[3][4];
Pełna inicjalizacja:
int matrix[2][3] = { {1, 2, 3}, {4, 5, 6} };
Przekazanie do funkcji — wszystkie rozmiary, oprócz pierwszego, muszą być wyraźnie podane:
void printMatrix(int m[][3], int rows) { for (int i = 0; i < rows; ++i) { for (int j = 0; j < 3; ++j) printf("%d ", m[i][j]); printf(" "); } }
Wprowadzenie standardu C99 umożliwia deklarowanie funkcji przyjmujących tablice o zmiennej długości:
void foo(int rows, int cols, int a[rows][cols]);
Kluczowe cechy:
1. Czy można zadeklarować funkcję przyjmującą tablicę dwuwymiarową bez podawania drugiego rozmiaru?
Nie, C wymaga, aby wszystkie rozmiary, oprócz pierwszego, były znane na etapie kompilacji. Ma to związek z arytmetyką wskaźników przy dostępie do elementów.
Przykład błędu:
// Błąd: void process(int arr[][], int rows); // Nie można
2. Co się stanie, jeśli zainicjalizujesz nie wszystkie elementy tablicy wielowymiarowej?
Pozostałe elementy zostaną automatycznie wypełnione zerami, jeśli tablica jest statyczna lub globalna. Dla lokalnej tablicy z częściową inicjalizacją niejawnie zainicjalizowane elementy również będą zerami.
int a[2][3] = {{1}, {4}}; // a[0][1] i a[0][2], a[1][1] i a[1][2] będą równe 0
3. Jaka jest różnica między tablicą wskaźników a tablicą dwuwymiarową?
Tablica dwuwymiarowa to jednolity blok pamięci. Tablica wskaźników to zbiór wskaźników na oddzielne (możliwie oddzielnie przydzielone) tablice jednowymiarowe. Jest to ważne, na przykład przy przydzielaniu pamięci dla „poszarpanych” tablic.
Próba zadeklarowania i przekazania tablicy dwuwymiarowej bez podania drugiego rozmiaru do funkcji, co prowadzi do błędu kompilacji. Powierzchowne poprawienie przez zamianę na wskaźnik prowadzi do nieokreślonego lub niepoprawnego zachowania podczas dalszych obliczeń.
Zalety:
Wady:
Programista wyraźnie wskazuje rozmiary wszystkich wymiarów, wyjaśnia w dokumentacji kolejność przechowywania elementów w pamięci, tym samym zmniejszając liczbę błędów w późniejszym użytkowaniu.
Zalety:
Wady: