Aktyw Forum

Zarejestruj się na forum.ep.com.pl i zgłoś swój akces do Aktywu Forum. Jeśli jesteś już zarejestrowany wystarczy, że się zalogujesz.

Sprawdź punkty Zarejestruj się

Typy stałych w C

Sławek5
-
-
Posty:485
Rejestracja:15 sie 2003, o 16:40
Lokalizacja:Szczecin
Kontaktowanie:
Typy stałych w C

Postautor: Sławek5 » 7 lis 2006, o 06:18

Cześć.
Chciałbym zrozumieć co oznacza promocja typu w C dla AVR-GCC. Ja ze swojej strony nic ciekawego nie znalazłem.
Skąd widaomo że stała np 1 mawartośc signed char a po operacji 1<<7 zmieni sie na signed int. Próbowałem znaleśc jakiś opis do tego ale nie jest to aż tak wyraźnie napisane jak by się wydawało.

Możecie mi to jakoś wytłumaczyć. A tak przy okazji jest gdzieś dostępny opis języka C dla AVR-CC, tzn opisy składni C wykorzystywanego w AVR-GCC

szymel
-
-
Posty:212
Rejestracja:16 sty 2005, o 16:42
Lokalizacja:Włocławek

Re: Typy stałych w C

Postautor: szymel » 11 lis 2006, o 00:24

Chciałbym zrozumieć co oznacza promocja typu w C dla AVR-GCC. Ja ze swojej strony nic ciekawego nie znalazłem.
Tak naprawdę , to promocja nie dotyczy konkretnego języka programowania , ona dotyczy nawet życia codziennego :)
Przykład:
Mamy dwie liczby typu 1-cyfrowego , zapisane w kodzie dziesiętnym np. 7 i 8 . Po ich pomnożeniu , czy też dodaniu , żeby zachować prawidłowy wynik (wartość bezwzględną oraz znak), musimy "promować" ten wynik do liczby 2-cyfrowej.Identycznie postępujemy przy działaniach na liczbach binarnych , na których "operują" układy cyfrowe (czytaj uC/uP) :D
Skąd widaomo że stała np 1 mawartośc signed char a po operacji 1<<7 zmieni sie na signed int. Próbowałem znaleśc jakiś opis do tego ale nie jest to aż tak wyraźnie napisane jak by się wydawało.
I właśnie w tym miejscu sprawa się nieco komplikuje , bo chcesz wykonać operację(działanie) logiczną , a nie arytmetyczną.Gdybyś zamiast (-1<<7) użył (-1*128) , nie było by problemu , a tak "zabiłeś klina" kompilatorowi ;) Jak wiadomo , liczby ujemne zapisywane są w kodzie U2 , a więc na najstarszym bicie danego typu liczby jest zapisany znak tej liczby.Kompilator , by zachować się "zgodnie z przepisami" , robi wszystko , by w czasie operacji nie utracić bit znaku tejże liczby. Ale co ma zrobić biedaczek , jeśli przesunięcie liczby ujemnej w lewo już tylko o 1 miejsce , wyrzuca mu znak liczby poza zakres bierzącego typu :?: A więc cwaniaczek , rozszerza sobie liczbę do kolejnego , wyższego pod względem ilości zajmowanych bitów typu i tyle.Ale to jeszcze nie wszystko , bo żeby teraz wartość nowego typu była prawidłowa , to musi bit znaku (który po przesunięciu w lewo o 1 pozycję, znalazł się na 8-pozycji licząc od najmłodszego bitu) umieścić na najwyższej pozycji tego nowego typu , czyli na poz. 15.Więc robi to , zapisując wrtością bitu znaku z poz. 8 ,wszystkie starsze bity aż do pozycji 15.
Co robi kompilator:

Kod: Zaznacz cały

-1 11111111 -1<<1 111111110 promocja 0000000111111110 wyrównanie 1111111111111110
Proponuję poobserwować zmienne uI i uL w poniższym przykładzie , oraz zwrócić uwagę na "cuda" , jakie wyczynia kompilator.

Kod: Zaznacz cały

#include <avr/io.h> int main(void) { unsigned int uI=0; unsigned long uL=0; signed char sC=-1; while(1) { uI=uL=sC; uL=uI=sC; uI=uL=(unsigned char)sC; uL=uI=(unsigned char)sC; } return 0; }
Chyba za bardzo nie zagmatwałem :oops:


Piotrek

Jurek Szczesiul
-
-
Posty:175
Rejestracja:10 paź 2003, o 20:44
Lokalizacja:Białystok
Kontaktowanie:

Re: Typy stałych w C

Postautor: Jurek Szczesiul » 11 lis 2006, o 17:27

Chciałbym zrozumieć co oznacza promocja typu w C dla AVR-GCC.
Promocja typu ( int promotion ) oznacza tylko tyle, że jeśli nie jest jawnie
zadeklarowany typ stalej czy zmiennej - jest ona przez kompilator domyślnie
traktowana jako int ( int16_t ). Dopóki poruszamy się z zakresie int można o tym
w ogóle nie pamiętać. Problem pojawia się gdy w jakimś miejscu wykraczamy
( wynik działań cząstkowych, przesunięcie itp. poza ten zakres ). Dobrze jeśli
kompilator nam to wyłapie i da ostrzeżenie bo inaczej dostajemy rezultaty
przekłamane i dziwaczne.

Zrób eksperyment. Zgodnie z powyższym stała np. 1 jest domyślnie rozmiaru int
czyli jest traktowana jako 2 bajty : 0x01

Jako "pojemnik" na wynik operowania na stałej zadeklaruj z zapasem zmienną
ulong Wynik; // 4 bajty i zobacz co się stanie przy różnych przesunięciach
( wygodnie ustawić wyświetlanie w hex ):
Wynik=1<<5; // 0x00 00 00 20
Wynik=1<<14; // 0x00 00 40 00
Bit posłusznie wędruje na żądaną pozycję.
A teraz :
Wynik=1<<15; // 0xff ff 80 00 zamiast oczekiwanego 0x00 00 80 00 !
Ustawienie najstarszego bitu dla int oznacza liczbę ujemną i taka jest
wpisana w Wynik zgodnie z regułami zapisu w U2.
I to jest największe potencjalne żródło błędów bo przechodzi bez żadnych
ostrzeżeń - sami musimy o tym pamiętać ( kompilator oblicza prawą stronę
zgodnie z występującymi w niej typami nie przejmując się typem zadeklarowanym
dla ostatecznego rezultatu ).
Przy próbie :
Wynik = 1 <<16;
dostajemy już ostrzeżenie : left shift count >= width of type
i widać ,że coś jest nie tak.

Natomiast jeśli jawnie ustalimy potrzebny typ stałej wszystko odbywa się już
bez niespodzianek np :
Wynik=1U << 15; // 0x00 00 80 00
Wynik=1UL <<16; // 0x00 01 00 00
Wynik = 1UL << 31; // 0x80 00 00 00

Pozdrowienia Jurek S.

Sławek5
-
-
Posty:485
Rejestracja:15 sie 2003, o 16:40
Lokalizacja:Szczecin
Kontaktowanie:

Postautor: Sławek5 » 22 lis 2006, o 11:26

Dziękuję za odpowiedź.
Mam jeszcze takie wątpliwości.
1. Dlaczego zmienna zadeklarowana jako

Kod: Zaznacz cały

unsigned long Wynik;
i powykonaniu operacji

Kod: Zaznacz cały

Wynik=1<<15;
staje się liczbą ze znakiem. Cy kompilator nie powienie "wiedzieć" że zmienna jest zadeklarowana jako unsigned.

2. Dlaczego i skąd sie wzięło, że stała 1 jest typu intm a nie char?

Awatar użytkownika
bis
-
-
Posty:134
Rejestracja:12 maja 2005, o 08:11
Lokalizacja:Warszawa

Postautor: bis » 22 lis 2006, o 13:45

ad1. A z czego wynika twoje przekonanie że stała się ona "ze znakiem"? taka liczba ma wartość 0x00008000, a to czy jest ona ze znakiem czy bez to tylko zależy od tego w jaki sposób ją sobie "wypiszesz". Np. jeżeli w printfie użyjesz formatu "%d" to wypisze dziesiętnie ze znakiem (signed) bo tak kazałeś ją potraktować, jeżeli użyjesz "%u" to wypisze ją jako unsigned ale cały czas jkao dwa bajty, jeżeli dodasz modyfikator "%lu" to dostaniesz bez znaku liczbę 32 bitową itd.. itp..
ad2. napis 1 (znak jedynki) nie posiada żadnego stałego/określonego typu. Za każdym razem gdy go używasz (podczas kompilacji dokładnie tego fragmentu kodu(wyrażenia języka C)) kompilator narzuca jakąś interpretację. Jeżeli nie dasz żadnych wskazówek (w postaci rzutowania typu) to kompilator używa zestawu domyślnego. Dla napisów które mozna potraktować jako liczby (to wynika z kontekstu) domyslne rzutowania przyjmują typ int. W bardziej zaawansowanych kompilatorach (z rozbudowaną optymalizacją) często jeszcze następują dalsze rzutowania wg spodziewanego typu wyniku branego z typu części wyrażenia po lewej stronie (np. gdy przypisujemy wynik do char to brak odpowiedniego nawiasowania powodował że rozbudowane wyrażenia po prawej stronie czasami były skracane do char już w trakcie wyliczania fragmentów pośrednich co powodowało że jeżeli wyniki pośrednie były wieksze to wartość całości wyrażenia była niezgodna z intuicją programisty (np. dawne Borlandowskie Turbo C tak potrafiło)

bis

Sławek5
-
-
Posty:485
Rejestracja:15 sie 2003, o 16:40
Lokalizacja:Szczecin
Kontaktowanie:

Postautor: Sławek5 » 22 lis 2006, o 18:58

Do Ad.1. Moje przekonanie wzięło się stąd że po ykonaniu kodu:

Kod: Zaznacz cały

unsigned long Wynik; Wynik=1<<15;
otzymałem 0xff ff 80 00 zamiast 0x00 00 80 00
Natomiast jeżel napisze

Kod: Zaznacz cały

Wynik=(unsigned long)1<<15;
to wynik jest zgodny z oczekiwaniem. Sam sprawdziłem w AvrStudio

szymel
-
-
Posty:212
Rejestracja:16 sty 2005, o 16:42
Lokalizacja:Włocławek

Postautor: szymel » 23 lis 2006, o 20:48

Mam jeszcze takie wątpliwości.
1. Dlaczego zmienna zadeklarowana jako

Kod: Zaznacz cały

unsigned long Wynik;
i powykonaniu operacji

Kod: Zaznacz cały

Wynik=1<<15;
staje się liczbą ze znakiem. Cy kompilator nie powienie "wiedzieć" że zmienna jest zadeklarowana jako unsigned.
A kto Ci powiedział , że po takiej operacji zmienna Wynik "staje się liczbą ze znakiem" :?: Czyżbyś uważał , że instrukcja Wynik=0xFFFF8000 powoduje , że zmienna Wynik zmienia swój typ , z unsigned long na signed long :?: Kompilator potraktował wyrażenie 1<<15 jako int , bo to jest jego domyślny typ dla wszystkich niezdefiniowanych stałych/wyrażeń , obliczył wynik wyrażenie zgodnie z jego typem argumentów , rozszerzył do wymaganej wielkości zgodnie z typem wyrażenia i skopiował wynik obliczeń do celu , czyli zmiennej Wynik , nie przejmując się jej typem.

Piotrek

Wróć do „AVR/AVR32”

Kto jest online

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