Początki programowania

0

Witam
Robię kurs i spotkałem takie zadanie:
Napisz funkcję, która losuje liczbę z przedziału od 50 do 60 włącznie. Wywołaj funkcję kilka razy (wypisz wylosowane wartości na ekran) w celu przetestowania czy działa ona poprawnie.
I zrobiłem tak i nie działało:

#include <iostream>
#include <cstdlib>
#include  <ctime>
using namespace std;
void losuj(){
    srand(time(NULL));
    cout<<(rand()%11)+51;
}
int main()
{
    losuj();
    cout<<endl;
    losuj();
    cout<<endl;
    losuj();

    return 0;
}

Trochę nad tym siedziałem i doszedłem do wniosku że Funkcja srand musi być w funkcji main wtedy działa. Może ktoś wytłumaczyć czemu tak się dzieje?

1

No faktycznie tak jest ciekawe... no ale widocznie tak to działa PS twój program nie losuje liczb z dobrego przedziału(losuje od 51 do 61 włącznie). dodałem ci jeszcze pętle bo tak jest wygodniej

#include <iostream>
#include <cstdlib>
#include  <ctime>
using namespace std;
void losuj(){

    cout<<(rand()%11)+50;
}
int main()
{
 srand(time(NULL));
    for(int i=0;i<3;i++)
    {
        losuj();
    cout<<endl;
    }

    return 0;
}
4

rand losuje liczbę.
srand inicjalizuje generator liczb pseudolosowych.
Jeśli przed wywołaniem rand nie dasz w ogóle srand, to za każdym razem podczas uruchomienia programu będziesz miał wylosowaną taką samą sekwencję liczb.
Tę sekwencję zmieniasz właśnie za pomocą srand (pod warunkiem, że argument podany do srand jest różny od 1).
Ty jako argument podajesz wynik działania funkcji time(NULL). Funkcja time zwraca liczbę sekund, które upłynęły od 01.01.1970.
Jeśli Twój komputer jest szybki i nie miałeś szczęścia, to kolejne wywołania Twojej funkcji losuj() wykonywały się w tej samej sekundzie. W konsekwencji srand inicjalizował generator tą samą wartością. Nie sprawdzałem tego programu, ale pewno dostawałeś trzy takie same liczby. Przy ponownym uruchomieniu - kolejne trzy (ale inne). Przy odrobinie szczęścia, kolejne dwa wywołania wykonałyby się w innej sekundzie i wtedy dwie byłyby takie same, a trzecia inna.

Zwróć też uwagę na funkcję time(), która zwraca wartość zarówno wprost, jak i przez wskaźnik (który u Ciebie jest NULL).
Czasem są pytania po co taka możliwość. Jedni mówią, że to zaszłość historyczna (w systemach 16-bitowych wartość zwracana z funkcji byłaby 16-bitowa, tymczasem time_t - to minimum 32 bity).

Ale takie rozwiązanie umożliwia też taką konstrukcję:

time_t t;
time_t v=time(&t)-cośtam;

W takim rozwiązaniu w jednym ruchu odczytujesz czas bieżący i jednocześnie możesz obliczyć jakąś różnicę czasu.

Rozwiązanie z srand(time(NULL)) w main nie jest złe w Twoim przykładzie, ale gdybyś to umieścił w jakimś większym kodzie, to nadal mogłoby być tak, że kolejne wywołania wykonywałyby się w tej samej sekundzie i losowość byłaby taka sobie.

0

Witam znowu napotkałem na taki dziwny problem. Napisałem funkcjię losującą która wpisuje losowe liczby do tablicy a na końcu ma ich zsumować. Przy sumowaniu pętlą do{}while sumowało źle a pętlą for dobrze. Jest jakieś wytłumaczenie na to?

Pętla for która działa:

void losuj(){
    int liczba[9];
    int licznik = 0;
do{

    liczba[licznik]=(rand()%7)+4;
    cout<<liczba[licznik]<<endl;
    licznik++;
    }while(licznik<10);
int suma = 0;
for(int licznik=0; licznik<10;licznik++){
    suma+=liczba[licznik];
    }
cout<<suma<<endl;
}

A tutaj pętla do{}while:

void losuj(){
    int liczba[9];
    int licznik = 0;
do{

    liczba[licznik]=(rand()%7)+4;
    cout<<liczba[licznik]<<endl;
    licznik++;
    }while(licznik<10);
licznik=0;
int suma = 0;
do{
    suma+=liczba[licznik];
    licznik++;
    }while(licznik<10);
cout<<suma<<endl;
}
1
#include <iostream>
#include <cstdlib>
#include  <ctime>
using namespace std;
void losuj(){
    int liczba[9];
    int licznik = 0;
do{

    liczba[licznik]=(rand()%7)+4;
    cout<<liczba[licznik]<<endl;
    licznik++;
    }while(licznik<9);//do 9 bo liczymy od zera
int suma = 0;
for(int licznik=0; licznik<9;licznik++)//musi być do 9 bo liczymy 9 argumentów od 0.
{ 
    suma+=liczba[licznik];
    }
cout<<suma<<endl;
}
int main()
{
 srand(time(NULL));
    losuj();

    return 0;
}
//PS Mi oba programy nie działały :)
//PPS A nie jednak ten drugi działa ale nie wiem czemu... bo nie powinien może znajdę przyczynę
//PPPS No nie wiem czemu ten drugi program działa i to jeszcze za każdym razem... nie powinien działać widocznie kompilator //zapisuje gdzieś w pamięci tą dodatkową wartość i następnie ją odczytuje,czasem tak bywa, ogólnie to jedyny błąd to 10 zamiast 9
1

Dorn już zauważył rozmiar tablicy, ale skoro napisałem list, to go wkleję :-)

C nie jest prostym językiem ze względu na to, że wielu oczywistych pomyłek kompilator nie zgłasza.

masz deklarację
int liczba [9];
to znaczy, że masz 9 elementów w tablicy (nie 10). Elementy w tablicy będę miały indeksy od 0 do 8.
A Twoje pętle przebiegają od 0 do 9. Czyli sięgasz po element liczba[9], który nie istnieje.
Kompilator tego nie zgłosi.

Czemu raz działa, a innym razem nie?
W pamięci zmienna "liczba" - to jest obszar

9 * sizeof(int) bajtów.

(sizeof(int) podaje Ci rozmiar w bajtach zmiennej typu int. Ile to będzie konkretnie - to zależy od środowiska. W Windowsie 16-bitowym - to były 2 bajty, w Win32 - 4 bajty, na Linuxie - nie wiem).
Załóżmy, że działasz na Win32. Wtedy Twoja zmienna "liczba" - to jest ciągły obszar pamięci o długości 36 bajtów (9 * 4).
Dla kompilatora (i de facto dla Ciebie) - to jest jednolity obszar pamięci (bajtów).

Możesz np. napisać tak:

char* t=(char*)liczba;

(to się nazywa rzutowanie typów. Zrzutowałeś typ int na typ char. I teraz pod zmienną t masz ten sam obszar pamięci, ale traktowany jak tablicę znaków, czyli upraszczając bajtów).

I dalej możesz spokojnie napisać t[12]=coś tam
W ten sposób do 13 bajtu tego obszaru wpakujesz "coś tam"
Ale - równie dobrze możesz napisać t[1250]=nowa wartość
Kompilator nie mrugnie, a Ty zapakujesz "nową wartość" do zupełnie innego obszaru pamięci. Rozumiesz?
W tym przykładzie dozwolone będzie t[35]=0, bo to jest ostatni bajt w Twoim zarezerwowanym obszarze.
t[36], to jest pierwszy bajt za zarezerwowanym obszarem. Jeśli w Twoim programie w tym miejscu nie ma żadnych istotnych (innych) danych, to nic się nie stanie. Będzie działać i da dobry wynik.

Wracając do int. To działa tak samo jak tablica bajtów, tylko ze kompilator sam "skacze" po bajtach przesuwając miejsce zapisu do położenia index*sizeof(typ)
Czyli jeśli masz:
int tablica[9], to tablica[2]=12 oznacza, że kompilator zapakuje 12 w cztery bajty poczynając od ósmego (faktycznie tylko jeden bajt będzie miłą wartość 12, a kolejne trzy będą zerami).
W twoim programie - w pętlach w pewnym momencie w czasie wykonywania programu dochodzi do sytuacji
suma+=tablica[9];
W tym momencie następuje pobranie czterech bajtów poczynając od 36 pozycji (licząc w bajtach). A tam może być dowolna wartość, bo twoim ostatnim zarezerwowanym bajtem jest komórka 35.

Reszta (działanie lub nie, ewentualne zawieszenia programu i różne dziwne zachowania) to już jest kwestia przypadku. To będzie zależało od tego, co się znajduje w obszarze do którego się odwołujesz, czy tylko czytasz, czy może próbujesz coś zapisać, itd.

W tych programach jest jeszcze jedna sprawa, na którą można zwrócić uwagę.
W wersji z for zmienna licznik jest deklarowana dwa razy. Raz na początku, i potem w pętli for.
Czy tak można, czy nie - to zależy od konkretnej wersji kompilatora. Niektóre na to pozwolą inne wykażą błąd ("zmienna już zadeklarowana").
A niektóre kompilatory mają nawet opcję konfiguracyjną. W każdym razie proponuję unikać takich sytuacji i nie deklarować dwóch zmiennych o tej samej nazwie w ramach jednej funkcji.

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