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

[atmega16] dziwne zachowanie się programu

raf_entek 04 Kwi 2011 00:47 1459 7
  • #1 9357797
    raf_entek
    Poziom 18  
    Witam!

    Ostatnio próbuję swoich sił z tym mikrokontrolerem i napotkałem się na dziwne zachowanie, otóż w przypadku takiego kodu


    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    zmienna counter_temp zachowuje się poprawnie i osiąga wartość jaka jest zapisana w tablicy do której wskazuje Table_temp, natomiast w przypadku takiej składni

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    dochodzi do jakiejś anomalii, gdy wartość tablicy o adresie 4 wynosi 0-3, to konwersja przebiega poprawnie, natomiast gdy wartość tej tablicy będzie wynosić 4 i więcej, to mnożenie przez 10000 wywołuje nieoczekiwane zachowania, np gdy wartość ta ustawiona zostanie na 4 a reszta komórek będzie przechowywała wartość 0, to zmienna counter_temp otrzymuje wartość 294941760 (wyświetlam na wyświetlaczu 9 cyfr). Nie wiem jak to wyjaśnić, czy to jest wada mikrokontrolera czy kompilatora (AVR studio i WinAVR dają taki sam efekt). Czemu ta anomalia występuje tylko przy mnożeniu przez 10000 i nie występuje, gdy wcześniej zawartość adresu zostanie przepisana do zmiennej i pomnożona przez tą wartość?
  • Pomocny post
    #2 9358008
    jarekz_2
    Poziom 16  
    Dla mnie wszystko jest jasne. Zakładam że table_temp jest tablicą wielkości typu int (16 bitów, ze znakiem). Mnożenie (*(Table_temp + 4) - '0') * 10000 (listing 2) kompilator domyślnie wykonuje jako int*int z wynikiem również int, no i dochodzi do przekroczenia zakresu -32768..+32767, które nie jest sygnalizowane. Natomiast operacja counter_temp += temp * 10000; (listing 1) wymusza użycie podprogramu mnożenia 32-bitowego, bo temp jest 32-bitowy - i wynik jest poprawny.
  • #3 9358393
    raf_entek
    Poziom 18  
    Table_temp jest wskaźnikiem typu char do tablicy typu char.
    Ten kod ma zadanie zmienić ciąg znaków z tej tablicy na liczbę, czyli tablicę o zawartości { 1,2,3,4,5,6,7,8,9 } na unsigned long counter_temp o wartości 123456789.

    Czyli rozumiem, że dopóki dwa mnożone typy będą mniejsze niż wartość 16bitowa (nawet unsigned) to będzie wykonywana operacja jak na 16bitowej liczbie bez znaku jeśli nie zostanie wymuszone inaczej?

    Jak można to zapisać inaczej? Do głowy mi przychodzi mnożenie tej wartości przez 100000 a potem dzielenie przez 10 lub stworzenie dwóch tablic, jedna unsigned long a druga unsigned int, gdzie w unsigned long znajdą się pozycje 32bitowe + 10000 a w unsigned int reszta.

    Po zastosowaniu takiego zapisu działa jak należy:

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Dzięki za informację, do głowy mi by nie przyszło, że wynikiem w tym miejscu będzie liczba 16 bitowa z znakiem, za małe doświadczenie :)

    Pozdrawiam!
  • #4 9358567
    jarekz_2
    Poziom 16  
    (...)Table_temp jest wskaźnikiem typu char do tablicy typu char.
    Ten kod ma zadanie zmienić ciąg znaków z tej tablicy na liczbę, czyli tablicę o zawartości { 1,2,3,4,5,6,7,8,9 } na unsigned long counter_temp o wartości 123456789.(...)


    Tak, domyślam się. W przypadku Listingu 2 kompilator co prawda wie, że pierwszy argument jest typu char, ale drugi traktuje domyślnie jak int (ze znakiem!). Dlatego program wykłada się już na mnożeniu 4*10000 (które wykonane jako unsigned int * unsigned int dałoby jeszcze poprawny wynik).

    (...)Czyli rozumiem, że dopóki dwa mnożone typy będą mniejsze niż wartość 16bitowa (nawet unsigned) to będzie wykonywana operacja jak na 16bitowej liczbie bez znaku jeśli nie zostanie wymuszone inaczej?(...)

    Tak jest, o ile stała będzie wyraźnie typu unsigned, np. 10000u.

    (...)Po zastosowaniu takiego zapisu działa jak należy(...)

    Ja bym użył tylko jednej 32-bitowej tablicy multiTable32[9]. Zaletą będzie prostszy zapis (wadą - dłuższy czas wykonania).
    Tablicę można zadeklarować jako const, wtedy nie zajmie miejsca w RAM-ie.
  • #6 9358760
    raf_entek
    Poziom 18  
    Teraz rozumiem, więc kompilator domyślnie traktuje wartości nie przypisane do zmiennych jako najbliższy typ czyli w tym przypadku gdy wartość nie przekracza 32767 to uznaje go jako unsigned int?

    Zapomniałem zupełnie (bo praktycznie raz to widziałem do tej pory), że można na końcu wartości określić jej typ i po wpisaniu np 10000ul, mnożenie wykonuje się poprawnie, ale zostawię na razie te tablice, kod jest tylko o 8 bajtów dłuższy.

    Dodanie const nic nie zmienia, chyba kompilator domyślnie ustawia tą tablicę jako read-only.

    Jeszcze raz dzięki, sam bym do tego nie doszedł tak szybko :)


    nsvinc napisał:
    Rzutować operacje na unsigned long!
    
    counter_temp += ((unsigned long)(*(Table_temp + 1) - '0')) * multiTable32[1];
    


    Dzięki, to też sobie zapamiętam.
  • #7 9358801
    dondu
    Moderator na urlopie...
    raf_entek napisał:
    Teraz rozumiem, więc kompilator domyślnie traktuje wartości nie przypisane do zmiennych jako najbliższy typ czyli w tym przypadku gdy wartość nie przekracza 32767 to uznaje go jako unsigned int?

    Z tego co pamiętam to signed int.
  • #8 9358908
    raf_entek
    Poziom 18  
    Ano tak, pomyliłem się.
REKLAMA