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
Moderatorzy:Jacek Bogusz, procesorowiec, r-mik, Moderatorzy
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
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
Re: Typy stałych w C
Tak naprawdę , to promocja nie dotyczy konkretnego języka programowania , ona dotyczy nawet życia codziennegoChciałbym zrozumieć co oznacza promocja typu w C dla AVR-GCC. Ja ze swojej strony nic ciekawego nie znalazłem.
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)
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.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.
Co robi kompilator:
Kod: Zaznacz cały
-1 11111111
-1<<1 111111110
promocja 0000000111111110
wyrównanie 1111111111111110
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;
}
Piotrek
-
- -
- Posty:175
- Rejestracja:10 paź 2003, o 20:44
- Lokalizacja:Białystok
- Kontaktowanie:
Re: Typy stałych w C
Promocja typu ( int promotion ) oznacza tylko tyle, że jeśli nie jest jawnieChciałbym zrozumieć co oznacza promocja typu w C dla AVR-GCC.
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.
Dziękuję za odpowiedź.
Mam jeszcze takie wątpliwości.
1. Dlaczego zmienna zadeklarowana jako
i powykonaniu operacji
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?
Mam jeszcze takie wątpliwości.
1. Dlaczego zmienna zadeklarowana jako
Kod: Zaznacz cały
unsigned long Wynik;
Kod: Zaznacz cały
Wynik=1<<15;
2. Dlaczego i skąd sie wzięło, że stała 1 jest typu intm a nie char?
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
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
Do Ad.1. Moje przekonanie wzięło się stąd że po ykonaniu kodu:
otzymałem 0xff ff 80 00 zamiast 0x00 00 80 00
Natomiast jeżel napisze
to wynik jest zgodny z oczekiwaniem. Sam sprawdziłem w AvrStudio
Kod: Zaznacz cały
unsigned long Wynik;
Wynik=1<<15;
Natomiast jeżel napisze
Kod: Zaznacz cały
Wynik=(unsigned long)1<<15;
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.Mam jeszcze takie wątpliwości.
1. Dlaczego zmienna zadeklarowana jakoi powykonaniu operacjiKod: Zaznacz cały
unsigned long Wynik;
staje się liczbą ze znakiem. Cy kompilator nie powienie "wiedzieć" że zmienna jest zadeklarowana jako unsigned.Kod: Zaznacz cały
Wynik=1<<15;
Piotrek
Kto jest online
Użytkownicy przeglądający to forum: Obecnie na forum nie ma żadnego zarejestrowanego użytkownika i 16 gości