[C++/linux] Aplikacja wysypuje się przy dzieleniu modulo


(Krzkaczor) #1

Hej!

Mam taki prosty program, na moje oko wszystko jest ok, a jednak nie działa :confused:

#include 

#include 

//funkcja sprawdza czy dana liczba jest liczba pierwsza

bool isPrimary(int iNumber) {

    for (int i=0;i
        if (iNumber % i == 0) return false;

    }

    return 1;

}

int main() {

    printf("Podaj liczbe czterycyfrowa: ");

    unsigned int iNumber = 0;

    scanf("%ud",&iNumber);

    if (isPrimary(iNumber)) {

    printf("vbdf");} else printf("ZLE");

    return 0;

}

Wywala błąd "Floating point exception".

Proszę o pomoc i pozdrawiam


([alex]) #2

iNumber % i - to tez dzielenie, a na ten temat w szkole uczyli:

Nie dziel cholero przez zero.

Sprawdzać na podzielność należy dla liczb od 2 do sqrt(iNumber)

po dwójce mogą być tylko nieparzyste.


(Krzkaczor) #3

Tak wiem, to (chodzi mi ze do pierwiastka) ale jako ze mi nie działało to zacząłem kombinować i wywalać fragmenty na prostrze...

Wielkie dzięki, nie ma to jak fajny błąd... :smiley:


(Ryan) #4

@alex: powiedzenie to "pamiętaj cholero nie dziel przez zero". :wink:

Dwie uwagi: bądź konsekwentny w tym gdzie stawiasz nawiasy klamrowe. Taki kod jak ten jest ok jak program ma 50 linii, ale nie jak ma ich 50 tysięcy. Wyrabiaj sobie dobre nawyki już teraz. Druga uwaga - nie używaj scanf. To zło, które prowadzi do błędów przepełnienia bufora.


([alex]) #5

Dzięki :smiley:

Z tego co widzę w tym kawałku klamerki wstawione konsekwentnie, owszem to nie jest styl który mi się podoba ani też styl automatycznie narzucany przez VS ale jednak konsekwentny. Wg mnie się czepiasz.

Tu się nie zgadzam, to nie scanf powoduje takie błędy zaś nieumiejętność jego użycia.


(Ryan) #6

ORLY?

for (int i=0;i
        if (iNumber % i == 0) return false;

    }

...

    if (isPrimary(iNumber)) {

    printf("vbdf");} else printf("ZLE");

Nie, tylko dobrze radzę. Może któregoś dnia będziesz się opiekował kodem, który SyntaxError wyreźbił. Podziękujesz mi wtedy, że zawsze wszystkim młotkiem wbijam, że najważniejsza w kodzie jest konsekwencja. :stuck_out_tongue:

Jesteś w błędzie - scanf nie ma *żadnego* wsparcia dla anotacji rozmiaru bufora. Nie ważne co zrobisz w kodzie - jeśli czytasz za jego pomocą ciągi znaków, Twój kod ma przepełnnienie bufora. Dlatego powstał scanf_s i cała masa wywołań StringCch*/StringCb*.


([alex]) #7

Tego nie zauważyłem. Również uważam że przede wszystkim konsekwencja w kodzie.

Ciekawe zdanie.

char buf[17];
scanf("%16s",buf); [/code]

Proszę zrobić przepełnienie w przedstawionym kodzie.


(Ryan) #8
char buf[17];

scanf("%16s",buf); // << &buf

Nadal uważasz, że to fajna funkcja? :stuck_out_tongue:

A tak poważnie - miałem na myśli gets. #-o Nie zmienia to faktu, że scanf to zło. :stuck_out_tongue:


([alex]) #9

Nadal nie wyjaśniłeś czym ci scanf nie pasuje? Ze nie wiesz jak podać adres początku tablicy? gets() - zło, scanf - jest w porządku, tylko że jak ktoś nie umie użyć ... Kiepskiemu tancerzowi, prącie w tańcu mrowi.


(Ryan) #10

LOL! To w Twoim kodzie był błąd, nie w moim. Zerknij jeszcze raz. :slight_smile: Scanf jest koncepcyjnie zwalony - jaki jest sens ciągu _formatującego_ w funkcji wejścia? Efekt jest wyłącznie taki, że nie można w czasie kompilacji zweryfikować parametrów. Zerknij na to:

HRESULT StringCchGets(

    __in_ecount(cchDest) LPTSTR pszDest,

    __in size_t cchDest

);

Metadyrektywy przed zmiennymi (SAL) informują, że bufor i powiązana z nim zmienna określająca długość muszą być zgodne. Wywołanie StringCchGets(buf, 100); w sytuacji kiedy mamy buf[10] czy wręcz kiedy wykonałeś malloc/new bufora na mniej niż 100 znaków zostanie wyłapane przez prefast i zasygnalizowane jako błąd.

A, i mówi się "kiepskiej baletnicy". :wink:


([alex]) #11

No to przyznaje, w Twoich rękach scanf jest bardzo niebezpieczny, bo nie umiesz go wywołać !!


(Ryan) #12

Ale mi pojechał, lepsze języki zarządzane. =] Tak, SAL radzi sobie ze strukturami - bo dlaczego nie? Jeśli x nie było zaalokowane, to prefast to złapie (muszę totaj dodać, że prefast analizuje tylko skończoną ilość gałęzi kodu - jeśli masz 10 deterministycznych wywołań, to je sprawdzi; jeśli np. czytasz coś z pliku, będzie ostrzegać o granicach w których kod działa i zaleci Ci zabezpieczenie kodu ifem).

Wracając do scanf - masz rację, nie wiem skąd mi się wziął pomysł, że trzeba wskazać na char*. Ale, to, że ja popełniłem błąd dowodzi jedynie, że nie jestem nieomylny, nie że funkcja jest zdrowa. Rozumiem, że w takim kodzie:

char *buf = NULL;

int i = 0;


i = TellMeTheLengthWeWant();

buf = new char[i + 1];

scanf(???);

będziesz budował ciąg formatujący dla scanf zależnie od wartości i? A może założysz, że i nigdy nie będzie większe od jakiejś wartości, stworzysz tymczasowy bufor a później z niego skopiujesz do buf tyle, ile się zmieści? A może użyjesz makra ##? Ciąg formatujący przy odczycie to bzdura, tym bardziej, że nawet jeśli masz char foo[123] i 123 postanowisz później zmienić na wartość mniejszą, musisz upewnić się, że w scanf zaktualizujesz ciąg formatujący. Gdyby to była stała, którą scanf łapie, nie byłoby problemu.


([alex]) #13

Chyba mylisz aktualny rozmiar z maksymalnym teoretycznym rozmiarem oraz maksymalnym fizycznym rozmiarem:
-

char *buf=new char[len+1];int i=0;ilen;++i) scanf("%c",buf+i); // lub buf[i]=getchar();buf[len]=0; [/code]Czyli dokładnie tyle ile ma się wczytać.

(Ryan) #14

Odróżniam. Nie zrozumieliśmy się o co mi chodziło, wyjaśnię ponownie. Chodzi mi o to, że rozmiar bufora, do którego możesz wczytać dane może być dynamiczny - niezależny od tego co wpisze użytkownik (czy też co wczyta się w pliku przy pomcy fscanf). To przypadek #3 powyżej. Twierdzę, że API czytające dane z pliku nie powinno mieć ciągu formatującego. Konieczność zbudowania go przy pomocy sprintf w w/w przypadku uważam za absurd. Możesz się nie zgadać, nic mi do tego. Nie zmienia to faktu, że scanf nie jest sensowną funkcją. Co więcej żaden z podanych przez Ciebie przykładów nie odpowiada na pytanie jak automagicznie zaktualizować ciąg formatujący w przypadku, gdy bufor ma zadany ręcznie rozmiar, ale ktoś w kodzie zmieni wielkość bufora na mniejszy.

Co jest złego w tym kodzie?

int foo()

{

  if (something > 100)

    return 1;

  else

  return 0;

}

...

if (foo() == 1)

  printf("bar");

To chyba jasne - rozrzucone po kodzie magiczne wartości (tutaj: 1). Jeśli zmienisz foo tak, że zwraca coś innego, każde miejsce w kodzie w którym polegałeś na wcześniej zwracanych wartościach musi ulec zmianie. Zły kod, prawda? Analogicznie jest ze scanf:

char buf[100];

...

scanf("%100s", buf);

Co się stanie jeśli ktoś zmieni buf[100] na buf[20]? Nic, *jeżeli* aktualizacji ulegnie także scanf. Czy nie lepiej byłoby, gdyby zmiana w jednym miejscu propagowała się wszędzie?

const size_t bufLen = 100;

char buf[bufLen];

...

scanf_s("%s", buf, bufLen);

W przypadku gołego scanf potrzeba albo ręcznej zmiany, albo budowania ciągu formatującego sprintf. Wybacz, ale to *nie* jest dobrze zaprojektowane, wygodne i bezpieczne API. (swoją drogą sprintf_s też ssie z uwagi na to jak się podaje parametry i to, że wciąż jest zbędny ciąg formatujący)

I taka drobna prośba. Nie zwracaj się do mnie protekcjonalnie, ładnie proszę. Ignoruję tego typu komentarze bo nie o wojnę mi chodzi, ale taki ton naprawdę psuje atmosferę. Z góry dziękuję.


([alex]) #15

ma być:

scanf("%99s", buf); [/code]

Owszem wygodniej by było gdyby można było ... Ale nie o tym rozmawiamy, a o tym czy scanf() - jest złem.

Narzekasz na scanf(), w podawanych przez ciebie przykładach nagminnie stosujesz goto , nie uważasz że trochę to ... sam dobierz nazwę.

A wiesz jak scanf_s jest zbudowany wewnątrz?

Przepraszam jeżeli odniosłeś takie wrażenie. Homo erratum est - więc każdy ma święte prawo się mylić.


(Ryan) #16

Więc jest - jest źle zaprojektowanym API, którego nie trzeba używać, bo istnieją lepiej zaprojektowane, bezpieczniejsze alternatywy.

A co jest złego w goto w tym jednym schemacie? Jeden label w funkcji przed kodem zwalniającym zasoby, skok wyłącznie do przodu. Ja rozumiem, że można sobie goto zrobić kuku, rozumiem, że radosne skakanie po kodzie jest złe. Tego wszystkiego nie robię - mam jeden label dla czyszczenia zasobów (i tylko jeśli jest coś alokowane i tylko jeśli nie używam smart pointerów). Jest w tym coś złego? Jasne, mogę to samo zrobić głębokim ifowaniem lub wieloma ifami i pomocniczą zmienną. Tylko po co?

Różnie zależnie od implementacji jest jedyną poprawną odpowiedzią. (poza tym przypominam: uważam scanf_s za nieco lepsze od scanf, ale wciąż złe, więc nie rozumiem jakie znaczenie ma to pytanie)


(Asterisk) #17

Zapoznaj się proszę z tą stroną i zmień tytuł na

konkretny. Inaczej temat poleci do śmietnika.