Diskuze: přetečení

C a C++ C a C++ přetečení

Aktivity (1)
Avatar
lukes90
Člen
Avatar
lukes90:16.10.2016 17:11

Ahoj,

jak ošetřím, aby nikdo nemohl zadat do proměnné int větší číslo, aby nepřeteklo.

Předem děkuji za odpověď.

 
Odpovědět 16.10.2016 17:11
Avatar
ostrozan
Redaktor
Avatar
Odpovídá na lukes90
ostrozan:16.10.2016 17:55

úplně jednoduše - dej proměnné typ long, případně long long :-)

ale vážně - porovnej hodnotu s max hodnotou pro int - https://www.tutorialspoint.com/…limits_h.htm

 
Nahoru Odpovědět 16.10.2016 17:55
Avatar
Odpovídá na ostrozan
Libor Šimo (libcosenior):17.10.2016 17:01

Predpokladam, ze nemyslis provnat to matematicky.

Nahoru Odpovědět 17.10.2016 17:01
Aj tisícmíľová cesta musí začať jednoduchým krokom.
Avatar
ostrozan
Redaktor
Avatar
ostrozan:17.10.2016 21:53

No původně jsem to tak myslel, ale teď si uvědomuju, že nevím jestli je dotaz na C, nebo C++.
Bude to podobné, ale C++ má asi víc nástrojů jak toho docílit.

 
Nahoru Odpovědět 17.10.2016 21:53
Avatar
Odpovídá na ostrozan
Libor Šimo (libcosenior):18.10.2016 6:57

V céčku by bola optimálna funkcia, ktorá berie ako parameter reťazec a vracia int, ak spĺňa parametre int.

Nahoru Odpovědět 18.10.2016 6:57
Aj tisícmíľová cesta musí začať jednoduchým krokom.
Avatar
lukes90
Člen
Avatar
lukes90:18.10.2016 10:59

Byl to dotaz na c++. Omllouvám se za neuvedení. :-)

 
Nahoru Odpovědět 18.10.2016 10:59
Avatar
Odpovídá na lukes90
Libor Šimo (libcosenior):18.10.2016 13:11

Otestuj.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <assert.h>
#define TESTY // pri zakomentovani budu testy odstavene

int parse_int(char *number)
{
    char *pom;

    pom = (char*)malloc(128);
    sprintf(pom, "%d", atoi(number));
    if (strcmp(number, pom)) {
        printf("Cislo nie je int!\n");
        return -1;
    }
    return atoi(number);
}

#ifdef TESTY
#define TESTY

int main(void)
{
    /** testy funkcie int parse_int(char *number) **/
    assert(parse_int("123456") == 123456);
    assert(parse_int("-123456") == -123456);
    assert(parse_int("2147483647") == INT_MAX);
    assert(parse_int("-2147483648") == INT_MIN);
    assert(parse_int("2147483657") == -1);
    assert(parse_int("2147483747") == -1);
    assert(parse_int("2147484647") == -1);
    assert(parse_int("2147493647") == -1);
    assert(parse_int("2147583647") == -1);
    assert(parse_int("2148483647") == -1);
    assert(parse_int("2157483647") == -1);
    assert(parse_int("2247483647") == -1);
    assert(parse_int("3147483647") == -1);
    assert(parse_int("2147483549") == 2147483549);
    assert(parse_int("2146499999") == 2146499999);
    assert(parse_int("2099999999") == 2099999999);
    assert(parse_int("2157483647265847589565888885445588555326598") == -1);

    printf("\n\nTesty presli.\n");

    return 0;
}

#endif // TESTY
Nahoru Odpovědět 18.10.2016 13:11
Aj tisícmíľová cesta musí začať jednoduchým krokom.
Avatar
Martin Dráb
Redaktor
Avatar
Odpovídá na Libor Šimo (libcosenior)
Martin Dráb:18.10.2016 20:37

Tohle nebude fungovat, pokud chceš podporovat i čísla v jiné než desítkové soustavě, protože třeba 0x10 není řetězcově rovno 16. Plus tam alokuješ paměť, kterou neuvolňuješ. Vzhledem k tomu, kolik %d zabere max. znaků, ani alokace není třeba, stačí dostatečně velká lokální proměnná na zásobníku.

Nebylo by nejlepší použít třeba strtoll a náslecně porovnat s maximální/minimální hodnotou intu?

Nahoru Odpovědět 18.10.2016 20:37
2 + 2 = 5 for extremely large values of 2
Avatar
DarkCoder
Člen
Avatar
Odpovídá na lukes90
DarkCoder:19.10.2016 0:22

Aby nikdo nemohl zadat do proměnné číslo tak aby nepřeteklo nezaručíš nikdy. Nepozorný programátor Ti ji naplní nesmyslnou hodnotou velmi snadno, třeba při inicializaci nebo přiřazení. Při překladu se mu zobrazí varovné hlášení, ale ne chybové. Viz. fragment kódu:

#include <limits.h>
#include <stdio.h>

...
int i = 1000000000000;
if((i>=INT_MIN)&&(i<=INT_MAX)) printf("%d", i);
else printf("i je mimo rozsah.\n");
...

Ikdyž je hodnota i evidentně mimo rozsah, hláška o tom, že tomu tak je, se nezobrazí.

Takže jakým způsobem, odkud se očekává hodnota proměnné typu int ze strany uživatele (ze souboru, z konzole, jako argument příkazového řádku, prostřednictvím síťové komunikace, atd)?

 
Nahoru Odpovědět 19.10.2016 0:22
Avatar
ostrozan
Redaktor
Avatar
Odpovídá na lukes90
ostrozan:19.10.2016 5:07

Tady je několik různých řešení

 
Nahoru Odpovědět 19.10.2016 5:07
Avatar
Odpovídá na Martin Dráb
Libor Šimo (libcosenior):19.10.2016 5:31

Nepredpokladal som, ze by chcel pouzit cisla ine ako desiatkovu sustavu a pre nu to funguje bezvadne. Ano mas pravdu v alokacii. Malo to byt staticky.

Nahoru Odpovědět 19.10.2016 5:31
Aj tisícmíľová cesta musí začať jednoduchým krokom.
Avatar
Odpovídá na DarkCoder
Libor Šimo (libcosenior):19.10.2016 7:17

Ako som už písal vyššie, vstup musí byť string.

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>

using namespace std;

int parse_int(char *number)
{
    char pom[128];
    sprintf(pom, "%d", atoi(number));
    if (strcmp(number, pom)) {
        cout << "Cislo nie je int!" << endl;
        return -1;
    }
    return atoi(number);
}

int main(void)
{
    char cislo[] = "10000000000000000";
    parse_int(cislo);
    return 0;
}

Ale funguje to iba na typ int.
Myslím, že toto by mohla byť odpoveď na prvú otázku.

Nahoru Odpovědět 19.10.2016 7:17
Aj tisícmíľová cesta musí začať jednoduchým krokom.
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Libor Šimo (libcosenior)
DarkCoder:19.10.2016 15:43

Přesněji řečeno vstupní data by měla být implementována a uložena do proměnné typu pole znaků. Předtím by však měla být otestována na paměťový rozsah a vyhodnocena na platnost, aby se nám do pole vešla. Zde už pracujeme s hotovou hodnotou. Řešení je to ale pěkné, neboť funkce atoi() dělá práci za nás. V případě přetečení rozsahu vrací hodnotu INT_MIN respektive INT_MAX což ve spojitosti s funkcí strcmp() dává požadovaný výsledek. Jedna věc tam ale není správná a to návratová hodnota funkce parse_int(). Nelze vracet hodnotu -1 v případě, že číslo není v daném rozsahu, neboť hodnota -1 koliduje s návratovou hodnotou získanou pomocí funkce atoi() v případě, že testovaným číslem je -1 a není tak zřejmé, jaká situace nastala.

 
Nahoru Odpovědět 19.10.2016 15:43
Avatar
Martin Dráb
Redaktor
Avatar
Odpovídá na DarkCoder
Martin Dráb:19.10.2016 16:48

Nevím, zda by nebylo elegantnější zkusit strtol, která by měla v případě, že se zadaná hodnota do typu nevejde, nastavovat errno na ERANGE. Je tam ale třeba dávat pozor i na to, zda vůbec na vstupu číslo je, popř. zda nám stačí, pokud vstupní řetězec (resp. proměnná) číslem začíná, nebo zda musí obsahovat pouze číslo v řetězcové podobě. Chce to zkrátka pořádně přečíst dokumentaci a popřemýšlet.

Hm a to atoi-strcmp řešení stejně nebude fungovat pro řetězce ve stylu 00025.

Nahoru Odpovědět  +1 19.10.2016 16:48
2 + 2 = 5 for extremely large values of 2
Avatar
Odpovídá na Martin Dráb
Libor Šimo (libcosenior):19.10.2016 17:17

Tie nuly ma nenapadly, ale staci mala uprava funkcie a bude to ok.

Nahoru Odpovědět 19.10.2016 17:17
Aj tisícmíľová cesta musí začať jednoduchým krokom.
Avatar
Martin Dráb
Redaktor
Avatar
Odpovídá na Libor Šimo (libcosenior)
Martin Dráb:19.10.2016 17:22

Ano, stačí malá úprava. Ale už mi to začíná připadat jako takové flikování... :-).

Nahoru Odpovědět 19.10.2016 17:22
2 + 2 = 5 for extremely large values of 2
Avatar
Odpovídá na DarkCoder
Libor Šimo (libcosenior):19.10.2016 17:23

Aj problem s -1 sa da jednoducho vyriesit. ;-)

Nahoru Odpovědět 19.10.2016 17:23
Aj tisícmíľová cesta musí začať jednoduchým krokom.
Avatar
Odpovídá na Martin Dráb
Libor Šimo (libcosenior):19.10.2016 17:24

Nie som profik, len sa zabavam. :-)

Nahoru Odpovědět 19.10.2016 17:24
Aj tisícmíľová cesta musí začať jednoduchým krokom.
Avatar
Odpovídá na Martin Dráb
Libor Šimo (libcosenior):19.10.2016 17:30

Sorry, retazec -1 spracuje spravne a vrati cislo -1.

Nahoru Odpovědět 19.10.2016 17:30
Aj tisícmíľová cesta musí začať jednoduchým krokom.
Avatar
Libor Šimo (libcosenior):19.10.2016 17:32

Blbnem. :-D

Nahoru Odpovědět 19.10.2016 17:32
Aj tisícmíľová cesta musí začať jednoduchým krokom.
Avatar
ostrozan
Redaktor
Avatar
ostrozan:19.10.2016 17:48

a co třeba

int cisloInt;
long vstupni_data;

scanf("%[0-9]%li",&vstupni_data);
if(vstupni_data>INT_MAX)
{
////chyba
}
else cisloInt=vstupni_data;

a máš to i s ošetřením vstupu, aby bral jen numerické znaky

 
Nahoru Odpovědět 19.10.2016 17:48
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Martin Dráb
DarkCoder:20.10.2016 3:43

Fragment kódu za použití funkce strtol()

unsigned int i = 0;
long int li;
char cislo[][15] = { "0", "-0", "+0", "100", "-100", "+100", "01000", "-01000", "+01000",
                              "2147483647", "-2147483648", "2147483648", "-2147483649", "" };

...
printf("%-16s%-16s%s\n\n", "Retezec", "Status", "Cislo");
while (strcmp(cislo[i], "")) {
        errno = 0;
        li = strtol(cislo[i], NULL, 10);
        if (errno==ERANGE) printf("%-16s%-16s%ld\n", cislo[i], "Mimo rozsah", li);
        else printf("%-16s%-16s%ld\n", cislo[i], "OK", li);
        i++;
}
...

I funkce atoi() resp. atol() nastavují při přetečení hodnotu makra errno na ERANGE. Výhodou funkce strtol() je ale možnost velmi snadno pracovat s konkrétní číselnou soustavou, možnost zastavit skenování a širší zpracování bílých znaků, jako třeba již zmíněné nuly. Použití funkce strtol() je tedy lepším řešením..

 
Nahoru Odpovědět  +1 20.10.2016 3:43
Avatar
Odpovídá na DarkCoder
Libor Šimo (libcosenior):20.10.2016 6:32

Ešte vyriešiť napr. takýto string "2548R569", aby to vpísalo mimo rozsah a nie "2548R569" OK 2548.
Preklep sa užívateľovi priptrafí.

Nahoru Odpovědět 20.10.2016 6:32
Aj tisícmíľová cesta musí začať jednoduchým krokom.
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Libor Šimo (libcosenior)
DarkCoder:20.10.2016 9:25
while (strcmp(cislo[i], "")) {
        errno = 0;
        if ((cislo[i][0] != '+') && (cislo[i][0] != '-') && (!isdigit(cislo[i][0]))) errno = ERANGE;
        else {
                for (j = 1; cislo[i][j]; j++) {
                        if (!isdigit(cislo[i][j])) {
                                errno = ERANGE;
                                break;
                        }
                }
                if (!errno) li = strtol(cislo[i], NULL, 10);
        }
        if (!errno) printf("%-16s%-16s%ld\n", cislo[i], "OK", li);
        else printf("%-16s%-16s\n", cislo[i], "Mimo rozsah");
        i++;
}

Doplněno o platné znaky na konkrétních pozicích znakového pole..

 
Nahoru Odpovědět  +1 20.10.2016 9:25
Avatar
Martin Dráb
Redaktor
Avatar
Odpovídá na DarkCoder
Martin Dráb:20.10.2016 9:48

To bych spíš využil druhého parametru funkce strtol, abych nemusel provádět psí kusy a dělat v podstatě již to, co ta funkce tak jako tak provádí.

while (strcmp(cislo[i], "")) {
        char *endptr = cislo[i];
        errno = 0;
        li = strtol(cislo[i], &endptr, 10);
        if (*endptr != '\0')
            errno = ERANGE;

        if (errno==ERANGE) printf("%-16s%-16s%ld\n", cislo[i], "Mimo rozsah", li);
        else printf("%-16s%-16s%ld\n", cislo[i], "OK", li);
        i++;
}
Nahoru Odpovědět  +2 20.10.2016 9:48
2 + 2 = 5 for extremely large values of 2
Avatar
Odpovídá na Martin Dráb
Libor Šimo (libcosenior):20.10.2016 10:45
li = strtol(cislo[i], &endptr, 10);
        if (*endptr != '\0')
            errno = ERANGE;

Správne som pochopil, že 10 znamená prevod na typ int a *endptr != '\0' znamená, že typ nie je int?

Nahoru Odpovědět 20.10.2016 10:45
Aj tisícmíľová cesta musí začať jednoduchým krokom.
Avatar
Martin Dráb
Redaktor
Avatar
Odpovídá na Libor Šimo (libcosenior)
Martin Dráb:20.10.2016 11:03

10 znamená, že strtol bude akceptovat pouze řetězce zapisující čísla v desítkové soustavě. Podle mě je tato specifikace zbytečná. Obvykle dávám nulu (libovolná soustava, což efektivně znamená binární, osmičkovou, desítkovou či šestnáctkovou; záleží na prefixu čísla).

Argument endptr ti funkce naplní adresou znaku v řetězci, který se nachází hned za posledním znakem podřetězce zkonvertovaného na číslo. Takže když máš třeba řetězec 123R456, tak po jednom zavolání strtol bude endptr odkazovat na řetězec R456.

Pokud nechceš tyhle "částečné" konverze, tzn. pokud potřebuješ, aby se buď zkonvertoval celý řetězec, nebo nic, tak potřebuješ, aby po úspěšném zavolání strtol argument endptr odkazoval na konec řetězec, tedy na nulový znak.

Je ale pravda, že ta moje implementace může potlačit jiné chyby reportované fcí strtol (nebo je převede na ERANGE), což nemusí být to pravé ořechové.

Nahoru Odpovědět 20.10.2016 11:03
2 + 2 = 5 for extremely large values of 2
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Martin Dráb
DarkCoder:20.10.2016 18:00

Ona i ta moje implementace přiřazovat makru errno hodnotu ERANGE u testu na platný znak asi nebude úplně správná. A tak zde přikládám kosmeticky upravenou implementaci testování.

while (strcmp(cislo[i], "")) {
        errno = 0;
        endptr = cislo[i];
        li = strtol(cislo[i], &endptr, 10);
        if ((*endptr != '\0') || (errno)) printf("%-16s%-16s\n", cislo[i], "Mimo rozsah");
        else printf("%-16s%-16s%ld\n", cislo[i], "OK", li);
        i++;
}
 
Nahoru Odpovědět 20.10.2016 18:00
Avatar
ostrozan
Redaktor
Avatar
ostrozan:20.10.2016 19:41

Jak se na to tak dívám, už se dostáváte do oblastí, kde by se uplatnil spíš regex
Já ho teda používám všude, kde má vstup nějaký požadovaný formát (hlavně v C# aplikacích).

 
Nahoru Odpovědět 20.10.2016 19:41
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 29 zpráv z 29.