Elektroda.pl
Elektroda.pl
X
Proszę, dodaj wyjątek www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

Funkcja bedąca argumentem innej funkcji - zasięg zmiennych

Jakub17 07 Cze 2017 20:42 1341 17
  • #1 07 Cze 2017 20:42
    Jakub17
    Poziom 6  

    Witam.

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Funkcja LCD_WriteText() wypisuje przekazany string "1" na ekranie wyświetlacza. itoa() skonwertowaną liczbę 1 na łańcuch zapisuje w tablicy będącej literałem złożonym, a następnie zwraca adres tej tablicy z łańcuchem. Tylko że po zakończeniu wywołania itoa() ta tablica znika, tak? No bo jest ograniczona zasięgiem do funkcji itoa(). Więc itoa() przekazuje adres do zawartości tablicy która zaraz zniknie - tak to rozumiem. Więc jakim cudem funkcja LCD_WriteText() wyświetla treść która nie istnieje?
    Gdzie popełniam błąd w swoim rozumowaniu?

    0 17
  • #3 07 Cze 2017 20:59
    michalko12
    Specjalista - Mikrokontrolery

    Kompilator w funkcji która wywołuje funkcję LCD_WriteText rezerwuje na stosie miejsce dla tej tablicy i przekazuje wskaźnik do tej tablicy funkcji itoa. Dopiero po wyjściu z tej funkcji obszar pamięci przeznaczony dla tej tablicy zostaje zwolniony na stosie.

    0
  • #4 07 Cze 2017 21:02
    tmf
    Moderator Mikrokontrolery Projektowanie

    @Jakub17 Rozumujesz poprawnie i taki kod jest błędny. Działa to dlatego, że literał tworzony jest na stosie i dopóki coś nie zamarze użytej wartości, ciągle będą na nim znajdowały się wyniki konwersji dokonanej przez itoa. Stąd też zwracany adres jest niepoprawny (w sensie, nie jest już przydzielony literałowi), ale jako, że jeszcze nic nie zdążyło w tym miejscu na stosie zapisać innych wartości, to funkcja wyświetlająca tekst odczyta go poprawnie.

    0
  • #5 07 Cze 2017 21:16
    grko
    Poziom 33  

    @tmf Mnie się jednak wydaje, że ten adres jednak będzie poprawny.

    0
  • #6 07 Cze 2017 21:30
    tmf
    Moderator Mikrokontrolery Projektowanie

    @grko Może przypadkowo być - jeśli kompilator przetłumaczy ten ciąg instrukcji jak napisał @michalko12.
    Ale nic nie stoi na przeszkodzie (chyba), aby zrobił to tak:

    Kod: c
    Zaloguj się, aby zobaczyć kod

    Co prowadzi w oczywisty sposób do błędnego kodu. IMHO to jest co najmniej bardzo ryzykowne użycie literału złożonego.

    0
  • #7 07 Cze 2017 21:38
    2675900
    Użytkownik usunął konto  
  • #8 07 Cze 2017 21:38
    grko
    Poziom 33  

    @tmf Wydaje mi się jednak, że nie masz racji i oba zapisy są w zasadzie równoważne:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Kod: bash
    Zaloguj się, aby zobaczyć kod


    Każdy literał został odłożony na stosie (po 256 bajtów) więc kod autora jest poprawny. Nie wiem dlaczego przy wejsciu do LCD write literał miałby przestać istnieć...

    0
  • #9 07 Cze 2017 22:04
    Jakub17
    Poziom 6  

    michalko12 napisał:
    Kompilator w funkcji która wywołuje funkcję LCD_WriteText rezerwuje na stosie miejsce dla tej tablicy i przekazuje wskaźnik do tej tablicy funkcji itoa. Dopiero po wyjściu z tej funkcji obszar pamięci przeznaczony dla tej tablicy zostaje zwolniony na stosie.


    Funkcąj wywołujacą LCD_WriteText() jest w main(). Ale kompilator wie tylko że funkcja ta przyjmuje za argument wskaźnik wiec tylko tyle pamięci zarezerwuje na ten wskaźnik, czy się mylę? To gdzie powstanie ta cała tablica jeżeli nie jest ona nawet argumentem tej funkcji, tylko dopiero funkcji itoa(). Kompilator sobie zgadnie że akurat do itoa() dam literał złożony?

    0
  • #10 07 Cze 2017 22:26
    michalko12
    Specjalista - Mikrokontrolery

    Kompilator najpierw na stosie rezerwuje miejsce na tablicę, potem funkcji itoa przez rejestry procesora ( lub przez stos) przekazuje wskaźnik( adres ) do tej tablicy. Funkcja itoa zwraca wskaźnik też przez rejestry i ten sam wskaźnik dopiero przekazywany jest do funkcji LCD_WriteText(). Z tego względu, że miejsce dla tej tablicy jest rezerwowane na stosie w funkcji main, to istnieje duże prawdopodobieństwo, że ta tablica istnieje "zawsze".

    0
  • #11 07 Cze 2017 22:37
    Jakub17
    Poziom 6  

    Tylko dziwi mnie że kompilator wie o tej tablicy jeszcze zanim sprawdzi jakie argumenty zawiera itoa(). Ale skoro mówisz że tak jest, to nie będe się kłocił. Do tej pory myślałem, że literał powstanie w momencie napotkania go przez kompilator w parametrach funkcji, tymczasem dzieje się to wcześniej. No ale jak literał znajduje sie gdzieś w main "luzem", nie jako argument, to już przy wywołaniu main() wiadomo o istnieniu takie tablicy mimo że kompilator nie doszedł jeszcze w kodzie do nazwy literału czyli jego adresu, tak? Czyli tak naprawdę tylko malloc pozwala stworzyć tablice w momencie napotkania w programie nazwy funkcji malloc(). Inne tablice powstają wcześniej, tak?

    0
  • #12 07 Cze 2017 22:59
    tmf
    Moderator Mikrokontrolery Projektowanie

    @Jakub17 Nie. Kompilator napotyka na funkcję LCD_WriteText i widzi, że jednym z argumentów jest funkcja, więc przystępuje do jej kompilacji, napotykając na literał złożony, więc rezerwuje na niego miejsce. Czyli ewaluacja takiego wywołania odbywa się od najbardziej zagnieżdżonej struktury.
    Wracając do problemu zasięgu - chyba istotnie zasięg jest rozszerzany do całego bloku, w tym przypadku do {} stąd ten literał będzie żył przy wywołaniu LCD_WriteText.

    0
  • #13 07 Cze 2017 23:00
    michalko12
    Specjalista - Mikrokontrolery

    @Jakub17 mylisz kompilator z interpreterem.

    0
  • #14 07 Cze 2017 23:16
    2675900
    Użytkownik usunął konto  
  • #15 08 Cze 2017 06:28
    Jakub17
    Poziom 6  

    Piotrus_999 napisał:
    Tali literał jest "statyczny i znany kompilatorowi w momencie kompilowania - wtedy decyduje jak go zainicjalizować.


    michalko12 napisał:
    Kompilator najpierw na stosie rezerwuje miejsce na tablicę


    To ten literał jest w pamięci statycznej czy na stosie? Pojawiły się dwie sprzeczne odpowiedzi.Automatyczny jest na pewno wskaźnik w nagłówku tej funkcji, który przechowuje adres tej tablicy. Z drugiej strony przykładowy literał łańcuchowy "slowo" jest przechowywany w pamięci statycznej, a do argumentu itoa() przekazany zostałby tylko adres tego literału, więc należałoby być może spodziewać się tego samego po literale złożonym. Chociaż z drugiej strony tablica w pamięci statycznej to chyba nieekonomiczne. Ale później Piotrus_999 mówi:
    Piotrus_999 napisał:
    W tym przypadku zaalokuje na stosie w scopie funkcji main.


    tmf napisał:
    @Jakub17
    Wracając do problemu zasięgu - chyba istotnie zasięg jest rozszerzany do całego bloku, w tym przypadku do {} stąd ten literał będzie żył przy wywołaniu LCD_WriteText.

    W literaturze przeczytałem, że literały złożone mają zasięg bloku.

    Czy ktoś mógłby mi podsumować tą całą dyskusję kilkoma zdaniami bo już się pogubiłem...

    0
  • #16 08 Cze 2017 07:58
    Freddie Chopin
    Specjalista - Mikrokontrolery

    Jakub17 napisał:
    Czy ktoś mógłby mi podsumować tą całą dyskusję kilkoma zdaniami bo już się pogubiłem...

    Proszę bardzo. Jeśli do spustu nabitego i zabezpieczonego pistoletu przywiążesz sznurek, a następnie wycelujesz go w swoją głowę i zaczniesz pociągać za sznurek, to pistolet nie wystrzeli (bo jest zabezpieczony). Niemniej jednak ja bym osobiście nie próbował.

    Kod który napisałeś jest tak ryzykowny i dziwny, że generalnie ciężko znaleźć jakikolwiek argument aby go obronić, gdyż dokładnie i absolutnie TO SAMO, z OCZYWISTYM i MAŁYM zasięgiem osiągniesz tak:

    Kod: C
    Zaloguj się, aby zobaczyć kod


    Efekt identyczny, kod oczywisty, myślę że absolutnie nikt nie ma wątpliwości co do zasięgu zmiennych. Owszem - całość jest dłuższa o 3 linijki, ale chyba zgodzimy się, że celem pisania programów w języku wysokiego poziomu jest to, aby były one bardziej, a nie mniej, czytelne. Kod taki nie przeszedłby nigdy przez żadne review w żadnym projekcie, gdyż konstrukt taki nie ma absolutnie żadnego uzasadnienia.

    0
  • #18 08 Cze 2017 08:48
    2675900
    Użytkownik usunął konto  
  Szukaj w 5mln produktów