• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Ciąg liczbowy

Object Storage Arubacloud
0 głosów
403 wizyt
pytanie zadane 26 października 2016 w C i C++ przez Sensej Użytkownik (540 p.)

Hey.

Zrobiłem kod, który wypisuje ciąg liczbowy. Niestety po uruchomieniu programu i wpisaniu jak długi ma być ten ciąg, pojawia się problem, mianowicie po wpisaniu liczby więcej niż 5 program poprawnie wypisuje liczby lecz wyskakuje komunikat "program przestał działać", a powyżej liczby 12 program już nawet się nie wykonuje. 

#include <iostream>

using namespace std;

int ile,k,licznik=0,licznik_tab=0;

int main()
{
    cout<<"Podaj n: ";
    cin>>ile;


    int *tablica;
    tablica = new int [ile];

    for(int i=0; i<ile; i++)
    {
        licznik++;
        for(k=1; k<=licznik; k++)
        {
            tablica[licznik_tab]=k;
            licznik_tab++;
        }
    }


    for(int i=0; i<ile; i++)
    {
        cout<<tablica[i]<<" ";
    }
    delete [] tablica;
    return 0;
}

 

2 odpowiedzi

+3 głosów
odpowiedź 27 października 2016 przez siof Gaduła (3,560 p.)
wybrane 27 października 2016 przez Sensej
 
Najlepsza

Tak jak pisał użytkownik egoon problem jest taki że w przypadku powyższego kodu masz zbyt małą tablicę. To czy program się wywali podczas pracy czy nie zależy czy nastąpi próba odwołania się do niedozwolonego miejsca pamięci.

Postaram się to prościej wyjaśnić:

Chodzi o to że każdy program ma przydzieloną jakąś pamięć w pamięci ram. Ty tworząc tablicę np 5 elementową mówisz systemowi operacyjnemu że chcesz dostać pamięć na te pare elementów i on Ci ją przydziela. I takie jakby klocki pamięci dostają wszystkie programy. Normalnie jeśli spróbujesz dostać się do pamięci z poza tego co przydzielił Ci system operacyjny program przestanie działać (tak zwana próba dostania się do pamięci chronionej). Tutaj niestety jest mały haczyk ponieważ system może przydzielić Ci tą pamięć w różnym miejscu pamięci ram oraz może też przydzielić więcej niż potrzebujesz. Może więc zdarzyć się tak że pamięć z poza tablicy do której próbujesz się dostaćnadal będzie w ramach pamięci przydzielonej Twojemu programowi i wtedy program będzie wydawał sięże działa prawidłowo.

Troche ciężko mi to wyjaśnić więc spróbuje jeszcze podać przykład obrazkowy:

+++-+--++XXXXX+*++++

Na powyższym obrazku XXXXX jest tablicą którą stworzyłeś a pozostałe znaki to inne rzeczy w pamięci. Jeśli spróbujesz np zapisać coś do tablica[7] to okazałoby się że próbujesz zapisać coś do tej * na obrazku powyżej. Jeśli ta gwiazdka należy do programu to wtedy działąnie programu nie zostanie przerwane ale jeśli nie to zostanie. Dodatkowo jeśli należy do programu to może sie zdarzyć tak że nadpiszesz jakąś inną zmienną programu. Aby to ostatnie zobrazować powiedzmy że pisząc grę trzymamy ekwipunek gracza w tablicy. Jeśli podczas modyfikacji tego ekwipunku wyjdziemy poza rozmiar tej tablicy może się okazać że przypadkowo zamiast np dodać przedmiot danemu graczowi to zmieniliśmy HP jakiegoś bossa na 5 punktów życia.

Korzystanie ze wskaźników/tablic w C++ pozwala na bardzo dużo ale może powodować problemy więc należy uważać na takie rzeczy jak wychodzenie poza zakres tablicy i podobne ;)

Mam nadzieje że było to w miarę zrozumiałe.

 

Jeśli chodzi o sam problem to możesz zrobić tak że w drugiej pętli dodatkowo zwiększasz także zmienną 'i' oraz sprawdzasz czy nie jest większa od 'ile'. Wtedy musiałbyś z pierwszej pętli dodatkowo jeszcze usunąć zwiększanie i. Przy takim podejściu powinno działać tak jak chcesz.

Czyli ta pętla po modyfikacjach wyglądałaby tak:

    for (int i = 0; i < ile;)
    {
        licznik++;
        for (k = 1; k <= licznik; k++)
        {
            tablica[licznik_tab] = k;
            licznik_tab++;
            ++i;

            if (i >= ile)
                break;
        }
    }

Ale można także zrobić inaczej pozbywając się drugiej pętli:

#include <iostream>

using namespace std;

int main()
{
    int ile = 0, maksymalnaWartosc = 1, aktualnaWartosc = 1;
    cout << "Podaj n: ";
    cin >> ile;

    int *tablica = new int[ile];

    for (int i = 0; i < ile; ++i)
    {
        tablica[i] = aktualnaWartosc;

        if (aktualnaWartosc == maksymalnaWartosc)
        {
            ++maksymalnaWartosc;
            aktualnaWartosc = 1;
        }
        else
            ++aktualnaWartosc;
    }

    for (int i = 0; i < ile; i++)
    {
        cout << tablica[i] << " ";
    }
    delete[] tablica;
    return 0;
}

 

komentarz 27 października 2016 przez egoon Obywatel (1,360 p.)

To jedna z tych rzeczy, które tak lubię w programowaniu - jedna mała rzecz, a można na tyle sposobów...smiley

Miodzio!

A co do tablic i ich rozmiarów - wektory nie mają tego kłopotu, choć są potencjalnie dość kosztowne. Ale wygodnedevil

komentarz 27 października 2016 przez siof Gaduła (3,560 p.)
Co do kosztu vectorów to zależy do czego i jak są wykorzystywane. Jeśli ma to być w miare statyczny kontener albo o konkretnym maksymalnym rozmiarze to nie jest aż tak kosztowny (choć wiadomo że bardziej kosztowny niż wykorzystanie tablic bez sprawdzania warunków wyjścia poza zakres). Vector jest wewnętrznie oparty o tablicę więc największy koszt przy nim jest w momencie jak dodajesz elementy bo co jakiś czas musi on stworzyć nową tablicę i przekopiować do niej wszystkie aktualne dane. Właśnie te momenty są kosztowne przy vectorach. Można jednak je zminimalizować określając początkowy rozmiar vectora.

W C++11 doszedł też typ 'Array' który ma wbudowane sprawdzanie wyjścia poza zakres (rzuca wtedy exception).
komentarz 27 października 2016 przez Sensej Użytkownik (540 p.)
siof dziękuje bardzo za wytłumaczenie tego. Teraz rozumiem gdzie leżał problem.
0 głosów
odpowiedź 26 października 2016 przez egoon Obywatel (1,360 p.)
edycja 26 października 2016 przez egoon

Powitać.

Pytanie - po co dwie pętle? Zagnieżdżone na dodatek? Do wypełnienia tablicy jedna w zupełności wystarczy.

I czemu zmienne globalne? To w tym przypadku (i wielu innych) nic nie daje, poza kłopotami.

Może tak:

#include <iostream>

using namespace std;

int main()
{
	int ile;
	cout << "Podaj n: ";
	cin >> ile;


	int *tablica;
	tablica = new int[ile];

	for (int i = 0; i<ile; i++)
	{
		tablica[i] = i;
	}


	for (int i = 0; i<ile; i++)
	{
		cout << tablica[i] << " ";
	}
	delete[] tablica;
	
	return 0;
}

 

komentarz 26 października 2016 przez Sensej Użytkownik (540 p.)
Zamiana zmiennych na lokalne nie rozwiązuje problemu.

Treść zadania jest taka: Utworzyć tablicę n-elementową zawierające następujące elementy: a)1,1,2,1,2,3,1,2,3,4,1,2,3,4,5,1...

Pierwsza pętla odpowiedzialna jest za wypisanie odpowiedniej ilości liczb w ciągu, a druga pętla wpisuje dane do tablicy.

Kod błędu jaki się wyświetla to: -1073741510 .
komentarz 26 października 2016 przez egoon Obywatel (1,360 p.)
A, to co innego, zapomnij o kodzie powyżej. Jeżeli druga pętla ma wpisywać to, co pokazujesz w treści zadania, to problem w wielkości tablicy. Policz, ile elementów ma zawierać, potem porównaj z zadanym rozmiarem (zmienna 'ile'). Jak wpiszesz 5, to do tablicy nie wpiszesz więcej niż 1,1,2,1,2. Dalej nie pójdzie, chyba że zadeklarujesz więcej wymiarów tablicy. Ale może lepiej oblicz rozmiar - jeśli dobrze rozumiem treść zadania, dla '5' powinno być 1+2+3+4+5 = 15 i tak dalej.
komentarz 26 października 2016 przez Sensej Użytkownik (540 p.)
Moim zdaniem dla '5' powinno wypisać 1,1,2,1,2. Ponieważ da nam to 5 elementów w tablicy.

Jeżeli to problem z wielkością tablicy to dlaczego przy wpisaniu np. 5 program wykonuje się poprawnie bez błędów, a przy podaniu np. 8 program przestaje działać. Wydaje mi się, że jeżeli byłby to problem z wielkością to powinien pojawiać się ten problem za każdym razem. Dodam, że problem pojawia się trochę przypadkowo, przed chwilą uruchomiłem program dla ile=6, i 3 razy program przestał działać, a 2 razy program wykonał się bez problemu.
komentarz 27 października 2016 przez egoon Obywatel (1,360 p.)

Ze mnie uparte bydle - to, że czasem zadziała, wynika z zawartości pamięci. Choć u mnie nie działa, może kwestia kompilatora?

Zrobiłem takie cóś:

#include <iostream>

using namespace std;

int main()
{
	int ile{}, licznik{};
	int *ptr_licznik = &licznik;

	cout << "Podaj n: ";
	cin >> ile;

	int *tablica = new int[ile]{};
	
	for (int i = 0; i<ile; i++)
	{
		for (int k = 0; k <= i; k++)
		{
			if (*ptr_licznik >= ile)
				break;
			int tmp = k + 1;
			tablica[*ptr_licznik] = tmp;
			*ptr_licznik += 1;
		}
	}

	
	for (int i = 0; i<ile; i++)
	{
		cout << tablica[i] << " ";
	}
	
	delete[] tablica;
	return 0;
}

Mało tu finezji i dopracowania, ale działa. Cel uświęca czasami środki. Co do zmiennych globalnych, to uważam po swoich różnych kłopotach, że to może nie totalne zło, ale trzeba z umiarem stosować, łatwo o kłopoty i szukanie trudnych do ogarnięcia błędów. No, ale to kwestia gustu.

komentarz 27 października 2016 przez Piotr Batko Stary wyjadacz (13,190 p.)

@egoon Mógłbyś powiedzieć co to za nawiasy klamrowe przy definicji zmiennych? Pierwszy raz coś takiego widzę :)

int ile{}, licznik{};

1
komentarz 27 października 2016 przez egoon Obywatel (1,360 p.)
edycja 27 października 2016 przez egoon
Dostępny od C++11 sposób przypisania 0(zera) podczas deklarowania zmiennych, tablic itp. Ponadto można np. do tablicy wpisać wartości, np.
int tablica[5]{1,2,3,4,5};

Oczywiście równie dobrze można pisać zwyczajnie, int zmienna = 0; Ps. W przypadku użycia do inicjalizacji wskaźnika, przypisuje mu 'nullptr'.

Podobne pytania

0 głosów
1 odpowiedź 1,201 wizyt
0 głosów
1 odpowiedź 211 wizyt
pytanie zadane 8 grudnia 2022 w C i C++ przez Pysa6 Nowicjusz (220 p.)
0 głosów
1 odpowiedź 208 wizyt
pytanie zadane 1 grudnia 2022 w C i C++ przez Pysa6 Nowicjusz (220 p.)

92,563 zapytań

141,413 odpowiedzi

319,590 komentarzy

61,948 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...