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

Wskaźniki w c, dziwny błąd

Object Storage Arubacloud
0 głosów
178 wizyt
pytanie zadane 1 grudnia 2020 w C i C++ przez jankustosz1 Nałogowiec (35,880 p.)
edycja 1 grudnia 2020 przez jankustosz1

Próbuję rozwiązać zadanie z uczelni po wysłaniu kodu sprawdzarka sprawdza kolejne testy chyba że się na którymś wywali. Metodą komentowania i wysyłania doszedłem do momentu, że wiem jaka dokładnie linijka wywala mi program, jednak wydaje mi się ona w porządku. Tu kod:

typedef struct node Node;
struct node{
    Node *g[3];
    long val;
    int vis;
};

Node *create(long val){
    Node *newNode = (Node*)malloc(sizeof(Node));
    newNode->val = val;
    for(int i = 0; i<3; i++)
        newNode->g[i] = NULL;
    newNode->vis = 0;

    return newNode;
}

#define N 100000
Node * poziom[N]; /// globalna tablica 


----------- w jakiejs funkcji:

poziom[i] = create(10); /// cos tego typu mam w pewnej funkcji, do poziom odwoluje sie tylko w tym jednym miejscu nigdzie indziej (bo wszystko wykomentowalem)

i okazuje się że jak zakomentuję poziom[i] = create(10); to program się nie wywala (oczywiście daje złą odpowiedź ale przechodzi przez wszystkie testy). Sprawdziłem też że malloc zawszy przydziela pamięć (nigdy nie zwraca NULLa, bo gdyby zwrócił to kończyłby się program i błędu by nie było). Nie wychodzi tez poza tablicę poziom, ale też się nie zapętla. Powód musi być jakiś inny. Jakieś pomysły?

 

Edit:

Jeszcze lepiej, zobaczcie na to: (niesamowite jest co tu się dzieje)

typedef struct node Node;
struct node{
    Node *g[3];
    long val;
    int vis;
};

Node *create(long val){
  return NULL;
}

#define N 100000
Node * poziom[N]; /// globalna tablica 


----------- w jakiejs funkcji:

poziom[i] = create(10); /// to wywala blad
jak zamienie na to to nie ma bledu:
create(10); 

I jeszcze raz powtórzę, że mam pewność że nie wychodzi za tablicę

1 odpowiedź

0 głosów
odpowiedź 1 grudnia 2020 przez tangarr Mędrzec (154,860 p.)
wybrane 2 grudnia 2020 przez jankustosz1
 
Najlepsza
Skompiluj program z włączonym sanitizerem pod Linuksem i wtedy potestuj.
https://en.wikipedia.org/wiki/AddressSanitizer
komentarz 1 grudnia 2020 przez jankustosz1 Nałogowiec (35,880 p.)

Właśnie odkryłem coś dla mnie niepojętego:

typedef struct node Node;
struct node{
    Node *g[3];
    long val;
    int vis;
};
 
Node *create(long val){
  return NULL;
}
 
#define N 100000
Node * poziom[N]; /// globalna tablica 
 
 
----------- w jakiejs funkcji:
 
poziom[i] = create(10); /// to wywala blad

natomiast jak zamienie na to, to NIE WYWALA BLEDU:
poziom[i] = NULL:

W moim rozumieniu create(10) i NULL to jest dokładnie to samo, bo create(10) zwraca NULLa, a okazuje się że jedno wywala błąd a drugie nie

komentarz 1 grudnia 2020 przez tangarr Mędrzec (154,860 p.)
Teraz mamy pewność, że gdzieś wyjechałeś poza zakres danych. Nadpisałeś fragment stosu i dlatego nie udało się wykonać skoku do funkcji.

Jeżeli zbudujesz program z obsługą sanitizera od razu ci pokaże w której linijce kodu nastąpił problem.
komentarz 1 grudnia 2020 przez jankustosz1 Nałogowiec (35,880 p.)

Kompiluję w ten sposób:

gcc main.c -o main -fsanitize=address -static-libasan -g

i odpalam ./main wrzucam dane, program daje poprawną odpowiedź, sanitizer nie krzyczy, a jak wrzucam na sprawdzarkę to jest błąd, widocznie moje testy jakoś go omijają.

A może coś się psuć jak się zaalokuje za dużo pamięci? Choć w sumie raczej to nie to, bo malloc na sprawdzarce nie wywaliło NULLa. Ehh c to tragedia.

komentarz 1 grudnia 2020 przez j23 Mędrzec (194,920 p.)
Niewykluczone, że jest ustawiony limit pamięci na testowane programy.

Pewny jesteś, że konieczna jest ta 100k-elementowa tablica?
komentarz 1 grudnia 2020 przez jankustosz1 Nałogowiec (35,880 p.)
W sensie sprawdziłem, że poza tę tablicę nie wychodzi, bo gdyby wyszło to by się program zakończył bezbłędnie ze złym wynikiem i poszedłby do kolejnego testu. Wina tej tablicy to raczej nie jest (choć w przypadku c niczego nie wykluczam), bo by się w poprzednich testach wywaliło, a pierwsze pięć testów przechodzi, gdzie ta tablica o tym samym rozmiarze także jest deklarowana.
komentarz 1 grudnia 2020 przez jankustosz1 Nałogowiec (35,880 p.)

@j23, Btw. nawet dla pięciu milionów elementów w tej tablicy wywala się dalej na tym samym teście. Więc to raczej nie wina zbyt małej ilości pamięci

komentarz 1 grudnia 2020 przez tangarr Mędrzec (154,860 p.)
Daj cały kod. Może ktoś zauważy w czym jest problem.
komentarz 1 grudnia 2020 przez j23 Mędrzec (194,920 p.)

bo by się w poprzednich testach wywaliło

Inne testy mogą mieć inne limity pamięci.

nawet dla pięciu milionów elementów w tej tablicy wywala się dalej na tym samym teście.

5M to więcej niż 100k, a jeśli założyć, że sprawdzarka ma ustawiony jakiś tam limit, to oczywistym jest, że tym bardziej będzie błąd.

komentarz 1 grudnia 2020 przez jankustosz1 Nałogowiec (35,880 p.)
https://onlinegdb.com/Hyg5-NNiw

treść jest dosyć długa więc nie wiem czy jest sens żebym ją przytaczał, w każdym razie dostaje się drzewo binarne i następnie pary indeksów i trzeba znaleźć dla każdej pary liczbę krawędzi pomiędzy nimi.
komentarz 1 grudnia 2020 przez jankustosz1 Nałogowiec (35,880 p.)

@j23, Znaczy, może złego argumentu użyłem. Gdy zamieniam ten kod co napisałem to wtedy na tamtym teście się nie wywala, a daje po prostu złą odpowiedź nawet przy 5mln elementów.

komentarz 1 grudnia 2020 przez tangarr Mędrzec (154,860 p.)
Masz jakieś dane testowe?
Najlepiej takie, dla których się wywala?
j23 może mieć rację. Kolejne wykonania testu mogą mieć ustawione inne rozmiary stosu. Bezpieczniej by było umieścić te duże tablice na stercie.
komentarz 1 grudnia 2020 przez jankustosz1 Nałogowiec (35,880 p.)
No właśnie problem w tym że sam nie mogę znaleźć testu dla którego się wywala. Nie wiem też czy mogę tak upubliczniać teść zadania które jest na uczelni, na priv wyślę
komentarz 1 grudnia 2020 przez tangarr Mędrzec (154,860 p.)

Nie podoba mi się konstrukcja

while( isSeparator(c = getchar()) ) 
    c = getchar();

W warunku pętli wczytujesz znak i go sprawdzasz, potem wczytujesz kolejny znak i go ignorujesz a potem zaczynasz od nowa.
Zamień to na

do {
    c = getchar();
} while ( isSeparator(c) );

 

komentarz 1 grudnia 2020 przez jankustosz1 Nałogowiec (35,880 p.)
o Jezu, rzeczywiście
komentarz 1 grudnia 2020 przez jankustosz1 Nałogowiec (35,880 p.)
ale jednak nie miał to wpływu, bo separator był zawsze jeden, dalej na tym samym teście wywrotka :/
komentarz 1 grudnia 2020 przez tangarr Mędrzec (154,860 p.)
Czy w tych testach otrzymujesz wyjście z programu (stdout/stderr) lub wynik wykonania programu?
komentarz 1 grudnia 2020 przez jankustosz1 Nałogowiec (35,880 p.)
Niestety nic. Gdyby się coś dało ujrzeć to bym ten przykład wejściowy dał na wyjście :)
komentarz 2 grudnia 2020 przez j23 Mędrzec (194,920 p.)

@tangarr,

do {
    c = getchar();
} while ( isSeparator(c) );

Tak bezpieczniej:

while ((c = getchar()) != EOF && isSeparator(c)) {}

 

komentarz 2 grudnia 2020 przez jankustosz1 Nałogowiec (35,880 p.)
while ( isSeparator(c=getchar()) ){}

Tak też jest dobrze.

Wszystkie te 3 wersje działają tak samo

komentarz 2 grudnia 2020 przez jankustosz1 Nałogowiec (35,880 p.)
Ostatecznie metodą prób i błędów znalazłem zakres ilości elementów jakie miał na wejściu ten test i go wyifowałem, żeby program zakończył się bez błędu. W efekcie wszystkie 10 testów przeszło oprócz tego jednego i jest 14/15 pkt. Czemu się to wywaliło nie wiem, ale mam teorię, że to wina zbyt dużej liczby wywołań rekurencji, bo jak zmieniłem build z rekurencji na iterację to przestał się tam wywalać i zaczął w rekurencji w dfs'ie. Mógłbym bawić się w pisanie stosu i zamianę tego na iterację 14/15 mi wystarcza.

Podobne pytania

+1 głos
2 odpowiedzi 238 wizyt
0 głosów
1 odpowiedź 103 wizyt
pytanie zadane 23 czerwca 2020 w C# przez petarda Nowicjusz (170 p.)
0 głosów
0 odpowiedzi 173 wizyt
pytanie zadane 22 czerwca 2020 w C i C++ przez Nabuchadonozor Gaduła (3,120 p.)

92,568 zapytań

141,424 odpowiedzi

319,634 komentarzy

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

...