W projekcie ktoś przesunął tę linię:
double (*e)[n+1] = malloc((n+1) * sizeof(*e));
Co rzekomo tworzy dwuwymiarową tablicę podwójnych liczb (n + 1) * (n + 1).
Podobno mówię, ponieważ do tej pory nikt, kogo zapytałem, nie mógł mi powiedzieć, co to dokładnie robi, skąd się wzięło ani dlaczego powinno działać (co podobno działa, ale jeszcze tego nie kupuję).
Być może brakuje mi czegoś oczywistego, ale byłbym wdzięczny, gdyby ktoś mógł mi wyjaśnić powyższą linijkę. Ponieważ osobiście czułbym się znacznie lepiej, gdybyśmy użyli czegoś, co naprawdę rozumiemy.
c
arrays
multidimensional-array
malloc
allocation
Użytkownik1291
źródło
źródło
n+1
ale zamiast tegodouble (*e)[rows] = malloc(columns * sizeof *e);
Odpowiedzi:
Zmienna
e
jest wskaźnikiem do tablicyn + 1
elementów typudouble
.Użycie operatora wyłuskiwania on
e
daje typ bazowy,e
którym jest „tablican + 1
elementów typudouble
”.malloc
Połączenia wystarczy wykonuje baza-type
(jak opisano powyżej) i otrzymuje swój rozmiar, namnaża się przez ton + 1
, że wielkość i przechodzącą wmalloc
funkcji. Zasadniczo przydzielanie tablicyn + 1
tablicn + 1
elementówdouble
.źródło
sizeof(*e)
jest odpowiednikiemsizeof(double [n + 1])
. Pomnóż to przez,n + 1
a dostaniesz wystarczająco.n+1
oba wymiary, wynik będzie kwadratowy. Jeśli to zrobiszdouble (*e)[cols] = malloc(rows * sizeof(*e));
, wynik będzie miał dowolną określoną liczbę wierszy i kolumn.int rows = n+1
iint cols = n+1
. Boże, chroń nas przed sprytnym kodem.Jest to typowy sposób dynamicznego przydzielania tablic 2D.
e
jest wskaźnikiem tablicy do tablicy typudouble [n+1]
.sizeof(*e)
dlatego podaje typ typu wskazanego, czyli rozmiar jednejdouble [n+1]
tablicy.n+1
takie tablice.e
aby wskazywał na pierwszą tablicę w tej tablicy tablic.e
jakoe[i][j]
dostępu do poszczególnych elementów w tablicy 2D.Osobiście uważam, że ten styl jest znacznie łatwiejszy do odczytania:
źródło
ptr = malloc(sizeof *ptr * count)
styl.malloc(row*col*sizeof(double))
występuje, gdyrow*col*sizeof()
przepełnienia, ale tak się niesizeof()*row*col
dzieje. (np. row, col areint
)sizeof *e * (n+1)
łatwiejsze w utrzymaniu; Jeśli kiedykolwiek zdecydujesz się zmienić typ podstawowy (oddouble
dolong double
, na przykład), a potem tylko trzeba zmienić deklaracjęe
; nie musisz modyfikowaćsizeof
wyrażenia wmalloc
wywołaniu (co oszczędza czas i chroni Cię przed zmianą w jednym miejscu, ale nie w drugim).sizeof *e
zawsze poda odpowiedni rozmiar.Ten idiom naturalnie wypada z alokacji tablicy 1D. Zacznijmy od przydzielenia tablicy 1D dowolnego typu
T
:Proste, prawda? Wyrażenie
*p
ma typT
, więcsizeof *p
daje taki sam wynik jaksizeof (T)
, więc mamy wystarczająco dużo miejsca alokacji dlaN
-elementowe tablicyT
. Dotyczy to każdego typuT
.Teraz
T
zastąpmy typem tablicowym, takim jakR [10]
. Wtedy staje się nasz przydziałSemantyka tutaj jest dokładnie taka sama, jak w przypadku metody alokacji 1D; zmienił się tylko typ
p
. ZamiastT *
tego jest terazR (*)[10]
. Wyrażenie*p
ma typ,T
który jest typemR [10]
, więcsizeof *p
jest równoważne temu,sizeof (T)
które jest równoważnesizeof (R [10])
. Więc przydzielamy wystarczającą ilość miejsca na tablicęN
by10
elementuR
.Jeśli chcemy, możemy pójść jeszcze dalej; przypuśćmy, że
R
jest typem tablicowymint [5]
. Zastąp toR
i otrzymamyTa sama umowa -
sizeof *p
jest taka sama jaksizeof (int [10][5])
i kończymy przydzielanie ciągłego kawałka pamięci wystarczająco dużej, aby pomieścić tablicęN
by10
by .5
int
Więc to jest strona alokacji; a co ze stroną dostępową?
Pamiętaj, że
[]
operacja z indeksem dolnym jest zdefiniowana w kategoriach arytmetyki wskaźnika:a[i]
jest zdefiniowana jako*(a + i)
1 . W ten[]
sposób operator indeksu niejawnie odwołuje się do wskaźnika. Jeślip
jest wskaźnikiem doT
, możesz uzyskać dostęp do wskazanej wartości albo przez jawne wyłuskiwanie za pomocą*
operatora jednoargumentowego :lub korzystając z
[]
operatora indeksu:Tak więc, jeśli
p
wskazuje na pierwszy element tablicy , możesz uzyskać dostęp do dowolnego elementu tej tablicy, używając indeksu na wskaźnikup
:Teraz powtórzmy naszą operację podstawienia i zastąpmy
T
typem tablicyR [10]
:Jedna natychmiast widoczna różnica; jawnie usuwamy odwołanie
p
przed zastosowaniem operatora indeksu dolnego. Nie chcemy indeksować dop
, chcemy indeksować do tego, cop
wskazuje na (w tym przypadku tablicęarr[0]
). Ponieważ jednoargumentowy*
ma niższy priorytet niż[]
operator indeksu dolnego , musimy użyć nawiasów, aby jawnie grupowaćp
z*
. Ale pamiętajcie z góry, że*p
to to samo cop[0]
, więc możemy to zastąpićLub tylko
Tak więc, jeśli
p
wskazuje tablicę 2D, możemy indeksować do tej tablicy w następującyp
sposób:Biorąc to do tych samych wniosków, jak powyżej, zastępując
R
wint [5]
:Działa to tak samo, jeśli
p
wskazuje na zwykłą tablicę lub jeśli wskazuje na przydzieloną pamięćmalloc
.Ten idiom ma następujące zalety:
free
. Ponownie, nie jest to prawdą w przypadku fragmentarycznej metody alokacji, w której trzeba cofnąć przydział każdego z nich,arr[i]
zanim będzie można zwolnićarr
.Czasami preferowana jest metoda alokacji fragmentarycznej, na przykład gdy sterta jest mocno pofragmentowana i nie można przydzielić pamięci jako ciągłego fragmentu lub chcesz przydzielić tablicę „postrzępioną”, w której każdy wiersz może mieć inną długość. Ale ogólnie jest to lepsza droga.
1. Pamiętaj, że tablice nie są wskaźnikami - zamiast tego wyrażenia tablicowe są w razie potrzeby konwertowane na wyrażenia wskaźnikowe.
źródło