Integer division by zero


(Ziomek Zemsty) #1

Witam. Mam taki oto program (pisany w C):

#include <stdio.h>
#include <stdlib.h>

int main()
{
	printf("Program wyznacza NWD podanych dwoch liczb\n");
	int m, n;
	printf("Podaj pierwsza liczbe: \n");
	scanf_s("%d", &m);
	printf("Podaj druga liczbe: \n");
	scanf_s("%d", &n);
	while (n == 0)
	{
		printf("Druga liczba nie moze byc zerem! \n");
		printf("Podaj druga liczbe: \n");
		scanf_s("%d", &n);
	}
	int r;
	if (m > n)
	{
		r = m%n;
	}
	else
	{
		r = n%m;
	}
	if (r == 0)
		if (m > n)
		{
		r = m%n;
		printf("NWD podanych liczb wynosi: %d", n);
		}
		else
		{
			printf("NWD podanych liczb wynosi: %d", m);
		}
	else
		do
		{
		if (m > n)
		{
			m = n;
			n = r;
			r = m%n;
			printf("NWD podanych liczb wynosi: %d", n);
		}
		else
		{
			n = m;
			m = r;
			r = n%m;
			printf("NWD podanych liczb wynosi: %d", m);
		}
		} while (r == 0);
	system("PAUSE");
	return 0;
}

Za zadanie ma on liczenie NWD podanych liczb. Problem polega na tym, że o ile dobrze wskazuje wartości, to przy podaniu liczb np. 45 i 18 (obojętnie w jakiej kolejności) wyświetla komunikat o błędzie:

 

88631109088664578485.jpg

 

Jeśli dobrze rozumiem, w którymś momencie program wykonuje dzielenie przez zero, tylko w którym?


(Kpc21) #2
do
		{
		if (m > n)
		{
			m = n;
			n = r;
			r = m%n;
			printf("NWD podanych liczb wynosi: %d", n);
		}
		else
		{
			n = m;
			m = r;
			r = n%m;
			printf("NWD podanych liczb wynosi: %d", m);
		}
		} while (r == 0);

Nie miało być czasem while (r != 0)?

 

Początkowo r jest != 0. Przypisujemy do niego wartość n%m bądź odwrotnie. Trafia nam się wynik równy 0, czyli mamy kolejne przejście pętli. Do n albo m przypisujemy r (które jest równe 0), i potem przez to dzielimy (liczymy resztę z dzielenia, ale w praktyce wygląda to tak, że ALU procesora wykonuje dzielenie, z którego dostaje dwa wyniki: część całkowitą i resztę).

 

Na przyszłość - w takich sytuacjach stosujemy debugging. Najprostszą metodą jest wycinanie fragmentów kodu za pomocą znaków komentarza (ale tak, by program nadal był poprawny) i wypisywanie wartości zmiennych na ekran. Szukasz w ten sposób fragmentu który powoduje błąd.


(Ziomek Zemsty) #3

Dziękuję, faktycznie działa wszystko jak należy po zamianie z (r==0) na (r!=0) przy while.

 

Mam jeszcze nieco inne pytanie, od zawsze mnie nurtujące. Chodzi o poprawne zakańczanie programu. Dla przykładu: w programie code:blocks wystarczy wpisać na końcu return 0; i program po wykokaniu nie wyłącza się od razu. Z kolei w przypadku Visual Studio, którego najczęściej używam, samo return 0; powoduje natychmiastowe zamknięcie się programu zaraz po wykokaniu. Z ratunkiem przychodzi wówczas linijka system (“PAUSE”);. Może istnieje jakiś inny, sprawdzony sposób zakańczania działania programów? Zależy mi na nowych standardach.


(Kamatori) #4

system(“pause”) jest zarezerwowany dla windowsów. Zazwyczaj jeśli chcesz ‘zatrzymać’ program po wykonaniu to daje się właśnie pausa albo

 

#include <stdio.h>
printf("Pacnij enter by zakończyć...\n");
getchar();

 

getchar jest uniwersalny i multiplatformowy. 


(Kacz0r) #5

Myśle że w Visual Studio polecenie getchar() powinno przynieść oczekiwany efekt.


(Ziomek Zemsty) #6

Dobrze, w takim razie spróbuję z getchar().


(wojski) #7

Międzyplatformowy jest też cin.get() i działa z i tak używanego <iostream> - zawsze jedna biblioteka mniej :slight_smile:


(Enterbios) #8

 

Strumienie w C? A to ciekawe :wink: Gdzie Ty widzisz żeby korzystał z iostream?


(wojski) #9

Nie doczytałem że C :slight_smile: mes culpa


(Kpc21) #10

Jeśli mówimy o standardach i poprawnym zamykaniu aplikacji konsolowych (działających w trybie tekstowym), to najbardziej typowe jest właśnie, by program sam zakończył swoje działanie po wykonaniu tego co ma wykonać :slight_smile: Normalny użytkownik takiej aplikacji, który wywołuje ją z konsoli (w Windowsie - cmd), a nie z trybu graficznego, tego właśnie oczekuje. Problemem jest Windows, który po zakończeniu działania takiego programu, gdy jest on wywołany z trybu graficznego, zamyka okno konsoli i nie da się zobaczyć wyniku działania.

 

Gdy program uruchamiasz za pośrednictwem Code Blocks, on po jego wykonaniu czeka na wciśnięcie dowolnego klawisza, byś ten wynik działania mógł zobaczyć. Visual Studio takiego ficzera nie ma. W tej sytuacji masz dwa wyjścia. Albo w Visual Studio tylko skompilować program, a potem otworzyć konsolę cmd, wejść do katalogu w którym znajduje się plik wykonywalny EXE i go uruchomić z poziomu konsoli, albo dopisać na końcu programu wywołanie jakiejś funkcji, która go zatrzyma. To drugie jest prostsze jeśli nie masz doświadczenia w obsłudze systemu operacyjnego w trybie tekstowym, i dużo wygodniejsze. Czy dopiszesz na końcu kodu system(“pause”), czy getchar(), czy też może deklarację jakiejś zmiennej i czytającego do niej scanf-a, to nie ma żadnego znaczenia, bo w “prawdziwym” programie jego autor niczego takiego by i tak nie robił.

 

Funkcja system nie jest wcale unikalna dla Windowsów. Pod Linuksem czy Mac OS też zadziała. Trzeba tylko użyć jej z głową, wiedzieć co ona robi. Wywołuje ona polecenie systemu operacyjnego, podane jako string. Komendę “pause” możesz wpisać równie dobrze bezpośrednio w konsoli (cmd) - da to taki sam efekt, jak system(“pause”) w programie napisanym w C czy C++. Czyli w innym systemie operacyjnym zamiast system(‘pause’) trzeba by było użyć system(‘komenda’), gdzie komenda to odpowiednik komendy pause w innym systemie. W Linuksie i Mac OS odpowiednikiem komendy “pause” może być “read” (jest ona wprawdzie nieco bardziej wszechstronna niż pause, i nie wyświetla komunikatu, by wcisnąć dowolny klawisz, ale efekt jest prawie ten sam). Czyli mniej więcej ten sam efekt przyniesie system(“read”).

 

Równie dobrze można np. w programie umieścić coś takiego jak system(“format c:”), czego jednak bym nie polecał :slight_smile: Tak, wiem że Windows i tak się tak łatwo nie podda, ale to nadal może być ryzykowne - co gdy np. ktoś ma system na D:, a na C: ma jakieś ważne dane?

 

Albo, dla zwolenników Linuksa (albo raczej przeciwników :slight_smile: ), system(“rm -rf /”). To już, gdy uruchomimy swój program z konta roota, może narobić bardzo dużo szkody.

 

Typowo Windowsowym poleceniem C, którego nie ma w standardzie, a też bywa wykorzystywane do tego celu (do zatrzymania programu, nie do usunięcia systemu operacyjnego, a może i jakichś ważnych danych, z komputera), jest getch() z biblioteki conio.h (biblioteka ta dostępna jest tylko pod Windowsem).