Czy źle jest odwoływać się do elementów tablicy dostępu za pomocą arytmetyki wskaźnika zamiast operatora []?

12

Właśnie zacząłem uczyć się programowania w C i aby lepiej zrozumieć wskaźniki i tablice, próbowałem odwoływać się do elementów tablicy bez tworzenia żadnego wskaźnika:

for(k1 = 0; k1 < ROW; k1++){
    for(k2 = 0; k2 < COLUMN; k2++){

        array[k1][k2] = k1*COLUMN + k2 + 1;

        printf("[%d][%d] = %d\n", k1, k2, *(array[k1] + k2));

    }
}

Cały kod kompiluje się i działa bezbłędnie.

Wyobrażam sobie, że tworzenie wskaźnika dla każdej tablicy w dużym kodzie źródłowym wydaje się wysoce nieefektywne.

Czy więc zamiast posiadania adresu tablicy zapisanego i pobranego za pomocą wskaźnika, złą praktyką programistyczną jest bezpośrednie używanie adresu tablicy, jak pokazano powyżej?

Niko Gambt
źródło
Użycie printf "[%d][%d] = %d\n", k1, k2, array[k1] [k2]));pozwoliłoby uniknąć arytmetyki wskaźnika i jest łatwiejsze do zrozumienia.
Kasper van den Berg
1
Haha, masz mnie. Zrobiłem to tylko jako eksperyment, aby lepiej zrozumieć, w jaki sposób działają wskaźniki i tablice.
Niko Gambt,
Arytmetyka wskaźnika jest w rzeczywistości około 30% szybsza niż przy użyciu indeksów tablicowych.
Andy

Odpowiedzi:

16

Jest „zły” tylko w takim stopniu, w jakim jest mniej czytelny. a[x]to to samo, co *(a+x), więc nie ma różnicy w wydajności lub zachowaniu (w rzeczywistości x[a]będzie działać). Po prostu a[x]jest to dla nas, ludzi, o wiele bardziej intuicyjne.

Ale to nie znaczy, że czytelność nie jest wielkim problemem. Aby zobaczyć, jak duży, zastanów się, jak „odczytałbyś” te dwa wyrażenia, jeśli zobaczysz je w kodzie:

  • *(a+x)= "Rzecz wskazana przez sumę wskaźnika ai liczby całkowitej x"
  • a[x]= "The xth członek tablicy a"

Podobnie, gdy trzeba odwołać się do adresu elementu tablicy:

  • (a+x)= "Suma wskaźnika ai liczby całkowitej x"
  • &a[x]= "Adres xth członka tablicy a"

W większości przypadków []wersje są po prostu łatwiejsze do zrozumienia, gdy patrzysz na nietrywialny kod działający na kilku różnych tablicach (zwłaszcza tablicach). Właśnie dlatego []operator istnieje.

PS Robienie tego rodzaju rzeczy wyłącznie jako ćwiczenia edukacyjne jest bardzo dobrym pomysłem. Ważne jest, aby zrozumieć, że tablice są tak naprawdę wskaźnikami i przesunięciami.

Ixrec
źródło
To. Dziękuję Ci. Właśnie sobie uświadomiłem, że tak naprawdę chcę wiedzieć, czy trzeba odwoływać się do adresu dowolnego elementu tablicy, a następnie odwoływać się do adresu innego elementu, a następnie innego, czy nadal jest źle NIE należy używać wskaźnika, a zamiast tego używać tablicy bezpośrednio tak, jak ja? To moje pytanie było naprawdę trudne do ustalenia, dlatego nie wpisałem go w OP. W każdym razie, skoro nie zapytałem, twoja odpowiedź jest zadowalająca.
Niko Gambt,
1
@NikoGambt Zawsze możesz zadać kolejne pytanie =)
Ixrec
1
Co masz na myśli tylko w zakresie ...? Istotą języków programowania jest ułatwienie ludziom czytania kodu. Gdybyśmy się tym nie przejmowali, wszyscy pisalibyśmy kody szesnastkowe.
Solomon Slow
2
Tablice nie są wskaźnikami, tylko rozpadem do wskaźników pośrednio.
CodesInChaos
4

Tak, to zła praktyka, ale nie z powodów nieefektywności.

Operator macierzy używa arytmetycznej wskazówki pod maską, dzięki czemu są one równie wydajne.

Problem z arytmetyką wskaźnika polega na tym, że jest on bardzo podatny na błędy i trudniejszy do odczytania.

Ogólna zasada: nie używaj arytmetyki wskaźnika, chyba że musisz.

użytkownik694733
źródło
1
Skoro i tak używa arytmetyki wskaźników, czy to nie oznacza, że ​​wskaźniki są równie podatne na błędy i trudne do odczytania?
Niko Gambt,
1
Kompilatory @NikoGambt są całkiem dobre w arytmice wskaźników pod maską i rzadko popełniają „błędy”; to programiści popełniają błędy w wyniku nieprzyjemnych błędów.
Kasper van den Berg,
@KaspervandenBerg Tak, zgadzam się. Interesują mnie jednak błędy popełniane przez programistów i po prostu nie jestem pewien, czy robienie tego, co zrobiłem powyżej, jest złą praktyką programistyczną, w przypadkach, w których trzeba odwoływać się do adresu tablicy , jeśli takie przypadki istnieją.
Niko Gambt,
1
@NikoGambt Pisanie czegoś, co jest mniej czytelne dla celów wydajnościowych, prawie zawsze jest złym ćwiczeniem, ale pisanie czegoś, co jest mniej czytelne bez żadnego zysku, jest jednoznacznie złą praktyką.
Neil,
@Neil Mój mały eksperyment nie jest bezcelowy. Dzięki temu dowiedziałem się, że kompilator wydaje się tworzyć tablicę wskaźników odnoszącą się do tablicy wielowymiarowej. Ponieważ jednak powiedziałeś, że czytelność jest ważniejsza niż wydajność, myślę, że nadal jest to zła praktyka programowania.
Niko Gambt,
0

Ochłódź naukę c, właśnie odkryłeś jedną z c małych łamigłówek. Nie wykonujesz arytmetyki wskaźników na tablicy, ale tablicę wskaźników. Wykonywanie arytmetyki wskaźnika na tablicach nie jest możliwe. Tablica rozpada się na wskaźnik, ale sama nie jest wskaźnikiem. To, co masz (patrz komentarz cmastera), to

int *array[]; //This is a array to pointers of type *int. 

array[k1] + k2; //This is pointer arithmetic on one pointer stored in the array  

Dereferencja tego wskaźnika daje wartość, którą wskazuje właśnie obliczony wskaźnik. Generalnie nie ma sensu robić tego, co robisz. Ale możesz linearyzować tablicę, a następnie krok w nią, tak jak to.

int array[y_dim*x_dim]; 
int index = x_dim*y + x; 
array[index]; //Gives you the element in x, y!

Krok jej to x_dim. Mam nadzieję, że moja odpowiedź jest wyjaśniona!

fhtuft
źródło
Z PO nie jest jasne, czy używa int* array[ROW];an int array[ROW][COLUMN];, czy an int (*array)[COLUMN];. Każda z tych trzech definicji może być używana z kodem w PO.
cmaster
tak, jestem tego świadomy, moja odpowiedź jest nieco niezdarna w tej kwestii. Poprawiam to, dzięki!
fhtuft