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

Znajdowanie liczby najbliższej danej - problem

Object Storage Arubacloud
0 głosów
3,035 wizyt
pytanie zadane 17 września 2015 w C i C++ przez mystery.of.silence Nowicjusz (230 p.)
edycja 19 września 2015 przez mystery.of.silence

Witam :) Próbuję napisać kod do zadania z odcinka 10 kursu o C++ o znajdywaniu ekstremów. Zadanie polega na wpisaniu do konsoli 5 liczb, a nastepnie wyznaczeniu z podanych tej najbliższej ich średniej. Zatem wyznaczyłam wartość bezwzględną różnicy każdej z nich od średniej, a następnie chciałam sprawdzić, która z obliczonych wartości jest najmniejsza. Jednak napotkałam pewne problemy. Obliczając to w osobnej funcji i przenosząc tablicę z liczbami za pomocą wskaźników, nie moglam poradzić sobie z dalszym schematem. Dlatego kod być może wygląda troche dziwnie, ale koniec końców myślałam, że zadziała, a jednak zamiast poprawnych wyników w konsoli pojawiają się dziwne, ogromne liczby... W szczególności zmienna "m" mnie zadziwia. Nie mam pojęcia skąd to się bierze i dlaczego nie działa tak jak powinno. Gdyby ktoś przeanalizował kod i znalazł problem, byłabym bardzo wdzięczna :)

#include <iostream>
#include <stdio.h>
#include <cmath>
#include <cstdio>


using namespace std;


double srednia(double *liczby, int ile);
double najblizsza (double *podane, double sredniaa, int ile);

int main()
{
	double tablica[5];
	double sre;
	
	cout<< "Podaj 5 liczb (jesli sa to liczby 'z przecinkiem' zamiast przecka uzywaj 'kropki', a kolejne liczby oddzielaj enterem): "<<endl;
		for (int i=0; i<5; i++)
			{
				cin>>tablica[i];	
			}
	
	
		sre= srednia(tablica, 5);
		
		cout<< "Srednia podanych liczb to: "<<sre<<endl;
		cout<< "Liczba najblizsza sredniej, to: "<< najblizsza(tablica, sre, 5)<<endl;
	
	
	getchar();getchar();
	return 0;
}

double srednia(double *liczby, int ile)
{
	double suma; 
	
	for(int i=0; i<ile; i++)
		{
			suma+=*liczby;
			liczby++;	
		}	
		
	return suma/ile;
	
}

double najblizsza(double *podane, double sredniaa, int ile)
{
	double odleglosc[ile];
	double m;
	int x;
	double y;
	m= odleglosc[0];

	
	for (int i=0; i<ile; i++)
	{ odleglosc[i]= abs( *podane-sredniaa );
	
	
			
		*podane++;
	}
	
	for(int i=0; i<ile; i++)
	{
		
		if (odleglosc[i] < m )
			{	m= odleglosc[i];
		    	x=i; 
			}
		
	}
	
	for(int i=0; i<ile; i++)
	{
		if(i==x) *podane= y;	
		
		
		
		*podane++;	
	}

	
	return y;
	
}

 

1 odpowiedź

0 głosów
odpowiedź 17 września 2015 przez Krawiec91 Pasjonat (19,600 p.)

Tak na pierwszy rzut oka to nie mogłem wyłapać, ale w końcu się udało.
1. Linia 55 - do zmiennej m, której potem używasz do porównań, przypisujesz przypadkową wartość. Utworzona w linii 51 tablica nie jest zainicjalizowana żadnymi wartościami, a jest to zmienna lokalna, więc może być tam cokolwiek, choćby kosmicznie mała liczba, która w większości przypadków, może powodować daremne dalsze obliczenia. Przeniosłem utworzenie i inicjalizację tej zmiennej za pętlę, w której obliczane są wartości odchyleń od średniej, czyli wtedy przypisuje jej jakąś pewną wartość.
2. Pierwsza i trzecia pętla for. W pierwszej pętli przesuwasz wskaźnik na ostatni element tablicy. A następnie w trzeciej pętli odwołujesz się do wartości spoza tablicy, więc może być tam cokolwiek. W pierwszej pętli użyłem indeku tablicy, aby pobierać wartości z kolejnych pól tablicy, ale bez ruszania wskaźnika.
3. Linia 78 - używasz niezainicjalizowanej zmiennej y, czyli jak wcześnie może być tam cokolwiek. A druga sprawa, że pewnie w ferworze walki przypisujesz ją do kolejnych pól tablicy, a na koniec i tak zwracasz z funkcji tą niezainicjalizowaną zmienną y.

Ogólnie polecałbym, jak już używasz wskaźników w funkcjach, to po prostu w samej funkcji utworzyć sobie wskaźnik i przypisać do niego przekazany z programu głównego do funkcji adres i na nim działać, żeby nie ruszać tego "oryginalnego" wskaźnika. Wskaźniki ogólnie do podszlifowania jeszczesmiley. Poniżej poprawiony kod Twojej funkcji i moja propozycja, krótszej wersji tej funkcji.
 

 double najblizsza(double *podane, double sredniaa, int ile)
{
    double odleglosc[ile];
    double m;
    int x;
    double y;



    for (int i=0; i<ile; i++)
    { odleglosc[i]= abs( *(podane+i)-sredniaa );



        //podane++;
    }
    m= odleglosc[0];
    for(int i=0; i<ile; i++)
    {


        if (odleglosc[i] < m )
            {   m= odleglosc[i];
                x=i;
            }

    }

    for(int i=0; i<ile; i++)
    {
        if(i==x)
           y= *podane;



        podane++;
    }


    return y;

}


 

double najblizsza(double *podane, double sredniaa, int ile)
{
    double odleglosc[ile];
    double m;
    int x=0;

    for (int i=0; i<ile; i++)
    {
        odleglosc[i]= abs( *(podane+i)-sredniaa );
    }

    m= odleglosc[0];
    for (int i=1;i<ile;i++)
    {
        if (odleglosc[i]<m)
        {
            m=odleglosc[i];
            x=i;
        }
    }
    return *(podane+x);
}

 

komentarz 17 września 2015 przez Krawiec91 Pasjonat (19,600 p.)

 Jeszcze jedną rzecz wyhaczyłem:
 

double srednia(double *liczby, int ile)
{
    double suma; 
     
    for(int i=0; i<ile; i++)
        {
            suma+=*liczby;
            *liczby++;  
        }   
         
    return suma/ile;
     
}


W pętli miałaś pewnie zamiar inkrementować wskaźnik liczby, więc derefencja (*) tego wskaźnika nie ma tu żadnego sensu. Program się na tym nie wysypał, bo inkrementacja  ma wyższy priorytet niż dereferencja i tak czy siak wskaźnik się przesuwał i nie modyfikowana była zawartość tablicy, no ale lepiej tego unikać.

komentarz 19 września 2015 przez mystery.of.silence Nowicjusz (230 p.)

Dziękuję za bardzo wyczerpującą odpowiedź, faktycznie wskaźniki to nadal troche niezrozumiały temat dla mnie :/

Poprawiłam kod, niestety ten pierwszy w dalszym ciągu nie działa poprawnie i nadal są duże liczby. Natomiast drugi, skrócony, faktycznie działa już jak najbardziej poprawnie :) Dlatego bardzo dziękuję, za tak dokładne przeanalizowanie kodu i znalezie problemu :) 

Przepraszam za zawracanie głowy, ale korzystając z okazji chciałam jeszcze zapytać o o jedną rzecz, bo nadal zrozumienie tego sprawia mi problem ;) Dlaczego postać *(wskaznik+i) zamiast inkrementacji wskaźnika naprawiła program? Faktycznie w takiej formie wszystko działa jak należy, ale cieżko mi zrozumieć dlaczego zrobiło to taką różnicę.

Co do funkcji double srednia(double *liczby, int ile), rzeczywiście, nie miało to kompletnie sensu. Dziekuję za spostrzegawczość :) 

komentarz 19 września 2015 przez Krawiec91 Pasjonat (19,600 p.)
Przy postaci *(wskaznik+i) wskaźnik przechowuje ten sam adres, który dostał jako argument funkcji, nie jest on zmieniany. Zmieniane jest tylko przesunięcie od tego adresu.
Przy zapisie z inkrementacją, wartość wskaźnika (adres) jest zmieniany. Taka jest różnica między tymi dwoma zapisami.
Przekładając to na grunt praktyki. Na przykładzie Twojego programu, dokładniej funkcji wyznaczającej liczbę najblizej średnie. Masz 2 pętle for, które zmieniają wartość wskaźnika. Pierwsze użycie wskaźnika jeszcze nie rozwala programu, bo poruszamy się po elementach tej tablicy. Drugie użycie rozwala całą sprawę, bo wychodzisz poza obszar tablicy. C++ nie sprawdza, nie informuje o tym, że takie coś nastąpiło. Wiara w umiejętności kodera i jego odpowiedzialność.;)
Przy użyciu zapisu *(tablica+i), po przejściu przez 2 pętle for wskaźnik tablica będzie przechowywał cały czas tą samą wartość, czyli pierwszy element tablicy.

Oba zapisy są poprawne, tylko trzeba uważać z tą modyfikacją wskaźnika jeśli jest załóżmy kilka pętli, gdzie wskaźnik może być zmieniany. Jak w takim zapisie robiłem, to sobie pomocniczy wskaźnik, do którego przypisywałem sobie oryginalny wskaźnik. Jedna pętla zakończona, to wtedy ponownie przypisywałem oryginalny adres do pomocniczego wskaźnika i tak w kółko.

Podobne pytania

0 głosów
1 odpowiedź 272 wizyt
+1 głos
3 odpowiedzi 599 wizyt
0 głosów
4 odpowiedzi 349 wizyt

92,579 zapytań

141,432 odpowiedzi

319,657 komentarzy

61,963 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!

...