Macierz dynamiczna - co jest czym?

0

Siemka,
tworząc macierz dynamiczną użyłem takiego czegoś, ale dwie linijki są zapożyczone z forum i zastanawia mnie co jest czym, tworzenie wygląda następująco:

int i,j,A;
int **macierza; 

A - jest tutaj rozmiarem macierzy kwadratowej.
[...]

macierza=(int**)malloc(sizeof(int*)*A);
for(i=0;i<A;i++)
macierza[i]=(int*)malloc(sizeof(int)*A);
for(i=0;i<A;i++)
{
	for(j=0;j<A;j++)
	{
		macierza[i][j]=0;
	}
} 

ale problem jest z:

macierza=(int**)malloc(sizeof(int*)*A); 

i:

 macierza[i]=(int*)malloc(sizeof(int)*A);

Co robią te elementy i czym są pojedyncze i podwójne gwiazdki?
Gdy powiedziałem profesorowi że ** to wskaźnik wskaźnika to podsumował mnie szybko "g**no prawda!",
natomiast wszyscy koledzy z informatyki mówią to samo i jak słyszą że źle to już nie wiedzą...

1

int ** to jest oczywiście wskaźnik do wskaźnika na int, czyli takie cudo które może pokazywać na element w pamięci który jest wskaźnikiem pokazującym na inta w pamięci.
int* to po prostu wskaźnik do inta, czyli takie cudo które może pokazywać na inty w pamięci.
Jeśli ktoś uważa że jest inaczej to po prostu jest niedouczony :)

0

No i wykładowca mówi mi że źle? ;/ inny kumpel podpowiedział, że może chodzić o:

-to jest rzutowanie zmiennej na zmienna bedaca tablica wskaznikow
-rzutowanie jej typu

0

No to jakie jest pytanie? Tzn o który fragment kodu?
malloc alokuje na stercie X bajtów i zwraca wskaźnik do nich jako void*, w efekcie trzeba sobie ten wskaźnik przerzutować żeby można było z niego korzystac ;]

0

Kod:

int **macierza; 
  • to wskaźnik do tablicy wskaźników na int

Kod:

macierza=(int**)malloc(sizeof(int*)*A);
  • tutaj
(int **)

to (jak napisano wyżej) rzutowanie typu z (void *) które jest zwracane przez malloc do typu (int **) w którym jest "macierza"

  • (int *) to wskaźnik na int, zamienny z tablicą intów, przykład:
int macierza[20];
int *ptr = macierza;
ptr[10] = 22;

http://ideone.com/fR3vB

Podsumowując, w kodzie masz alokowanie serii bloków pamięci, z których każdy jest wierszem finalnej tablicy.
Dzięki temu możesz potem użyć zwykłej dwuwymiarowej składni a[x][y] dla dynamicznej tablicy.

0

Pamiętam jak się uczyłem czytać typy z symfonii i Tobie też polecam. To by było tak:

int ** macierza

Od prawej do lewej:

macierza - nazwa zmiennej
* - jest wskaźnikiem na...
* - wskaźnik na
int - na typ int

Łącząc to mamy
macierza jest wskaźnikiem na wskaźnik na int. Jeżeli profesor mówi inaczej, to się nie zna.

1

Jeśli profesor jest czepialski to może warto bardziej szczegółowo:

*

Gwiazdka to operator. Dwie gwiazdki ** to dwa operatory ;) (i nic więcej!)

Gwiazdka użyta przy typie danych oznacza nowy typ danych - wskaźnik na ten typ. Czyli jeśli obok int postawimy gwiazdkę otrzymamy wskaźnik na int int*. Jeśli obok wskaźnika na int int* postawimy gwiazdkę otrzymamy wskaźnik na wskaźnik na int int** itd. Tak więc

int**

jest typem danych - wskaźnik na wskaźnik na liczbę int.

int **macierza;

Powyższa linijka to definicja zmiennej o nazwie "macierza" i typie "wskaźnik na wskaźnik na int". macierza jest przede wszystkim zmienną.

macierza=(int**)malloc(sizeof(int*)*A);

Powyższa linijka to instrukcja przypisania. Zmiennej macierza przypisywana jest wartość wyrażenia stojącego po prawej stronie operatora przypisania (=). W wyrażeniu tym (int**) (zwróć uwagę na nawiasy!) jest rzutowaniem na typ "wskaźnik na wskaźnik na int".

0
vpiotr napisał(a)

wskaźnik do tablicy wskaźników na int

Właśnie o to mu chodziło i jak stwierdził, zabrakło mu w tym dodatkowej definicji czym jest wskaźnik, ale o to jakoś nie potrafił zapytać na starcie ;/
Niemniej jednak, mam zaliczone więc dzięki wielkie ;)

0

Ale to nie jest prawda, wskaźnik to nie tablica. To jest wskaźnik do wskaźnika, jak już pisali wcześniej, może być wskaźnikiem na tablicę wskaźników.

0
Endrju napisał(a)

Ale to nie jest prawda, wskaźnik to nie tablica. To jest wskaźnik do wskaźnika, jak już pisali wcześniej, może być wskaźnikiem na tablicę wskaźników.

Składniowo masz racje, wykładowcy pewnie chodzi o wytłumaczenie "na ludzki rozum". Wskaźnik do wskaźnika na int jest sztucznym tworem, nie uważasz? Ta uznana wersja jest bardziej logiczna.

0

Gwiazdki wcale nie mówią czy coś jest tablicą czy nie. Tak więc przy pytaniu "co oznaczają te gwiazdki" nie ma mowy o żadnych tablicach.

"Składniowo" wskaźnikiem na tablicę wskaźników jest np. taki twór:

int* (*tablica)[5];

Ale jeśli takie coś:

int *x = malloc(...)

nazywać by wskaźnikiem na tablicę toint** y = malloc(...)

również będzie wskaźnikiem na tablicę.
0
adf88 napisał(a)

"Składniowo" wskaźnikiem na tablicę wskaźników jest np. taki twór:

int* (*tablica)[5];

Ale jeśli takie coś:

int *x = malloc(...)

nazywać by wskaźnikiem na tablicę toint** y = malloc(...)

również będzie wskaźnikiem na tablicę.


Gdy zapytasz kogokolwiek z programistów C/C++ co to jest (int **) to każdy odpowie Ci w pierwszej chwili, że to wskaźnik na wskaźnik na int.
I z jednej strony mają rację. Z drugiej strony "mentalnie" nie (dla mnie "nie"). 
Ale jest to zagadnienie nietechniczne, bardziej humanistyczne w tym wypadku:

1) Alokujesz serię bloków po x bajtów 
2) Adres do każdego bloku zapisujesz w osobnym bloku zbiorczym.
3) Jak napisano wyżej nazwa tablicy to inaczej wskaźnik na pierwszy jej element którego nie można zmienić (nie należy)

W podanym przykładzie nie należy zmieniać wskaźników które są przechowywane w bloku z pkt. 2
Gdybyśmy to zrobili, to doszło by do wycieku pamięci.
W związku z tym można powiedzieć, że (int **) to wskaźnik do tablicy intów lub wręcz tablica tablic intów (co zresztą widać w zapisie poniżej):
```cpp
macierza[i][j]=0;

Mieszanie do tego jeszcze konstrukcji typu:

int* (*tablica)[5];

to wg mnie niepotrzebna komplikacja tematu.

Oczywiście to tylko nazewnictwo i trochę wyższy poziom abstrakcji (poza syntaktyczny), więc dziwię się, że nauczyciel się tego czepiał. Dla mnie to taki szczegół na który nie zwróciłbym nawet uwagi gdybyście o tym nie zaczęli dyskutować.

0
vpiotr napisał(a)

Gdy zapytasz kogokolwiek z programistów C/C++ co to jest (int **) to każdy odpowie Ci w pierwszej chwili, że to wskaźnik na wskaźnik na int.
I z jednej strony mają rację. Z drugiej strony "mentalnie" nie (dla mnie "nie").

Ma rację mentalnie, nie mentalnie whatever. Wszystko zależy od użycia, przecież może pokazywać na dokładnie jeden wskaźnik, który to pokazuje na dokładnie jeden int.

vpiotr napisał(a)
  1. Jak napisano wyżej nazwa tablicy to inaczej wskaźnik na pierwszy jej element którego nie można zmienić (nie należy)

Nie, to uproszczenie pokutuje i później się mści. Nazwa tablicy wcale nie zachowuje się jak (stały) wskaźnik.

#include <iostream>

using namespace std;

int main()
{
	int tab[5];
	int *wsk = tab;

	cout << "tab:\t" << reinterpret_cast<int>(tab) << endl;
	cout << "&tab:\t" << reinterpret_cast<int>(&tab) << endl << endl;

	cout << "wsk:\t" << reinterpret_cast<int>(wsk) << endl;
	cout << "&wsk:\t" << reinterpret_cast<int>(&wsk) << endl << endl;

	cout << "sizeof tab: " << sizeof(tab) << endl;
	cout << "sizeof wsk: " << sizeof(wsk) << endl;

	return 0;
}

przykładowy output:

tab:    4258852
&tab:   4258852  //gdyby zmienna tab była wskaźnikiem do int to pokazuje na siebie czyli na int* (no to coś nie tak) ;)

wsk:    4258852
&wsk:   4258840

sizeof tab: 20
sizeof wsk: 4

1 użytkowników online, w tym zalogowanych: 0, gości: 1