Potrzebuję wskaźnika do statycznej dwuwymiarowej tablicy. Jak to się robi?
static uint8_t l_matrix[10][20];
void test(){
uint8_t **matrix_ptr = l_matrix; //wrong idea
}
Otrzymuję różnego rodzaju błędy, takie jak:
- ostrzeżenie: przypisanie z niezgodnego typu wskaźnika
- wartość indeksowana nie jest tablicą ani wskaźnikiem
- błąd: nieprawidłowe użycie elastycznego elementu tablicy
Odpowiedzi:
Tutaj chcesz zrobić wskaźnik do pierwszego elementu tablicy
Z typedef wygląda to bardziej przejrzysto
Wtedy znów możesz cieszyć się życiem :)
Uważaj na świat wskaźników / tablic w C, wokół tego jest dużo zamieszania.
Edytować
Przejrzyj niektóre inne odpowiedzi tutaj, ponieważ pola komentarzy są zbyt krótkie, aby je tam zrobić. Zaproponowano wiele alternatyw, ale nie pokazano, jak się zachowują. Oto jak to robią
Jeśli naprawisz błąd i dodasz operator address-of,
&
jak w poniższym fragmencieNastępnie ten tworzy wskaźnik do niekompletnego typu tablicy elementów typu array o wartości 20 uint8_t. Ponieważ wskaźnik wskazuje tablicę tablic, musisz uzyskać do niej dostęp za pomocą
A ponieważ jest to wskaźnik do niekompletnej tablicy, nie można tego zrobić jako skrótu
Ponieważ indeksowanie wymaga znajomości rozmiaru typu elementu (indeksowanie oznacza dodanie liczby całkowitej do wskaźnika, więc nie będzie działać z niekompletnymi typami). Zauważ, że działa to tylko w
C
, ponieważT[]
iT[N]
są kompatybilne typy. C ++ nie ma koncepcji kompatybilnych typów , więc odrzuci ten kod, ponieważT[]
iT[10]
są różne typy.Poniższa alternatywa w ogóle nie działa, ponieważ typ elementu tablicy, gdy widzisz ją jako tablicę jednowymiarową, nie jest
uint8_t
, aleuint8_t[20]
Oto dobra alternatywa
Masz do niego dostęp za pomocą
Ma tę zaletę, że zachowuje rozmiar wymiaru zewnętrznego. Więc możesz nałożyć na to sizeof
Jest jeszcze jedna odpowiedź, która wykorzystuje fakt, że elementy w tablicy są przechowywane w sposób ciągły
Otóż, to formalnie pozwala tylko na dostęp do elementów pierwszego elementu dwuwymiarowej tablicy. Oznacza to, że spełniony jest następujący warunek
Zauważysz, że prawdopodobnie działa
10*20-1
, ale jeśli użyjesz analizy aliasów i innych agresywnych optymalizacji, jakiś kompilator może przyjąć założenie, które może złamać ten kod. Powiedziawszy to, nigdy nie spotkałem kompilatora, który zawodzi (ale z drugiej strony nie użyłem tej techniki w prawdziwym kodzie), a nawet C FAQ zawiera tę technikę (z ostrzeżeniem o jej UB'ness ), a jeśli nie możesz zmienić typu tablicy, jest to ostatnia opcja, aby cię uratować :)źródło
uint8_t *d[20]
pomyliłeś to z , co tworzy tablicę 3 wskaźników do uint8_t, ale to nie zadziała w tym przypadku.Aby w pełni to zrozumieć, musisz zrozumieć następujące pojęcia:
Tablice nie są wskaźnikami!
Przede wszystkim (i zostało to wystarczająco nauczone) tablice nie są wskaźnikami . Zamiast tego w większości zastosowań „rozpadają się” na adres do ich pierwszego elementu, który można przypisać do wskaźnika:
Zakładam, że działa to w ten sposób, że można uzyskać dostęp do zawartości tablicy bez kopiowania ich wszystkich. To tylko zachowanie typów tablicowych i nie ma na celu sugerowania, że są one tym samym.
Tablice wielowymiarowe
Tablice wielowymiarowe to tylko sposób na „partycjonowanie” pamięci w sposób, który kompilator / maszyna może zrozumieć i na którym może operować.
Na przykład
int a[4][3][5]
= tablica zawierająca 4 * 3 * 5 (60) „fragmentów” pamięci o rozmiarze całkowitoliczbowym.Zaletą używania
int a[4][3][5]
vs zwykłegoint b[60]
jest to, że są teraz „podzielone na partycje” (łatwiej jest pracować z ich „fragmentami”, jeśli to konieczne), a program może teraz wykonywać sprawdzanie powiązań.W rzeczywistości
int a[4][3][5]
jest przechowywany dokładnie tak , jakint b[60]
w pamięci - jedyną różnicą jest to, że program teraz zarządza nim tak, jakby były oddzielnymi jednostkami o określonych rozmiarach (w szczególności cztery grupy po trzy grupy po pięć).Pamiętaj: oba
int a[4][3][5]
iint b[60]
są takie same w pamięci, a jedyna różnica polega na tym, jak są obsługiwane przez aplikację / kompilatorZ tego widać wyraźnie, że każda „partycja” jest po prostu tablicą, którą program śledzi.
Składnia
Teraz tablice różnią się składnią od wskaźników . W szczególności oznacza to, że kompilator / maszyna będzie traktować je inaczej. Może się to wydawać oczywiste, ale spójrz na to:
Powyższy przykład wyświetla ten sam adres pamięci dwukrotnie, na przykład:
Jednak tylko jeden może być przypisany do wskaźnika tak bezpośrednio :
Dlaczego nie można
a
go przypisać do wskaźnika, alea[0]
można?Jest to po prostu konsekwencja wielowymiarowych tablic i wyjaśnię, dlaczego:
Na poziomie „
a
” nadal widzimy, że mamy inny „wymiar”, na który czekamy. Jednak na poziomie „a[0]
” jesteśmy już w najwyższym wymiarze, więc jeśli chodzi o program, patrzymy tylko na normalną tablicę.Możesz zapytać:
Dlaczego ma to znaczenie, jeśli tablica jest wielowymiarowa, jeśli chodzi o tworzenie wskaźnika?
Najlepiej myśleć w ten sposób:
`` Zanik '' z wielowymiarowej tablicy to nie tylko adres, ale adres z danymi partycji (AKA nadal rozumie, że jego podstawowe dane składają się z innych tablic), który składa się z granic wyznaczonych przez tablicę poza pierwszym wymiarem.
Ta logika `` partycji '' nie może istnieć we wskaźniku, chyba że ją określimy:
W przeciwnym razie znaczenie właściwości sortowania tablicy zostanie utracone.
Zwróć również uwagę na użycie nawiasów wokół
*p
:int (*p)[5][95][8]
- To jest określenie, że tworzymy wskaźnik z tymi granicami, a nie tablicą wskaźników z tymi granicami:int *p[5][95][8]
Wniosek
Przejrzyjmy:
W skrócie: tablice wielowymiarowe rozpadają się na adresy, które niosą zdolność zrozumienia ich zawartości.
źródło
int *p1 = &(a[0]); // RIGHT !
w rzeczywistości jest identyczne zint *p1 = a;
W
możesz uzyskać dostęp jak
w końcu wszystkie dwuwymiarowe tablice są również przechowywane jako 1-d.
źródło
Dzień dobry,
Deklaracja
zarezerwował miejsce na 10 rzędów po 20 jednostek unit8_t, tj. 200 lokalizacji o rozmiarze uint8_t, przy czym każdy element został znaleziony przez obliczenie 20 x wiersz + kolumna.
Tak nie jest
podać to, czego potrzebujesz i wskazać element zerowy kolumny w pierwszym wierszu tablicy?
Edycja: Rozważając to nieco dalej, czy nazwa tablicy nie jest z definicji wskaźnikiem? Oznacza to, że nazwa tablicy jest synonimem położenia pierwszego elementu, tj. L_matrix [0] [0]?
Edit2: Jak wspomnieli inni, miejsce na komentarze jest trochę za małe, aby można było je dalej omawiać. Tak czy siak:
nie zapewnia alokacji pamięci dla danej macierzy.
Jak wspomniano powyżej i zgodnie z definicją zawartą w normie, oświadczenie:
odłożył 200 kolejnych lokalizacji typu uint8_t.
Odwołując się do l_matrix przy użyciu instrukcji w postaci:
poda zawartość colno'tego elementu znalezionego w wierszu rowno.
Dzieje się tak również w przypadku, gdy podczas przechowywania obiektu jest zaangażowane jakiekolwiek dopełnienie lub przesunięcie wyrównania bajtów. Kompilator automatycznie dostosuje się do nich. Z definicji standardu C ANSI.
HTH
Twoje zdrowie,
źródło
W C99 (wspieranym przez clang i gcc) istnieje niejasna składnia do przekazywania wielowymiarowych tablic do funkcji przez odniesienie:
W przeciwieństwie do zwykłego wskaźnika, ta wskazówka dotyczy rozmiaru tablicy, teoretycznie pozwalając kompilatorowi na ostrzeżenie o przekazywaniu zbyt małej tablicy i zauważeniu oczywistego dostępu poza granicami.
Niestety, to nie naprawia,
sizeof()
a kompilatory nie wydają się jeszcze używać tych informacji, więc pozostaje ciekawostką.źródło
static 10
to pewnego rodzaju gwarancja, że obecnych jest co najmniej 10 elementów, co znowu oznacza, że rozmiar nie jest ustalony.static
tablica nie byłaby przekazywana przez odniesienie, co nie jest prawdą. Tablice i tak są przekazywane przez odwołanie. Pierwotne pytanie dotyczyło innego przypadku użycia - dostępu do elementów tablicy 2D za pomocą dodatkowego wskaźnika w ramach tej samej funkcji / przestrzeni nazw.Zawsze możesz uniknąć majstrowania przy kompilatorze, deklarując tablicę jako liniową i wykonując (row, col) w celu obliczenia indeksu tablicy samodzielnie.
i tak by to zrobił kompilator.
źródło
Podstawowa składnia wskaźnika inicjalizującego wskazującego na tablicę wielowymiarową to
type (*pointer)[1st dimension size][2nd dimension size][..] = &array_name
Podstawowa składnia jej wywołania to
Oto przykład:
Wynik to:
Zadziałało...
źródło
Możesz to zrobić w ten sposób:
źródło
Chcesz mieć wskaźnik do pierwszego elementu, więc;
źródło
Możesz również dodać przesunięcie, jeśli chcesz użyć indeksów ujemnych:
Jeśli Twój kompilator wyświetla błąd lub ostrzeżenie, możesz użyć:
źródło
c
, więc odpowiedź powinna być w tym samym języku. Proszę zwrócić uwagę na tagi.