Dynamiczna tablica wskaźników, 'szachownica', zad. z 'Symfonii'

0

Witam.

Mam problem z następującym zadaniem pochodzącym z "Symfonii C++" (ciut skrócę treść):
Do wskaźnika o nazwie szachownica przypisz adres tablicy elementów typu char o rozmiarze takim, by zajęła **88sizeof(long)bajtów pamięci.
Wiemy, że pola szachownicy oznacza się symbolami od A1do H8. Dla wygody przyjmijmy, że indeks będący cyfrą zmienia się tu częściej niż indeks będący literą <- tego zdania nie rozumiem...
Na tak zdobytym obszarze tworzone będą obiekty typu long w sposób następujący: program pyta użytkownika o pole szachownicy (najpierw o literę, później o cyfrę), a program tworzy obiekt umiejscawiając go na odpowiedniej pozycji szachownicy. Np. dla 4F będzie miejsce o adresie &szachownica[172], bo ((5*8)+3)4=172 **<- skąd w ogóle wzięło się tutaj ostatnie mnożenie przez 4? Wydaje mi się, że powinno być 58+3=43, tj. &szachownica[43] ...

W rezultacie stworzony zostanie obiekt, którego adres zapamiętaj w pierwszym wolnym elemencie przygotowanej w tym celu tablicy wskaźników o nazwie figury.
Do obiektu tego wpisz wartość, która będzie liczbowo równa indeksowi tablicy szachownica, pod którym znajduje się ten obiekt (czyli 172 wg autora, a 43 wg mnie).
Jeśli użytkownik poda symbol literowy 'X' - oznacza to koniec pętli dodawania nowych obiektów. Wypisz wtedy na ekranie kolejno wartości wszystkich tak stworzonych obiektów. Zlikwiduj obiekty i zakończ program.

Siłowałem się z tym zadaniem i oto, co udało mi się zrobić:

 #include <iostream>
using namespace std;
// A ma w kodzie ASCII wartosc 65
int main()
{
    char *szachownica = new char[8*8*sizeof(long)];  
    cout << "Ile figur chcesz stworzyc?: ";
    unsigned short ile;
    cin >> ile;  
    long int *figury[ile];  
    
    for(int i=0; i<ile; i++)
    {
            int i=0;         
            cout << "Na jaim polu szachownicy mam umiescic nowy obiekt typu long?"
            "\nPodaj symbol literowy: ";
            char symbol;
            cin >> symbol;
            if(symbol == 'X' || symbol == 'x') break;            
            cout << "\nPodaj symbol cyfrowy: ";
            unsigned short cyfra;
            cin >> cyfra;
            
            char *gdzie = &szachownica[0];
            figury[i] = new (gdzie + (symbol - 65) * 8 + cyfra) long;
            *figury[i] = (symbol - 65) * 8 + cyfra;
    }      
    for(int i=0; i<ile; i++)
    {
            cout << *figury[i] << endl;
    }    
    delete [] szachownica;
    delete [] figury;   
    system("pause");
    return 0;
}

Nie wiem dlaczego przy próbie stworzenia więcej niż jednej 'figury' (już podczas pracy programu), gdy ma dojść do ostatniej pętli pokazującej wyniki, program się zawiesza i Windows mi jakiś błąd wyrzuca.
Druga sprawa - jak widać nie wiem jak stworzyć tablicę figur "na tyle dynamiczną", żeby nie było potrzeby podawania liczby figur na początku i wtedy możliwość zakończenia będzie wyłącznie po podaniu znaku 'X'.
Oczywiście proszę również o wskazaniu błędów i sugestię ich naprawienia.

(Aha, mam nadzieję, że odpowiedni dział wybrałem, aczkolwiek jestem 'newbie', więc być może tam powinienem ten temat założyć...)

0

"Na tyle dynamiczny" będzie np std::vector.

    cin >> ile;  
    long int *figury[ile];

To czego tutaj użyłeś to VLA z C99 - w C++ jest to nieprawidłowa konstrukcja. Działa w g++ (GCC/MinGW) jako rozszerzenie GNU. ;)
Nie powinieneś takiej tablicy zwalniać przez delete.

for(int i=0; i<ile; i++)
    {
            int i=0;   

Przesłaniasz sobie licznik pętli i kolejną zmienną lokalną o tej samej nazwie.
Przez co masz wyciek pamięci, bo

figury[i] = new (gdzie + (symbol - 65) * 8 + cyfra) long;

wygląda zawsze tak: figury[0] = new (gdzie + (symbol - 65) * 8 + cyfra) long;

Dlatego w drugiej pętli jak tylko iterator będzie równał się 1... No co się tam dzieje? Jaką wartość ma `figury[1]`? ;)
Poza tym - nawet gdybyś poprawnie alokował, to nigdy nie zwalniasz pamięci przypisanej elementom tablicy `figury`.

Spróbuj to skompilować z flagami: `-pedantic -std=c++03 -Wall -Wextra -Wshadow` - chociaż i bez żadnych flag na g++ 4.6.0 dostaję ostrzeżenie i błąd (kod się nie kompiluje, bo nie wiadomo co to jest `system()`).
Po próbie kompilacji:

cpp_main.cpp: In function 'int main()':
cpp_main.cpp25: warning: ISO C++ forbids variable length array 'figury' [-Wvla]
cpp_main.cpp17: warning: declaration of 'i' shadows a previous local [-Wshadow]
cpp_main.cpp13: warning: shadowed declaration is here [-Wshadow]
cpp_main.cpp15: warning: deleting array 'long int* figury [(((unsigned int)(((int)ile) + -0x000000001)) + 1)]' [enabled by default]
cpp_main.cpp19: error: 'system' was not declared in this scope


edit: A zamiast magicznej liczby `65` nie lepiej użyć po prostu `'A'`, a zamiast magicznej liczby `8` stałej nazwanej np `const unsigned rozmiar_szachownicy = 8;`? ;)
0

Sam ucze się z symfoni ale do tego zadania jeszcze nie doszedłem ;p

Wydaje mi się że "(5*8)+3)*4=172 "
5 = litera w kolejności alfabetycznej
8 = pole w formie liczby
te 3 to nie wiem;d
a *4 bo "long" to 4 zajmuje.... sizeof(long) = 4.

0

Skoro już poprawiasz to poprawiaj, co to ma niby być? Wywołanie przeciążonego operatora new, a gdzie jest przeciążony?
figury[0] = new (gdzie + (symbol - 65) * 8 + cyfra) long;
powinno być:
figury[0] = new long(gdzie + (symbol - 65) * 8 + cyfra);

0

i wtedy możliwość zakończenia będzie wyłącznie po podaniu znaku 'X'.

  for(;;){
            int i=0;         
            cout << "Na jaim polu szachownicy mam umiescic nowy obiekt typu long?"
            "\nPodaj symbol literowy: ";
            char symbol;
            cin >> symbol;
            if(symbol == 'X' || symbol == 'x') break;
...
}

Pętla skończy się tylko w wypadku podania X lub x

0
patry93 napisał(a)

Wiemy, że pola szachownicy oznacza się symbolami od A1do H8. Dla wygody przyjmijmy, że indeks będący cyfrą zmienia się tu częściej niż indeks będący literą <- tego zdania nie rozumiem...
Zapewne chodzi tu o właściwe dobranie kolejności indeksowania, by dostęp do danych był optymalny, czyli masz zdecydować czy indeksowanie będzie tab[A][1] czy tab[1][A].
Jest parę dziwnych sformułowań w tym zadaniu, na pewno je dobrze tutaj przepisałeś (Grębosz jest dość konkretnym i zrozumiałym autorem, więc wątpię byś go cytował dosłownie)?

0
Azrael_Valedhel napisał(a)

"Na tyle dynamiczny" będzie np std::vector.

Ok, aczkolwiek do momentu tego zadania w książce (tj. Symfonii) nie pojawiła się żadna wzmianka o czymś takim jak 'vector', więc pewnie można to ominąć i na tym chciałbym się skupić (oczywiście w żadnym wypadku nie neguję takiej alternatywnej metody, jedynie odkładam na później).

Azrael_Valedhel napisał(a)

To czego tutaj użyłeś to VLA z C99 - w C++ jest to nieprawidłowa konstrukcja.

Nie widzę jak ją zamienić na prawidłową niestety...

Azrael_Valedhel napisał(a)

Nie powinieneś takiej tablicy zwalniać przez delete.

Dlaczego? I w jaki sposób w takim razie usunąć taką tablicę?

Azrael_Valedhel napisał(a)

Przesłaniasz sobie licznik pętli i kolejną zmienną lokalną o tej samej nazwie.

Aj, faktycznie, bardzo głupi błąd i niedopatrzenie. Po usunięciu definicji zmiennej i wewnątrz pętli program już się nie zawiesza.

Azrael_Valedhel napisał(a)

Poza tym - nawet gdybyś poprawnie alokował, to nigdy nie zwalniasz pamięci przypisanej elementom tablicy figury.

Hm, tutaj pytanie chyba jest równoważne z postawionym w tym poście jako trzecie.

Azrael_Valedhel napisał(a)

Spróbuj to skompilować z flagami: -pedantic -std=c++03 -Wall -Wextra -Wshadow

Nie wiem, co to są flagi, ale postaram się poczytać.

Azrael_Valedhel napisał(a)

edit: A zamiast magicznej liczby 65 nie lepiej użyć po prostu 'A', a zamiast magicznej liczby 8 stałej nazwanej np const unsigned rozmiar_szachownicy = 8;? ;)

Co do 65 - masz na myśli miejsce, gdzie ustawiam wskaźnik figury[i] ? Co do magicznej 8 - tę instrukcję 88... podał sam autor, ale pewnie istotnie lepiej mieć to w const ;)

ace4ur napisał(a)

a *4 bo "long" to 4 zajmuje.... sizeof(long) = 4.

Zgadza się, że long (przynajmniej zazwyczaj) zajmuje 4 bajty, ale w Twojej wypowiedzi nie ma (albo nie widzę, wtedy proszę o wytłumaczenie) uzasadnienia, dlaczego owe mnożenie przez 4 jest słuszne. Inaczej - gdyby autor tego nie napisał, to czy pomyślałbyś w ten sam sposób i potrafił uzasadnić, dlaczego? Osobiście ja tego nie rozumiem.

MarekR22 napisał(a)

Zapewne chodzi tu o właściwe dobranie kolejności indeksowania, by dostęp do danych był optymalny, czyli masz zdecydować czy indeksowanie będzie tab[A][1] czy tab[1][A].

W takim razie powinienem trzymać wprowadzone wartości w tablicy dwuwymiarowej?

Hm, właściwie jedno zdanie może odbiegać (choć nie powinno) od oryginału (chciałem przeinaczyć "na lepsze", być może nie wyszło), a mianowicie to:
Do obiektu tego wpisz wartość, która będzie liczbowo równa indeksowi tablicy szachownica, pod którym znajduje się ten obiekt (czyli 172 wg autora, a 43 wg mnie).
W oryginale brzmi:
do obiektu tego wpisz wartość, która będzie przypominała jego adres (w tym przypadku liczbę 172).

0
patry93 napisał(a)
ace4ur napisał(a)

a *4 bo "long" to 4 zajmuje.... sizeof(long) = 4.

Zgadza się, że long (przynajmniej zazwyczaj) zajmuje 4 bajty, ale w Twojej wypowiedzi nie ma (albo nie widzę, wtedy proszę o wytłumaczenie) uzasadnienia, dlaczego owe mnożenie przez 4 jest słuszne. Inaczej - gdyby autor tego nie napisał, to czy pomyślałbyś w ten sam sposób i potrafił uzasadnić, dlaczego? Osobiście ja tego nie rozumiem.

Zapewne autor przyjął, że nie zrzutniesz sobie wskaźnika do zaalokowanej pamięci na long i będziesz indeksował po bajtach. Na 32bit x86 long ma 4 bajty, zatem aby przeskoczyć do właściwej "komórki" pamięci po bajtach, musisz mnożyć indeks przez 4. Osobiście albo bym rzutnął wynik malloc() na long*, albo mnożył przez sizeof(long).

0

Kumashiro - faktycznie, brzmi rozsądnie.
Co do pytań z mojego poprzedniego postu - nadal aktualne.

0
patry93 napisał(a)

Ok, aczkolwiek do momentu tego zadania w książce (tj. Symfonii) nie pojawiła się żadna wzmianka o czymś takim jak 'vector', więc pewnie można to ominąć i na tym chciałbym się skupić (oczywiście w żadnym wypadku nie neguję takiej alternatywnej metody, jedynie odkładam na później).

i się nie pojawi. Symfonia to dobra książka na początek ale nie wystarczająca, omija tam autor mnóstwo ciekawych ciekawostek związanych ze standardem.

patry93 napisał(a)

Nie widzę jak ją zamienić na prawidłową niestety...
long *figury = new long[ile]

patry93 napisał(a)

Dlaczego? I w jaki sposób w takim razie usunąć taką tablicę?
przez delete zwalnia się tylko to co zarezerwowane jest przez new. Wszystko inne jest tworzone na stosie i usuwa się samo po wyjsicu z bloku / po zakończeniu programu.

patry93 napisał(a)

Azrael_Valedhel:
Poza tym - nawet gdybyś poprawnie alokował, to nigdy nie zwalniasz pamięci przypisanej elementom tablicy figury.

Hm, tutaj pytanie chyba jest równoważne z postawionym w tym poście jako trzecie.
ponieważ musisz najpierw każdą z tablic utworzonych usunąć

for(int i = 0; i < ile; i++)
delete [] figury[i];
// a na koniec usunąć samą tablicę
delete [] figury;
patry93 napisał(a)

Nie wiem, co to są flagi, ale postaram się poczytać.
Flagi pojawią Ci się dopiero gdzieś pod koniec Symfonii, prawdę mówiąc sam nie wiem o co tu chodziło Azreal'owi

patry93 napisał(a)

Co do 65 - masz na myśli miejsce, gdzie ustawiam wskaźnik figury[i] ? Co do magicznej 8 - tę instrukcję 88... podał sam autor, ale pewnie istotnie lepiej mieć to w const
Chodzi o to, że zamiast używania magicznych liczb z ASCII raczej stosuje się ich char'owe odpowiedniki - jeżeli chcesz z '1' zrobić 1, to po prostu robisz '1' - '0', tak samo tutaj chcesz z 'F' zrobić 5, to po prostu symbol - 'A'

patry93 napisał(a)

Zgadza się, że long (przynajmniej zazwyczaj) zajmuje 4 bajty, ale w Twojej wypowiedzi nie ma (albo nie widzę, wtedy proszę o wytłumaczenie) uzasadnienia, dlaczego owe mnożenie przez 4 jest słuszne. Inaczej - gdyby autor tego nie napisał, to czy pomyślałbyś w ten sam sposób i potrafił uzasadnić, dlaczego? Osobiście ja tego nie rozumiem.
nie chcialo mi sie czytac postu dodanego przez kolegę, dlatego tylko stwierdze, ze nie nalezy ufac za bardzo slowu "zazwyczaj" bo long moze rowniez zajmowac i wiecej bajtow dlatego powinno sie uzywac sizeof(long) po prostu

patry93 napisał(a)

W takim razie powinienem trzymać wprowadzone wartości w tablicy dwuwymiarowej?
skoro long *figury = new long[ile] to jest tablica wskaznikow, to tworzac pokolei kolejne tablice

for(int i = 0; i < ile; i++)
    figury[i] = new long[ilestam];

to ile w tym przypadku jest tu figury[ile][ilestam];

0
MJay napisał(a)

long *figury = new long[ile]

Sprawdźcie proszę, czy dobrze to zrozumiałem: otóż zapis ten jest równoważny takiemu: *long (figury) = new long[ile]
i lewa strona to definicja wskaźnika figury mogącego pokazywać na wskaźniki pokazujące na long, a prawa to stworzenie tablicy ile-elementowej przechowującej wskaźniki pokazujące na long?

MJay napisał(a)

ponieważ musisz najpierw każdą z tablic utworzonych usunąć

for(int i = 0; i < ile; i++)
delete [] figury[i];
// a na koniec usunąć samą tablicę
delete [] figury;

Czy to oznacza, że dla sensownego i (od 0 do ile-1) figury[i] jest tablicą i jednocześnie elementem tablicy figury? Czy wobec tego, skoro figury są tablicą wskaźników, tutaj te wskaźniki traktowane są jak tablice? (mam nadzieję, że nie fandzolę tutaj mocnych głupot... ; ))

MJay napisał(a)

Chodzi o to, że zamiast używania magicznych liczb z ASCII raczej stosuje się ich char'owe odpowiedniki - jeżeli chcesz z '1' zrobić 1, to [błąd ortograficzny] robisz '1' - '0', tak samo tutaj chcesz z 'F' zrobić 5, to [błąd ortograficzny] symbol - 'A'

Co się kryje pod "błąd ortograficzny"? :)

MJay napisał(a)

to ile w tym przypadku jest tu figury[ile][ilestam];

Niepewnie odpowiem, że o ilestam*sizeof(long) bitów dalej niż pokazuje wskaźnik figury[ile] ?

0
patry93 napisał(a)

Sprawdźcie proszę, czy dobrze to zrozumiałem: otóż zapis ten jest równoważny takiemu: long *(figury) = new long[ile]
i lewa strona to definicja wskaźnika figury mogącego pokazywać na wskaźniki pokazujące na long, a prawa to stworzenie tablicy ile-elementowej przechowującej wskaźniki pokazujące na long?
Tak.

patry93 napisał(a)

Czy to oznacza, że dla sensownego i (od 0 do ile-1) figury[i] jest tablicą i jednocześnie elementem tablicy figury? Czy wobec tego, skoro figury są tablicą wskaźników, tutaj te wskaźniki traktowane są jak tablice? (mam nadzieję, że nie fandzolę tutaj mocnych głupot... ; ))
Tak. To jest na tej samej zasadzie co byłoby long* tab = new long[n]; tylko teraz tab[i] zamiast tab tam jest.

patry93 napisał(a)

Co się kryje pod "błąd ortograficzny"?
Jak najedziesz kursorem na napis "bląd ortograficzny" to pojawi Ci się co pod nim się kryje, nie mogę zapamiętać, że "po prostu" pisze się oddzielnie ;]

patry93 napisał(a)

Niepewnie odpowiem, że o ilestam*sizeof(long) bitów dalej niż pokazuje wskaźnik figury[ile] ?
Niestety tak dobrze nie ma, tak jest w przypadku tworzenia tablic wielowymiarowych natomiast niedynamicznie. Dynamicznie możesz to zrobić jeżeli sam wstawiasz gdzie ma się utworzyć dana tablica. W przeciwnym wypadku kompilator szuka sobie odpowiednio wielkiej przestrzeni w pamięci i tam tworzy tablice. Kazde kolejne elementy sa jednak faktycznie co sizeof(long) porownaj z rzuconego programu adresy.

#include <iostream>
 
using namespace std;
 
int main()
{
	long** tab = new long*[3];
	for(int i = 0; i < 3; i++)
		tab[i] = new long[2];

	for(int i = 0; i < 3; i++)
	{
		for(int j = 0; j < 2; j++)
			cout << reinterpret_cast<int>(&tab[i][j]) << endl;
		cout << "Porownaj z elementem 2 linijki wyzej oraz z elementem pod kolejnym napisem:\n"
			   << reinterpret_cast<int>(&tab[i]) << endl;
	}
	// kasowanie tablic
	for(int i = 0; i < 3; i++)
		delete [] tab[i];
	delete [] tab;

	system("pause");
    return 0;
}
 
1

Ogólnie mieszasz trochę pojęcie tablicy i wskaźnika. Jeżeli w kodzie masz wyrażenie w stylu

int tablica[10]; 

to tablica jest nazwą identyfikującą obszar w pamięci (dokładniej na stosie) na 10 intów. W przypadku tablic dynamicznych wyrażenie int* tab_dyn=new int[10]; rezerwuje w pamięci (sterta) miejsce na dziesięć intów, do którego wskaźnik przechowywany jest na stosie, w kodzie źródłowym nazywany tab_dyn. W C wyrażenie a[b] dla wskaźników jest równoważne wyrażeniu *(a+b), możesz to sprawdzić pisząc przykładowo wyrażenie 3[tablica]. Wracając do pytania figury[i] jest wskaźnikiem, a nie tablicą (tzn. jest inna niż tablica statyczna). W większości przypadków możesz używać jednak tablicy statycznej jako wskaźnika, np. przy wywoływaniu funkcji przekazywany jest po prostu jej adres.

0

Można by tylko dodać, że to co mówiłeś, że:

int tab[3][2];
&tab[0][2] == &tab[1][0];

bo w C++ tablice są układane w rzędzie. Ale tworzenie tablicy dynamicznej działa na tej zasadzie;

int *tab[3]; /* to jest tablica wskaźników na int, ona ma swoje miejsce w pamięci*/
int tab1[2]; /* to jest tablica intów ma swoje miejsce w pamięci powiedzmy 50 bajtów dalej*/
int tab2[2]; /* druga tablica intów i ma swoje miejsce w pamięci powiedzmy 4 bajty dalej niż tab1*/
int tab3[2]; /* trzecia tablica intów i ma swoje miejsce w pamięci powiedzmy 212 bajtów dalej niż tab2*/
// a teraz
tab[0] = tab1;
tab[1] = tab2;
tab[2] = tab3;
0

Można by tylko dodać, że to co mówiłeś, że:
int tab[3][2];
&tab[0][2] == &tab[1][0];
bo w C++ tablice są układane w rzędzie.

Ten wartunek może być spełniony w Delphi, ale w C/C++ wcale nie musi. To porównanie sprowadza się do wyrażenia

*tab + 2 == *(tab+1)

i może, ale nie musi być spełnione. Tablica int tab[3][2] jest tablicą tablic, a nie prawdziwą tablicą 2-wymiarową.

0

Wydaje mi się, że na wykładzie z C koleś coś takiego wspominał, ale skoro tak mówicie to ok

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