Lista SLL - warning w działającym kodzie

W tym miejscu zadajemy pytania na temat języka C, dzielimy się swoją wiedzą, udzielamy wsparcia, rozwiązujemy problemy programistyczne.
Awatar użytkownika
Antystatyczny
Geek
Geek
Posty: 1177
Rejestracja: czwartek 03 wrz 2015, 22:02

Lista SLL - warning w działającym kodzie

Postautor: Antystatyczny » sobota 05 kwie 2025, 16:12

Cześć, od kilku dni siedzę nad listą jednokierunkową i mam pewien problem dotyczący ostrzeżenia, które generowane jest przez środowisko Visual Studio. Najpierw przedstawię, o co mi chodzi:

Kod: Zaznacz cały

static void List_destroy(node_t** n)
{
   node_t *temp = NULL;

#ifdef DEBUG
   int cnt = 0;
#endif /* DEBUG */

   if ((*n) != NULL)
   {
      do
      {
         temp = (*n)->next;// <--- C6001 Używanie niezainicjowanej pamięci „*temp”.

         free((*n));

#ifdef DEBUG
         printf("memory freed %d times\n", ++cnt);
#endif /* DEBUG */

         *n = temp;
      }while (*n != NULL);
   }
}


Kod się kompiluje bez błędów i ostrzeżeń, a program działa poprawnie, czyli dynamicznie alokuje pamięć dla kolejnych węzłów, wyświetla ich listę, na koniec niszczy wszystkie węzły i uwalnia pamięć. W Visual Studio mam możliwość skorzystania z usługi Copilot, więc z niej skorzystałem i otrzymałem, co następuje:

Aby naprawić błąd C6001, musisz upewnić się, że zmienna temp jest zainicjowana przed jej użyciem. W tym przypadku, problem polega na tym, że temp jest używana przed przypisaniem do niej wartości. Możesz to naprawić, inicjując temp przed zwolnieniem pamięci. Oto poprawiona wersja funkcji List_destroy:

Kod: Zaznacz cały

static void List_destroy(node_t** n)
{
   node_t *temp = NULL;

#ifdef DEBUG
   int cnt = 0;
#endif /* DEBUG */

   while (*n != NULL)
   {
      temp = (*n)->next; // Zainicjuj temp przed zwolnieniem pamięci <--- tutaj nadal mam to samo ostrzeżenie

      free(*n);

#ifdef DEBUG
      printf("memory freed %d times\n", ++cnt);
#endif /* DEBUG */

      *n = temp;
   }
}







Jak widać wywalony jest if sprawdzający, czy wskaźnik n nie jest pusty, ale to sprawdzenie i tak następuje podczas wejścia do pętli while. Tak czy siak nadal wyświetlane jest ostrzeżenie o tej samej treści i dotyczące tej samej zmiennej. Kod proponowany przez Copilot również działa poprawnie i kompiluje się bez ostrzeżeń. Ktoś ma jakieś pomysły, co z tym zrobić? Visual studio jest nadgorliwe?
"The true sign of intelligence is not knowledge but imagination" Albert Einstein.

Awatar użytkownika
xor
User
User
Posty: 175
Rejestracja: poniedziałek 05 wrz 2016, 21:44

Re: Lista SLL - warning w działającym kodzie

Postautor: xor » niedziela 06 kwie 2025, 11:53

Czesć! n może być niezainicjowane a nie jest sprawdzane.

Awatar użytkownika
Antystatyczny
Geek
Geek
Posty: 1177
Rejestracja: czwartek 03 wrz 2015, 22:02

Re: Lista SLL - warning w działającym kodzie

Postautor: Antystatyczny » niedziela 06 kwie 2025, 17:10

W funkcji mam test

Kod: Zaznacz cały

if ((*n) != NULL)

przed wejściem w pętlę, a sama pętla wykonuje się dopóki

Kod: Zaznacz cały

while (*n != NULL);
"The true sign of intelligence is not knowledge but imagination" Albert Einstein.

Awatar użytkownika
j23
Expert
Expert
Posty: 536
Rejestracja: czwartek 08 paź 2015, 18:40

Re: Lista SLL - warning w działającym kodzie

Postautor: j23 » poniedziałek 07 kwie 2025, 06:16

W Twoim oryginalnym kodzie (ten zaraz podany na wstępie Twojego postu Anty) jest warunek if, który sprawdza zmienną "n", a pod koniec pętli do-while jest TEN SAM WARUNEK, który też sprawdza zmienną "n". Jasne, że kod działa poprawnie i będzie działał. Nie wiem czy Visual Studio jest nadgorliwe, ale na mój rozumek to VS może nie pasować to, że używasz dwóch "spadochronów" przeciw zmiennej "n" PODCZAS GDY wg VS można to by zrobić za pomocą jednego i DLATEGO Visual Studio proponuje pętlę while (a nie do-while), bo w pętli while warunek jest sprawdzany ZANIM program wejdzie w pętle while (a konkretnie to zanim instruction pointer z wykonywanego programu wejdzie w pętle while).

Twój program Anty
(pseudokod - 6 linijek):
[START]
[CZY ZMIENNA N JEST PUSTA?]
{
[WEJDŹ DO PĘTLI WHILE...]
<JEDNORAZOWA_ITERACJA_PĘTLI_WHILE>
[...I WYKONUJ PĘTLĘ WHILE DOPÓKI N NIE STANIE SIĘ PUSTA]
}

[END]

Modyfikacja zaproponowana przez Copilota VS (żeby kod był wydajniejszy? a może VS jest rzeczywiście nadgorliwy? ;) )
(pseudokod - 4 linijki):
[START]
[ZACZNIJ WYKONYWAĆ PĘTLĘ WHILE POD WARUNKIEM ŻE N NIE JEST PUSTA]
{
<JEDNORAZOWA_ITERACJA_PĘTLI_WHILE>
}

[END]

Nie jestem pewny do końca czy moje rozumowanie jest tutaj poprawne, ale warning (ostrzeżenie) od VS ma chyba na celu sprawić, żeby kod był po prostu wydajniejszy.

Na stackoverflow trochę o tym piszą i podają rozwiązanie co zrobić, żeby rozkazać VS aby zamknął japę z niepotrzebnymi ostrzeżeniami, a konkretnie jest tam fragment (przetłumaczone przy pomocy https://translate.google.com):
To jest bardzo fałszywy alarm i nadal istnieje nawet w MSVC 2019. Nie ma możliwości, aby zmienna t.p mogła zostać niezainicjowana.

W rzeczywistości nie ma możliwości, aby mogła dotrzeć do instrukcji free() bez zainicjowania jej wartością różną od NULL. Ale nawet jeśli dopuścisz możliwość, że kompilator nie wie, że funkcja exit() nie zwróci, to jest to w zasadzie nieistotne. Niezależnie od tego, czy zwróci, czy nie, struktura nadal będzie zainicjowana czymś i w każdym przypadku free(NULL) jest całkowicie legalne.

Usunięcie if .. exit nie ma wpływu na ostrzeżenie, więc wątpię, aby to był problem. Bardziej prawdopodobne jest, że MSVC po prostu agresywnie zgłasza ostrzeżenia i najlepszym sposobem, aby przestać Cię to niepokoić, jest po prostu to zignorować.

Nie mam na myśli, że ignorujesz ostrzeżenie (ja nigdy nie mógłbym tego zrobić ze względu na moją naturę), mam na myśli powiedzenie MSVC, żeby się zamknął:

while (1) {
MyStruct t = Test();

// MSVC wrongly reports this as using uninitialised variable.
#pragma warning(push)
#pragma warning(disable: 6001)
free(t.p);
#pragma warning(pop)
}

Innymi słowy gościu ze stackoverflow podaje rozwiązanie typu brute-force aby poprzez dyrektywę preprocesora "#pragma" kazać VS aby nie generował denerwującego ostrzeżenia o błędzie 6001.
Internet łączy ludzi, którzy dzielą się swoimi zainteresowaniami, pomysłami i potrzebami, bez względu na geograficzne (przeciwności).
BOB TAYLOR, PARC

Awatar użytkownika
Antystatyczny
Geek
Geek
Posty: 1177
Rejestracja: czwartek 03 wrz 2015, 22:02

Re: Lista SLL - warning w działającym kodzie

Postautor: Antystatyczny » poniedziałek 07 kwie 2025, 17:03

j23 pisze:Modyfikacja zaproponowana przez Copilota VS (żeby kod był wydajniejszy? a może VS jest rzeczywiście nadgorliwy?


Możliwe, choć z opisu wynikało, że copilot poprawia mój błąd braku inicjalizacji zmiennej. Zerknąłem sobie na Stackoverflow i przeczytałem cały wątek. W zasadzie u mnie jest taka sama sytuacja i chyba skorzystam z chwilowego wyłączenia ostrzeżenia 6001:

Kod: Zaznacz cały

#pragma warning(push)
#pragma warning(disable: 6001)
free(*n);
#pragma warning(pop)


W każdym razie serdeczne dzięki. Szukałem, ale na ten wątek nie trafiłem.
"The true sign of intelligence is not knowledge but imagination" Albert Einstein.

Awatar użytkownika
xor
User
User
Posty: 175
Rejestracja: poniedziałek 05 wrz 2016, 21:44

Re: Lista SLL - warning w działającym kodzie

Postautor: xor » piątek 11 kwie 2025, 17:56

Antystatyczny pisze:W funkcji mam test

Kod: Zaznacz cały

if ((*n) != NULL)



Ja mówię o teście

Kod: Zaznacz cały

n != NULL

Od któregoś nowszego standardu C jest jakaś składnia wskazująca kompilatorowi, że parametr przekazany do funkcji jest zawsze valid, Visual, zdaje się ma też jakieś swoje dyrektywy. W tej postaci fukcji takiej pewności nie ma, n może być NULL i podejrzewam, że tego się czepia. Ale VS ni w ząb nie umiem więc pewnie się mylę. :-)

Awatar użytkownika
Antystatyczny
Geek
Geek
Posty: 1177
Rejestracja: czwartek 03 wrz 2015, 22:02

Re: Lista SLL - warning w działającym kodzie

Postautor: Antystatyczny » piątek 11 kwie 2025, 18:09

xor pisze:Ale VS ni w ząb nie umiem więc pewnie się mylę.


No właśnie ja też nie jestem biegły w tym środowisku, a w dodatku programuję wyłącznie hobbystycznie. W każdym razie, jeśli natrafię na jakiś lepszy opis lub rozwiązanie problemu, ewentualnie napiszę lepszy kod (bo to moje pierwsze podejście do SLL), wrzucę informację do wątku. Póki co zawiesiłem warning 6001 wokół jednej linijki kodu i mam spokój :)
"The true sign of intelligence is not knowledge but imagination" Albert Einstein.

Awatar użytkownika
Antystatyczny
Geek
Geek
Posty: 1177
Rejestracja: czwartek 03 wrz 2015, 22:02

Re: Lista SLL - warning w działającym kodzie

Postautor: Antystatyczny » wtorek 04 lis 2025, 21:06

Po jakimś okresie bezczynności w kwestii list jednokierunkowych wróciłem dziś do tematu, bo muszę poukładać widgety w listach, żeby zautomatyzować propagację zdarzeń. No i tym razem udało mi się bez problemu napisać lepszy kod, czyli:

Kod: Zaznacz cały

void List_destroy(node_t** n)
{
   while (*n != NULL)
   {
      node_t* tmp = *n;
      *n = tmp->next;
      free(tmp);

   }
}


Brak ifa sprawdzającego obecność wskaźnika, bo pętla while na wejściu zrobi to automatycznie. W związku z tym tmp zostanie zainicjalizowana poprawnym adresem, więc środowisko VS również przestało się czepiać. Takie proste, a tyle czasu mi zajęło. Temat rozwiązany, pozdro!
"The true sign of intelligence is not knowledge but imagination" Albert Einstein.


Wróć do „Pisanie programów w C”

Kto jest online

Użytkownicy przeglądający to forum: Obecnie na forum nie ma żadnego zarejestrowanego użytkownika i 1 gość