Dynamiczna tablica bez new i delete

0

Cześć, przeglądałem ćwiczenia na tablice. Jest jedno ćwiczenie aby wpisać do tablicy cyfry, a potem je od tyłu wypisać.
Znalazłem rozwiązanie (kod poniżej). Czemu takie tworzenie tablicy działa? Z tego co czytałem w książce to rozmiar tablicy musi być znany na poziomie kompilacji i tworzyć tak tablice można tylko przez wskaźniki oraz new i delete. (Dynamiczna alokacja).

#include <iostream>

using namespace std;
int t,n,j;
int k=0;
int main()
{
    cout << "Wprowadz liczbe testow: " << endl;
    cin >>t;
    while ((t<0)||(t>100))
        {
            cout<<"Wprowadz liczbe od 0 do 100"<<endl;
            cin>>t;
        }
    while(t>0)
        {
        cout<<"Wprowadz liczbe elementow a nastepnie kolejno liczby: "<<endl;
        cin>>n;
        int tablica[n]; 
        for(j=0;j<n;j++)
            {
            cout << "Podaj " << j << "] el. tablicy: ";
            cin>>tablica[j];
            k++;
            }
//k po wyjsciu z petli ma wartosc n, a najwiekszy index to n-1, bo indeksujemy od 0
//zmiejszam k o 1
        k--;
        while(k>=0)
            {
            cout<<tablica[k]<<" ";
            k--;
            }
        t--;
        }
    return 0;
}
1

To się nazywa VLA, i jest włączone w standard C, a nie jest ujęte w standardzie C++.
Natomiast realnie wybrane kompilatory C++ to akceptują, jako swoje rozszerzenie standardu.

https://en.wikipedia.org/wiki/Variable-length_array

4

Z tego co czytałem w książce to rozmiar tablicy musi być znany na poziomie kompilacji i tworzyć tak tablice można tylko przez wskaźniki oraz new i delete

Coś pomieszałeś. Rozmiar tablic musi być znany w czasie kompilacji ALBO musisz użyć new/delete (albo lepiej: std::vector). Ogólnie bardzo zachęcam do przeczytania https://dsp.krzaq.cc/post/176/ucze-sie-cxx-kiedy-uzywac-new-i-delete/ - użycie nagich new i delete jest zdecydowanie niezalecane.

PS: jeśli książka uczy używania zmiennych globalnych - zmień książkę.

0

czyli powinno to wyglądać tak? z tego linka co wysłałeś na razie nic nie rozumiem, widocznie jeszcze jestem za bardzo nowicjuszem :P

#include <iostream>
using namespace std;

void odwroc(int *tab, int rozm){

    int *wsk = tab;
    int i=(rozm-1);
    for(wsk=tab+rozm-1;i>=0;i--,wsk--)
    {
        cout << *wsk << ", ";
    }
}

int main() {
	int rozm;
	cout << "Wielkosc tablicy: "; cin >> rozm;
	int *tab = new int[rozm];
    int *wsk=tab;

	for(int i=0;i<rozm;i++){
		cout << "Wpisz [" << i << "] element: "; cin >> *wsk;
		wsk++;
	}
	wsk=tab;
	cout << endl << endl << "Twoja tablica \n";
	for(int i=0;i<rozm;i++){
		cout << "[" << i << "] element: " << *wsk << endl;
		wsk++;
	}

    odwroc(tab,rozm);
	delete [] wsk;

	return 0;
}
2
NewbieKodeRR napisał(a):

czyli powinno to wyglądać tak?

#include <iostream>
using namespace std;

void odwroc(int *tab, int rozm){

    int *wsk = tab;
    int i=(rozm-1);
    for(wsk=tab+rozm-1;i>=0;i--,wsk--)
    {
        cout << *wsk << ", ";
    }
}

Rozwiązanie jest, jak wiele studenckich, nieczyste w tym sensie, że albo:

  • funkcja drukuje, co jest słabością wielu funkcji studenckich. W małych programach nie czujecie róznicy ("jakie ma znaczenie, czy drukuję w funkcji, czy w main"), ale wyobraź sobie, że z czasem chcesz funkcji użyć w zupenie innym kontekście, i drukowania sobie nie życzysz.
  • ma nieprawdziwą nazwę "odwróc" - gdyby byłą "drukujOdwrócone" bym nie szczekał. Dałbym wtedy stream jako argument, bo może ktoś chce drukować na jakiś inny plik, a nie na cout
  • po trzecie dodanie const ładnie sygnalizuje ludziom i kompilatorowi, że masz "nieszkodliwe" zamiary (tzn nie masz zamiaru rzeczywiście odwracać)
void drukujOdwrocone(std::stream out, const int *tab, int rozm){
  cout << wsk[i] << ", ";

W tym przypadku możesz to uleczyć na sposób pierwszy lub drugi.

Pominę, ze chore jest używanie w C++ tablic w stylu C, i wszędzie jej długość.
I osobiście proszę, zaklej gumą do żucia gwiazdkę, a potrenuj kwadratowe klamerki.

0

@AnyKtokolwiek: Jeszcze nie wiem co to stream i nie studiuje. Nie programowałem w C więc nie wiem jaki jest styl C. Czemu nie bawić się wskaźnikami tylko notacją tablicową? Nie wiem jeszcze jak zrobić aby funkcja rzeczywiście odwracała, pewnie coś w stylu drugiej tablicy gdzie skopiuje odwrotnie te cyfry a potem ją zwrócić jakoś do maina ale jeszcze nie mam pomysłu jak.

Jesli dam const int *tab w miejscu gdzie pokazałeś to przy deklaracji tablicy też muszę dać const? Znaczy sie tu?

const int *tab = new int[rozmiar]
cin >> rozmiar

bo przeciez wlasnie ten obiekt wysylam do funkcji, czy wyslac moge normalnie a tylko odebrac jako const?

2
NewbieKodeRR napisał(a):

@AnyKtokolwiek: Jeszcze nie wiem co to stream i nie studiuje. Nie programowałem w C więc nie wiem jaki jest styl C. Czemu nie bawić się wskaźnikami tylko notacją tablicową? Nie wiem jeszcze jak zrobić aby funkcja rzeczywiście odwracała, pewnie coś w stylu drugiej tablicy gdzie skopiuje odwrotnie te cyfry a potem ją zwrócić jakoś do maina ale jeszcze nie mam pomysłu jak.

Jesli dam const int *tab w miejscu gdzie pokazałeś to przy deklaracji tablicy też muszę dać const? Znaczy sie tu?

const int *tab = new int[rozmiar]
cin >> rozmiar

No nie, w ten sposób uczynisz w/w bufor nieużytecznym (po drugie masz odwrócone linie).
znaki "nie - const" możesz przekazywać do funkcji const, to jest OK - błędem jest na odwrót, kompilator powie "hej, ty mi dałeś tablicę, wierząc, że ci jej nie zmienię, ale ja będę zmieniał"
Z tym, że jak funkcja ma rzeczywiście odwracać, to nie dawaj const.

  1. Strumieniem jest np cout, więc już wiesz, co to są strumienie ;)
  2. Gratuluję, ze się interesujesz, jesteś o wiele lepszy od bidnych studencin wyłudzających zadania na litość.
  3. arytmetyką wskaźników o wiele łatwiej przyciąć sobie palce.

bo przeciez wlasnie ten obiekt wysylam do funkcji, czy wyslac moge normalnie a tylko odebrac jako const?

I to jest genialna myśl, skrajnie trudna w C, a łatwa w C++, tylko właśnie nie z tymi starymi tablicami, a z std::string albo std::vector. Sam wynalazłeś argumenty za prawdziwym C++.

std::string odwroc(const std::string)

odebranie jako const jest możliwe

const std::string odwroc(const std::string)

ale moze się nie opłacać, bo będzie zamrożony, niezmienny czy jak mówią immutable.

0

@AnyKtokolwiek: No spoczko, programuje dla siebie, zacząłem 2 dni temu przez za dużo wolnego czasu, z gręboszem i symfonią i na razie jade jak jest w książce, a że jestem na wskaźnkach to teraz utrwalam to co ostatnio czytałem, notacje tablicową rozumiem w 100%. Poprawiłem kod i teraz może jest lepiej? (tak lubię używać też *(wsk++) bo po prostu szybciej).

Ewentualnie notacja tablicowa:

#include <iostream>
using namespace std;

void odwroc(const int tab[], int tab2[], int rozm){



    for(int i=(rozm-1), j=0;i>=0;i--,j++)
    {
        tab2[j]=tab[i];
    }
}

int main() {
	int rozm;

	cout << "Wielkosc tablic: "; cin >> rozm;
	int *tab = new int[rozm];
	int *tab2 = new int[rozm];




	for(int i=0;i<rozm;i++){
		cout << "Wpisz [" << i << "] element: "; cin >> tab[i];

	}

	cout << endl << endl << "Twoja tablica \n";
	for(int i=0;i<rozm;i++){
		cout << "[" << i << "] element: " << tab[i] << endl;

	}

    odwroc(tab, tab2, rozm);

    cout << "\n\nPo wyjsciu z funkcji odwroc, wpisanie odwrotnie do drugiej tablicy ";
    for (int i=0;i<rozm;i++)
    {
        cout << "\n[" << i << "] element: " << tab2[i] << endl;

    }

	delete [] tab;
	delete [] tab2;

	return 0;
}




#include <iostream>
using namespace std;

void odwroc(const int *tab, int *tab2, int rozm){

    const int *wsk = tab;
    int i=(rozm-1);
    for(wsk=tab+rozm-1;i>=0;i--)
    {
        *(tab2++)=*(wsk--);
    }
}

int main() {
	int rozm;

	cout << "Wielkosc tablic: "; cin >> rozm;
	int *tab = new int[rozm];
	int *tab2 = new int[rozm];
    int *wsk=tab;
    int *wsk2=tab2;



	for(int i=0;i<rozm;i++){
		cout << "Wpisz [" << i << "] element: "; cin >> *wsk;
		wsk++;
	}
	wsk=tab;
	cout << endl << endl << "Twoja tablica \n";
	for(int i=0;i<rozm;i++){
		cout << "[" << i << "] element: " << *wsk << endl;
		wsk++;
	}

    odwroc(tab, tab2, rozm);

    cout << "\n\nPo wyjsciu z funkcji odwroc: ";
    for (int i=0;i<rozm;i++)
    {
        cout << "\n[" << i << "] element: " << *wsk2 << endl;
		wsk2++;
    }

    wsk = NULL;
    wsk2 = NULL;
	delete [] tab;
	delete [] tab2;

	return 0;
}




trzeci wariant

#include <iostream>
using namespace std;

void odwroc(const int tab[], int tab2[], int rozm){

   for(int i=(rozm-1);i>=0;i--)
   {
       tab2[rozm-i-1]=tab[i];
   }
}

int main() {
   int rozm;

   cout << "Wielkosc tablic: "; cin >> rozm;
   int *tab = new int[rozm];
   int *tab2 = new int[rozm];

   for(int i=0;i<rozm;i++){
       cout << "Wpisz [" << i << "] element: "; cin >> tab[i];

   }

   cout << endl << endl << "Twoja tablica \n";
   for(int i=0;i<rozm;i++){
       cout << "[" << i << "] element: " << tab[i] << endl;

   }

   odwroc(tab, tab2, rozm);

   cout << "\n\nPo wyjsciu z funkcji odwroc, wpisanie odwrotnie do drugiej tablicy ";
   for (int i=0;i<rozm;i++)
   {
       cout << "\n[" << i << "] element: " << tab2[i] << endl;

   }

   delete [] tab;
   delete [] tab2;

   return 0;
}

1
void odwroc(const int tab[], int tab2[], int rozm){
     for(int i=(rozm-1); i>=0; i--)
    {
        tab2[rozm-i]=tab[i];
    }
}

Dla mnie o wiele lepsze.
Sprawdź warunki końcowe, mogłem się pomylić o 1.
Argumenty zostawiam jak u ciebie, ale powinny być zamienione

void odwroc(int dest[], const int src[], int rozm)
0

@AnyKtokolwiek: Racja pomyliłeś się o jeden ale nie wiem co tam innego dać - już chyba nie myślę. bo jak damy
// EDIT JUZ WIEM, wystarczylo dać tab2[rozm-i-1]=tab[i];

for(int i=(rozm-1); i>=0; i--)  // np. rozm = 5
        tab2[rozm-i]=tab[i];  

i = 4 (5-1) czyli tu sie zgadza bo 4 element tablicy ejst ostatni w tym przypadku ale
tab25-4 to bedzie 1 element tablicy, a nie zerowy

calosc:

#include <iostream>
using namespace std;

void odwroc(const int src[], int dest[], int rozm){

    for(int i=(rozm-1);i>=0;i--)
    {
        dest[rozm-i-1]=src[i];
    }
}

int main() {
    int rozm;

    cout << "Wielkosc tablic: "; cin >> rozm;
    int *dest = new int[rozm];
    int *src = new int[rozm];

    for(int i=0;i<rozm;i++){
        cout << "Wpisz [" << i << "] element: "; cin >> src[i];

    }

    cout << endl << endl << "Twoja tablica \n";
    for(int i=0;i<rozm;i++){
        cout << "[" << i << "] element: " << src[i] << endl;

    }

    odwroc(src, dest, rozm);

    cout << "\n\nPo wyjsciu z funkcji odwroc, wpisanie odwrotnie do drugiej tablicy ";
    for (int i=0;i<rozm;i++)
    {
        cout << "\n[" << i << "] element: " << dest[i] << endl;

    }

    delete [] src;
    delete [] dest;

    return 0;
}

1

Ja już nie myślę o tej porze 1).
Dobranoc

*1) przynajmniej w C. Wystarczy do Javy / C# ;)
*

3

Jest jedno ćwiczenie aby wpisać do tablicy cyfry, a potem je od tyłu wypisać

Standard język C++ cały czas ulega zmianie, tak aby programowało się łatwiej i bezpieczniej. Zamiast używać dynamicznie alokowanych tablic (new/delete), wskaźników czy też indeksów w pętlach, rozwiązanie można przedstawić w następującej postaci ( C++ 14 ):

#include <iostream>
#include <vector>
#include <iterator>

using namespace std;

int main()
{
    vector<int> tab( istream_iterator<int>{cin} , {} );  // tablica/kontener dynamiczy typu vector<int> wraz z jego inicjalizacją polegającą na wpisywaniu liczb z klawiatury
    copy( rbegin(tab), rend(tab), ostream_iterator<int>(cout, " ") ); // wyświetlenie elementów tablicy/kontenera w odwrotnej kolejności  
}

https://godbolt.org/z/G5v4E7

Zyskujesz na rozmiarze, prostocie oraz bezpieczeństwie, gdyż nie musisz się martwić o rozmiar tablic, usuwanie zasobów, czy też pilnować aby indeksy nie przekroczyły swoich zakresów.
IMHO, naukę programowania można zacząć od tego typu konstrukcji, przechodząc na dalszym etapie do trudniejszych tematów.

0

@TomaszLiMoon: tyle, że jestem na tyle początkujący, że nie wiem jak może być typ "vector<int>" dziwny zapis, istream_iterator i ostream nie wiem jeszcze co to jest, to samo rend i rbegin.
Ucze sie z ksiazki grebosza jak na razie i jestem na wskaznikach, moze pozniej bedzie o tym pisac, a jak ją skoncze to pewnie bedzie mi latwiej przyswajac takie cos co tu napisales

4

Dlatego nie polecamy Grębosza. Uczysz się stosunkowo niepotrzebnych rzeczy - szczególnie na początku - a na rzeczy faktycznie przydatne patrzysz z podejrzliwością, bo twoja książka to kurs C z klasami.

0

A co polecacie?

2

https://stackoverflow.com/a/388282/2456565 - ta lista jest bardzo dobra. Tylko jeśli będziesz patrzył na polskie przekłady to absolutnie nie bierz nic starszego niż C++11.

0

Tak, ale cena jest zabójcza... Jeśli jesteś studentem/uczniem to zobacz czy masz dostęp przez uczelnię/szkołę, bo to jest kawał kasy za jedną książkę.

0

@kq: Nie jestem uczniem ani studentem, programuje z hobby, ogólnie moge sie uczyć po angielsku tylko będzie to o wiele wolniejsze kiedy czasem bede musiał używać słownika :P A jest coś polskiego? Albo po polsku co jest warte nauki?

0

Ja nie wiem, ale myślę, że inni coś Ci podpowiedzą. W każdym razie 250zł za jedną z potencjalnie kilku książek jest imo zabójcza i bezsensowna.

3

Primer C++ (wersja angielska) jest dostępna jako free eBooks na https://www.pdfdrive.com/c-primer-5th-edition-e158392725.html

1

Większość Meyersów masz po polsku na Helionie, Stroustrupa chyba też.

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