logo elektroda
logo elektroda
X
logo elektroda
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

ATMEGA168P - Dlaczego kompilator traktuje `xpos` jako int zamiast char?

excray 31 Paź 2012 21:34 1095 7
  • #1 11473265
    excray
    Poziom 41  
    Zaintrygowany tematem kolegi Qmexx postanowiłem dorzucić beczkę dziegciu do tej łyżki miodu niemniej kolega mnie uprzedził i zamknął temat. Chciałbym zapytać o jeszcze dwie dziwne przypadłości z którymi się spotkałem w czasie mojej zabawy z avrgcc.
    1.
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    We wskazanym fragmencie kompilator przetwarza zmienną xpos jako int mimo że zadeklarowana jest jako char. Próby jawnego rzutowania na char nie dały najmniejszego rezultatu. W zasadzie poddałem się stwierdzając jakąś wyższą konieczność kompilatora do takiego zachowania po czym już całkiem przypadkowo aby sprawdzić różnicę w objętości kodu podmieniłem tą linię na xpos = (xpos%8); i voila! Kompilator potraktował zmienną i operacje na niej jako char. Czemu wcześniej nie chciał?
    2. Mam dwie mocno zbliżone do siebie funkcje. Obie korzystają mniej więcej z tych samych rejestrów i obie je niszczą oraz obie są wywoływane bezpośrednio z main i nie są częścią innej funkcji. I teraz niespodzianka - w przypadku pierwszej funkcji kompilator wrzuca wszystkie możliwe rejestry na stos a w przypadku drugiej nie odkłada ani jednego. Od czego to zależy?
  • Pomocny post
    #2 11473305
    mirekk36
    Poziom 42  
    ad.1 - poczytaj tutaj to ci się wszystko wyjaśni - i będziesz nie tylko wiedział o co chodzi ale jak kod pisać LINK

    ad.2 - pokaż chociaż fragmenty kodu, co to oznacza że obie funkcje korzystają z tych samych rejestrów ? o jakich rejestrach piszesz ? czyżby to była wstawka w asemblerze ?
  • #3 11473384
    excray
    Poziom 41  
    mirekk36 napisał:
    ad.1 - poczytaj tutaj to ci się wszystko wyjaśni - i będziesz nie tylko wiedział o co chodzi ale jak kod pisać LINK

    Ok, ale jak wspominałem stosowałem rzutowanie niejawne które kompilator zignorował, niemniej drugi kod który nota bene stanowi na taki sam wynik już traktuje jako char bez żadnej zachęty.

    mirekk36 napisał:
    ad.2 - pokaż chociaż fragmenty kodu, co to oznacza że obie funkcje korzystają z tych samych rejestrów ? o jakich rejestrach piszesz ? czyżby to była wstawka w asemblerze ?

    Kod jest cały w C, a piszę o rejestrach bo podglądałem listing asm tego co kompilator stworzył.
    Kod:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod
  • #4 11473603
    mirekk36
    Poziom 42  
    excray napisał:

    Ok, ale jak wspominałem stosowałem rzutowanie niejawne które kompilator zignorował, niemniej drugi kod który nota bene stanowi na taki sam wynik już traktuje jako char bez żadnej zachęty.


    To pokaż mi gdzie próbowałeś robić to rzutowanie niejawne bo ja nie widzę :( napisz gdzie wg ciebie robisz (w którym miejscu) w tym kodzie na górze to rzutowanie niejawne

    Dodano po 2 [minuty]:

    ad.2 ja nadal jednak kompletnie nie rozumiem o co chodzi :( tzn no teraz widać jakąś funkcję i fajnie - jest to jakaś tam normalna funkcja. I w czym kłopot ?

    to co opisałeś na górze może świadczyć o tym że albo coś się źle skompilowało, albo nie użyłeś tej funkcji w programie - no nie wiem - pokaż może sam kontekst jak wywołujesz te funkcje z maina i pokaż ten fragment z pliku *.lss który wydaje ci się podejrzany
  • Pomocny post
    #5 11474307
    tmf
    VIP Zasłużony dla elektroda
    excray napisał:
    Zaintrygowany tematem kolegi Qmexx postanowiłem dorzucić beczkę dziegciu do tej łyżki miodu niemniej kolega mnie uprzedził i zamknął temat. Chciałbym zapytać o jeszcze dwie dziwne przypadłości z którymi się spotkałem w czasie mojej zabawy z avrgcc.
    1.
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    We wskazanym fragmencie kompilator przetwarza zmienną xpos jako int mimo że zadeklarowana jest jako char. Próby jawnego rzutowania na char nie dały najmniejszego rezultatu. W zasadzie poddałem się stwierdzając jakąś wyższą konieczność kompilatora do takiego zachowania po czym już całkiem przypadkowo aby sprawdzić różnicę w objętości kodu podmieniłem tą linię na xpos = (xpos%8); i voila! Kompilator potraktował zmienną i operacje na niej jako char. Czemu wcześniej nie chciał?


    Odpowiedź na to pytanie nie jest banalna i wymaga pewnej znajomości wewnętrznej budowy kompilatora. W obu przypadkach domyślnie dochodzi do promocji typów, jednak to co powstaje finalnie zależy od zestawu "instrukcji" którymi dysponuje kompilator. Kompilator posługuje się pewnymi gotowymi blokami, nazywanymi INSNS, kod C jest tłumaczony na dwukierunkową listę składającą się z insns'ów, realizujących konkretne operacje. To jak będzie wyglądał kod wynikowy zależy więc od tego jakimi insns'ami dysponuje kompilator. Dla przykładu operacja dodawania może być realizowana dla zmiennych typu float lub int. Istnieją więc co najmniej dwa oddzielne insnsy, jeden dla dodawania floatów, jeden dla dodawania intów. Stąd też pośrednio (oprócz standardu języka) wynika konieczność promowania typów - zamiast robić insns dla int+int, int+char, wystarczy char promować do int i wykorzystać insns int+int. Z tego masz też efekt uboczny - dla pewnych operacji istnieją insns bardziej specjalizowane, np. char%char, ale nie ma char&char - brak tej ostatniej też pośrednio wynika ze standardu - operacje logiczne są promowane do int.

    excray napisał:
    2. Mam dwie mocno zbliżone do siebie funkcje. Obie korzystają mniej więcej z tych samych rejestrów i obie je niszczą oraz obie są wywoływane bezpośrednio z main i nie są częścią innej funkcji. I teraz niespodzianka - w przypadku pierwszej funkcji kompilator wrzuca wszystkie możliwe rejestry na stos a w przypadku drugiej nie odkłada ani jednego. Od czego to zależy?


    Tu znowu trzebaby mieć dokładne przykłady, wersję gcc i opcje kompilacji. No i trzeba się trochę zagłębić w budowę gcc. Ale najogólniej odpowiedź brzmi - kompilator ma generować poprawny kod będący odpowiednikiem kodu C. Nie ma gwarancji, że wygeneruje optymalny kod. Programista C nie powinien wchodzić w takie tajniki, bo to po prostu nie ma sensu. Na jednej wersji kompilatora będzie tak, a na innej inaczej. Jeśli z jakiegoś powodu ma być wygenerowany ściśle określony kod to należy użyć wstawki w assemblerze. Albo naprawdę głęboko wejść w strukturę wewnętrzną używanego kompilatora - dla gcc akurat to jest proste bo są i opisy i kod źródłowy, więc zabawa w detektywa jest ułatwiona :)
  • Pomocny post
    #6 11474679
    mirekk36
    Poziom 42  
    Zresztą zdaje się, że już ci kiedyś też podpowiadałem, że nie korzystając w AVR GCC z tych typów uint8_t czy uint16_t .... wkopiesz się sam w konieczność w wielu przypadkach jawnych rzutowań typów. Najgorzej jest z tym ciągłym używaniem przez ludzi w gcc typu

    unsigned char

    a wystarczyłoby

    uint8_t

    i mnóstwo rzeczy odpadłoby ci z głowy..... Ale jak wolisz to brnij w unsigned char.
  • Pomocny post
    #7 11474780
    tmf
    VIP Zasłużony dla elektroda
    A co zmienia unsigned char, czy uint8_t? W avrlibc uint8_t jest zdefiniowany tak:
    typedef unsigned char uint8_t;
    Więc używanie uint8_t lub unsigned char to to samo.
    Promocja robi się domyślnie niezależnie od użytego typu, aby temu zapobiec i tak trzeba jawnie stosować rzutowanie.
  • #8 11475590
    excray
    Poziom 41  
    Ok, zamykam temat. Jeszcze raz bardzo dziękuję za udzielone informacje.
REKLAMA