Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

Problem z policzeniem średniej i wskaźniki

Ostatnio zmodyfikowano 2016-04-04 13:19
Autor Wiadomość
Talamak
Temat założony przez niniejszego użytkownika
Problem z policzeniem średniej i wskaźniki
» 2016-04-03 23:05:31
Serdecznie witam. Już raz mi pomogliście, więc ze śmiałością zwracam się o pomoc do użytkowników forum raz jeszcze.
Mam informatykę na studiach, gdzie mnie nic nie uczą, uczę się sam w domku i klepię trochę programów w C++. Zaczynałem od zupełnego zera, a teraz z pomocą Mirosława Zelenta i Jerzego Grębosza już ogarnąłem jakieś podstawy.
Przyszły wskaźniki i... Zrobiło się kiepsko. Temat, który z początku mnie przerósł. Początkowo miałem prosić o całkowitą pomoc w napisaniu programu, ale zawziąłem się, otworzyłem Symfonię C++ i coś jednak zrobiłem sam. Mam jednak parę wątpliwości i jeden problem.
Zadanie do zrobienia jest następujące:

Napisz program, który dla tablicy liczb całkowitych o stałym rozmiarze:
a) znajduje wartość największą i najmniejszą,
b) oblicza średnią arytmetyczną,
c) oblicza standardowe odchylenie
W zadaniu tym można zaprogramować własne funkcje do realizacji zadań, np. funkcja, która
nadaje wartości losowe wszystkim elementom tablicy: void LosujTablice(int*, int, int, int) -
pierwszy parametr to adres tablicy, drugi liczba elementów tablicy, trzeci to najmniejsza możliwa
wartość, jaką może mieć element tablicy, czwarty parametr to największa możliwa wartość, jaką
może mieć element tablicy.
Funkcja wyświetlająca wszystkie elementy tablicy na ekranie: void WyswietlTablice(int *, int,
int) - pierwszy parametr to adres tablicy, drugi to liczba elementów tablicy, trzeci parametr to
liczba elementów tablicy w jednym wierszu.
Funkcje do szukania wartości min i max, liczenia średniej arytmetycznej oraz standardowego
odchylenia należy zdefiniować samodzielnie.

Poczyniłem coś takiego:

C/C++
#include <iostream>
#include <ctime>
#include <windows.h>

using namespace std;

void GeneratorTablicy( int * wsk, int m ); //*wsk  - gwiazdka swiadczy o tym, ze chodzi nam o zawartosc adresu, a nie adres (???)
void WyswietlaczTablicy( int * wsk, int m );
float fSrednia( int * wsk, int m );
int fNajmniejsza( int * wsk, int m );
int fNajmniejsza( int * wsk, int m );
float fOdchylenie( int * wsk, int m );


int main()
{
    srand( time( NULL ) );
    int m = rand() % 14 + 2; // losujemy liczbe od 2 do 15
   
    int * tablica;
    /* wskaznik, ktory wskazuje na adres pierwszego wyrazu tablicy
        (nazwa jest adresem pierwszego wyrazu) */
    tablica = new int[ m ]; /* stworzenie nienazwanej tablicy typu int
        o rozmiarze m, do ktorego prowadzi wskaznik tablica. Uwaga. Stworzona tablica nie ma nazwy. Nie nazywamy tablicy.
        Poslugujemy sie tylko jej adresem - wskaznikiem. */
    cout << endl;
   
    GeneratorTablicy( tablica, m ); //wywoluje funkcje. Przesylam jej adres pierwszego wyrazu tablicy i jej dlugosc
    WyswietlaczTablicy( tablica, m ); //wywoluje funkcje. Przesylam jej adres pierwszego wyrazu i jej dlugosc
   
    cout << "Co chcesz zrobic?" << endl;
    cout << "1. Policz srednia liczb w tablicy" << endl;
    cout << "2. Znajdz najmniejsza wartosc w tablicy" << endl;
    cout << "3. Znajdz najwieksza wartosc w tablicy" << endl;
    cout << "4. Policz odchylenie standardowe liczb w tablicy" << endl;
    int wybor;
    cin >> wybor;
    switch( wybor )
{ case 1: cout << fSrednia( tablica, m );
        break;
        /* case 2: fNajmniejsza()
                  break;
                  case 3: fNajwieksza()
                  break;
                  case 4: fOdchylenie()
                  break; */
    }
   
   
    return 0;
}



void GeneratorTablicy( int * wsk, int m )
{
    for( int i = 0; i <= m - 1; i++ )
    {
        wsk[ i ] = rand() % 5 + 1; //przpypisz wartosc 1 - 5 adresowi pierwszego, drugiego, n-tego;
    }
}

void WyswietlaczTablicy( int * wsk, int m ) //*wsk swiadczy o tym, ze chodzi nam o zawartosc adresu, a nie adres
{
    cout << "Wylosowano m= " << m << endl << endl;
    cout << "Otrzymujemy nastepujaca tablice: " << endl;
    for( int i = 0; i <= m - 1; i++ )
    {
        cout << wsk[ i ] << "\t"; //wyswietl co jest pod adresem kazdego wyrazu
    }
    cout << endl;
}

float fSrednia( int * wsk, int m )
{
    int suma;
    float srednia;
   
    for( int i = 0; i <= m - 1; i++ )
    {
        suma += wsk[ i ];
    }
    return srednia;
}

Wiem, że nie ma tych parametrów maksymalnych i minimalnych, jakie mogą przyjmować elementy tablic, ale to nieważne póki co. Proszę na to nie zwracać uwagi. Wątpliwości mam następujące:
1) Proszę o zwrócenie uwagi na moje komentarze. Mam taki zwyczaj je pisać, żeby jak najwięcej się nauczyć przy pisaniu programów. Czy te komentarze mają ręce i nogi? Zdaje sobie sprawę, że nie pisane są super profesjonalnie - do użytku własnego, ale czy nie ma w nich rażących błędów logicznych? Czy moje rozumowanie jest poprawne?
2) Szczególne pytania do komentarza pierwszego, z trzema znakami zapytania. Czy ta gwiazdka oznacza, że ja wysyłam do funkcji jako argument obiekt pod danym adresem, a nie adres? Tym sobie próbuję tłumaczyć to, że np. w funkcji GeneratorTablicy używamy wsk - bez "*". A przecież przeczytałem, że "*" powoduje, że pracujemy na obiektach pod tym adresem, a nie na adresie. A generując tablicę ja pracuje właśnie na obiektach pod tymi adresami.
3) Dlaczego program źle liczy średnią? Nie mogę znaleźć błędu. Reszta podpunktów cały czas przede mną i mam nadzieję, że przy dalszych problemach będę mógł się do Was zwrócić.
Z góry bardzo dziękuję!
P-146957
michal11
» 2016-04-03 23:18:44
Nie wiem co rozumiesz przez zawartość adresu, ale int *wsk oznacza, że przesyłasz wskaźnik czyli adres (miejsce w pamięci) obiektu lub tablicy. Jeszcze prościej przekazujesz do funkcji informację w którym miejscu w pamięci zaczyna się obszar który program ma interpretować jako tablicę.
Reszta komentarzy wygląda ok.

Program źle liczy średnią bo funkcja jest źle napisana. Średnia to suma podzielona przez ilość elementów, i to właśnie powinna robić twoja funkcja obliczyć sumę elementów w pętli (i to robi) zwrócić ta sumę podzieloną przez ilośc el. ( w twoim przypadku m). Dostajesz dziwne wartości bo zwracasz zmienną średnia która nie jest zainicjowana i nic do niej nie przypisujesz czyli ma losową wartość.
Pamiętaj tylko, że gdy dzielisz int/int to jako wynik otrzymujesz też int nawet jeżeli przypisujesz wyniki dzielenia do float/double, np.:
C/C++
float w = 5 / 2;
//w==2
Jeżeli chcesz otrzymać poprawny wynik to albo zadeklaruj jedną zmienną jako float albo dodaj rzutowanie przy obliczaniu wyniku.
P-146958
Talamak
Temat założony przez niniejszego użytkownika
» 2016-04-04 00:04:13
Bardzo dziękuję za odpowiedź.
Co do mojej funkcji "średnia". Strasznie mi głupio - tylko zabrałem czas. Jasne, że to, co napisałem powyżej jest bzdurą. Wzięło się to stąd, że tyle razy kombinowałem z tą funkcją różne rzeczy, że w końcu skopiowałem tutaj zupełną głupotę. Teraz mam coś takiego:

C/C++
float fSrednia( int * wsk, int m )
{
    float suma;
    float srednia;
   
    for( int i = 0; i <= m - 1; i++ )
    {
        suma += wsk[ i ];
    }
    srednia = suma / m;
    return srednia;
}
Na samym początku napisałem to tak - tak mi się przynajmniej wydawało. Teraz jednak wszystko działa, a mnie się wydaje, że po prostu wtedy liczenie sredniej dałem jeszcze w klamry for. I stąd problem i dalsze kombinowanie. Tę sprawę możemy już zamknąć, dziękuję.

Teraz wątpliwości co do tych wskaźników. W Symfonii czytam:
[q]Słowem: gwiazdka kieruje nas do obiektu pokazywanego przez wskaźnik[/q]
Zrozumiałem z tego, tyle, że:
C/C++
int * wskaznik = & zmienna;
* wskaznik = 200;
Ta gwiazdka oznacza, że nie zmieniam wskaźnika na 200, a zmieniam to, co wskazuje wskaźnik, a więc zmienną na 200.
Ale dla przykładu:
C/C++
int * wskaznik = & zmienna;
cout << wskaznik;
Wyświetli mi nie zmienną, a jej adres.
Czyli zapis bez gwiazdki dotyczy samego adresu, a z gwiazdką obiektu pod tym adresem.

W funkcji GeneratorTablicy ja przecież nadaje wartości elementom tablicy, a nie im adresom.Dlaczego więc używam zapisu bez gwiazdki? Tego nie rozumiem i byłbym wdzięczny o wytłumaczenie, bo czuje, że jak to pojmę to już będzie znacznie łatwiej.
Z góry dziękuje. 
P-146959
mateczek
» 2016-04-04 00:46:43
C/C++
float fSrednia( int * wsk, int m )
{
    float suma = 0; //tu problem !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    float srednia;
   
    for( int i = 0; i <= m - 1; i++ )
    {
        suma += wsk[ i ];
    }
    srednia = suma / m;
    return srednia;
   
   
   
   
   
   
   
   
    void GeneratorTablicy( int * wsk, int m )
    {
        for( int i = 0; i < m; i++ ) // jak chcesz z gwiazdką !!!!!!!!!!!!!!!
        {
            * wsk = rand() % 5 + 1; //przpypisz wartosc 1 - 5 adresowi pierwszego, drugiego, n-tego;
            wsk++;
        }
    }
P-146960
michal11
» 2016-04-04 01:01:02

Zrozumiałem z tego, tyle, że:

C/C++
int * wskaznik = & zmienna;
* wskaznik = 200;

Ta gwiazdka oznacza, że nie zmieniam wskaźnika na 200, a zmieniam to, co wskazuje wskaźnik, a więc zmienną na 200.
Ale dla przykładu:

C/C++
int * wskaznik = & zmienna;
cout << wskaznik;

Wyświetli mi nie zmienną, a jej adres.
Czyli zapis bez gwiazdki dotyczy samego adresu, a z gwiazdką obiektu pod tym adresem.

Tak.

C/C++
wsk[ i ] = rand() % 5 + 1; //przpypisz wartosc 1 - 5 adresowi pierwszego, drugiego, n-tego;

Tu nie używasz gwiazdki ponieważ używasz operatora[] która za ciebie jakby wyciąga tan obiekt na który wskazuje dana komórka tablicy, to
wsk[ i ]
 jest równoważne temu
*( wsk + i )
.
P-146962
Talamak
Temat założony przez niniejszego użytkownika
» 2016-04-04 13:11:01
mataczek - program działa i bez tego, ale zgadzam się w zupełności, że najlepiej jest zdefiniować początkową wartość sumy. Chociażby dla jasności i porządku. Zupełnie o tym zapomniałem Dzięki!

michal11 - jej, wreszcie kumam. Dzięki za wyjaśnienie, ale nasunęło mi się kolejne pytanie, tak już z czystej ciekawości. Skoro
wsk[ i ]
 oznacza jednak obiekt przypisany do adresu, to jak wywołać, powiedzmy adres komórki numer 4? wsk[4] da nam element tablicy. A jeśli chcę jej adres?
P-146965
michal11
» 2016-04-04 13:19:15
Dziwne, że sumowanie działa bez wartości początkowej, nie powinno.

Jeżeli chcesz wyciągnąć adres jakiegokolwiek obiektu to używasz operatora &, w twoim przykładzie adres obiektu spod 4 el. tablicy to
& tab[ 4 ]
.
P-146966
« 1 »
  Strona 1 z 1