keil raz jeszcze...

Sep 13, 2003 14 Replies

witam ponownie... Znowu zatrzymalem sie na jakiejs drobnostce. Chcialem zrobic komunikacje szeregowa miedzy procesorami. W zmiennej typu char znak mam przechowany bajt danych, ktory chce wyslac szeregowo. Wszystko jest ok, tylko nie potrafie "rozebrac" bajtu na bity... Chce to zrobic iloczynem logicznym, mam ksiazke do ansi c i tam jest napisane ze wynikiem operacji bajt & bajt = true/false, tak tez napisalem kawalek kodu (do zobaczenia nizej), podczas uruchomienia debuger'a wyszlo na to ze jezeli w znak mam np. 64 (dziesietnie) i wykonam operacje znak & 64 to w wyniku otrzymam 64 (0x0040). I jak to interpretowac? dlazcego wynikiem nie jest 1? Jak poprawnie to napisac?



PS. Zmodyfikowalem program jeszcze tak: if (znak & 64 == 64){ ... ale rowniez nie dziala to prawidlowo. i w wyniku dzialania programu sa same zera nn1=0, nn2=0, nn3=0.



pozdrawiam, Bartek.



//przyklad programu fragment unsigned char znak; znak = 0x0040;



if( znak & 128 == 1 ){ data_out = 1; puts("nn1 = 1"); } else { data_out = 0; puts("nn1 = 0"); }



data_clk = 0; while(data_in == 1) {} data_clk = 1; //2



if( znak & 64 == 1){ data_out = 1; puts("nn2 = 1"); } else { data_out = 0; puts("nn2 = 0"); }



data_clk = 0; while(data_in == 1) {} data_clk = 1;



//3 if(znak & 32 == 1){ data_out = 1; puts("nn3 = 1"); } else { data_out = 0; puts("nn3 = 0"); }


przesuniêcie bitowe i sprawdzaj tylko jaka wartosc jest na pierwszej pozycji czyli znak & 1

Normalnie - napisanie "&" to jest zwykla operacja logicznego iloczynu i poprawnym wynikiem 64 & 64 jest oczywiscie 64. Jezeli chcesz cos sprawdzic przy pomocy instrukcji if (albo dowolnej innej sprawdzajacej warunek, np. na koncu petli while) - to wystarczy rozroznienie wartosci zerowej (FALSE) i niezerowej (TRUE). Krotki przyklad:

char znak = 64;

if (znak & 64) { // to jest true } else { // to jest false }

Jezeli chcesz sprawdzic, czy bit n jest zapalony, wygodniej napisac: if (znak & (1<<n)) a jezeli sprawdzasz, czy jest zgaszony: if (!(znak & (1<<n))) i juz.

Podobnie gdy w zmiennej znak chcesz zapalic bit n: znak |= (1<<n); lub go zgasic: znak &= ~(1<<n);

To co napisalem powyzej dotyczy kazdego kompilatora C na dowolna platforme, nie tylko Keila.

U¿ytkownik "Neo" snipped-for-privacy@terramail.pl napisa³ w wiadomo¶ci news:bjul8q$3vl$ snipped-for-privacy@topaz.icpnet.pl...

czyli cos takiego: (zanczynam od MSB)

unsigned char znak; unsigned char wynik;

znak = 0x040; wynik = znak >> 7; if (wynik & 1 == 1){ puts("nn1 = 1") } else { puts("nn1 = 0") }

wynik = znak >> 6; if (wynik & 1 == 1){ ...

</ciach>
</ciach>

juz wyjasnil p. Adam Dybkowski. dzieki.

W praktyce programujac w C znacznie lepiej jest pozamykac te operacje w makrach, bo w dluzszym kodzie latwo o pomylke, np. zapomni sie napisania tyldy. A w przypadku np. BSET(x,n), BCLR(x,n) i BTST(x,n) taka mozliwosc nie zachodzi.

Pozdrawiam Piotr Wyderski

Albo ksiazka klamie, albo Ty zaniedbales kontekst. :-) x & y to jest operacja na wektorze bitowym (w szczegolnosci na znaku) i jej wynikiem jest rowniez wektor bitowy, stad trudno przypuszczac, by mogl sie on rownac true albo false. Wartosc booleowska zwraca operator &&, ale to tez _nie jest_ to, o co Ci chodzi.

Jako brak bledu w ALU procesorka. ;-)

if (znak & 0x40) {

data_out = 1; puts("nn1 = 1");

} else {

data_out = 0; puts("nn1 = 0"); }

Dziala zupelnie prawidlowo, tylko nie w sposob, ktorego oczekujesz. ;-) Popatrz sobie na gramatyke C: multiplicative_operator jest umieszczony ponad equality_operator, wiec kompilator rozbierze ten napis jako "znak & (64==64)" zamiast "(znak & 64) == 64". 64==64 jest prawda, wiec C przeksztalci to na 1, co w efekcie da "znak & 1". Tak, w tym miejscu mozesz podziekowac panom K&R za wprowadzenie automatycznych konwersji typow, promocji i dziwnego priorytetu operatororow -- oto efekty. Uprzedzam, ze to nie ostatnia bzdura, ktora zaskoczy Cie C. BTW, tej czesci ") == 64" nie musisz dopisywac, w porownaniach C samo nieproszone przeksztalca wartosci niezerowe na true i zero na false, NULL na 0 i 0 na NULL itd. zamiast zwrocic blad "type mismatch", wiec z bolem serca mozna z tego skorzystac skracajac nieco kod zrodlowy.

Pozdrawiam Piotr Wyderski

Zazwyczaj do komunikacji uzywasz jakiegos interfejsu szeregowego, nie trzeba "rozbierac" bajtu programowo.

A powinno - znak nie jest przypadkiem 0 ?

tego radze unikac. Nie wiadomo czy kompilator obliczy to jako 128 czy -128. [a 1 to na pewno nie wyjdzie !]

Mozesz natomiast zapisac if (znak & 128)

Jesli wynik jest niezerowy - C uznaje to za "true".

to sie tez zazwyczaj w petli daje i przesuwanie wykorzystuje:

for (i=0;i<8;i++) { if (znak & 128) [....]

znak <<= 1 }

Oczywiście, zgadzam się w całej rozciągłości.

BTW: Ostatnio spotkałem się z ciekawym błędem (którego kompilator C, mając pełną rację, nie zauważył), powodującym niezłe zamieszanie. Kumpel napisał:

a=+5;

zamiast

a+=5;

Patrząc na kod, ciężko to zauważyć, a wynik jest do przewidzenia. Z drugiej strony to ciekawe, że można sobie napisać tak po prostu stałą +5 i już. Im mniejszy błąd tym dłużej się go tropi. :-)

news.onet.pl napisa³(a) w wiadomo¶ci: ...

if((znak & 128) == 128) ....

w ten sposob sprawdzisz, czy najstarszy bit jest "1". Metoda jest prosta i skuteczna i zupelnie nie wiem, czemu sie Kolegom nie podoba. Zlozenie w analogiczny sposob dwoch operacji - OR (sprawdzanie zer) i AND (sprawdzanie jedynek) umozliwia sprawdzenie dowolnej maski (np. 101xx011, x oznacza bity dowolne). Natomiast wykonanie operacji przesuniecia, np:

znak >>= 1;

powoduje zniszczenie zmiennej znak, natomiast uzycie operacji przesuniecia w warunku:

if (znak & (1<<n)) ...

jest zwyczajnie bardzo malo optymalne (np. w '51 spowoduje wygenerowanie n razy operacji RLC, potem operacji ANL i potem czegos do sprawdzenia, czy akumulator jest niezerowy)

entrop3r

Piotr Wyderski napisa³(a) w wiadomoœci: ...

W czym problem ? Niezerowy wektor bitowy jest traktowany jako logiczne 1, zerowy jako 0. Przejrzenie listingu ujawnia, ze operacja jest zazwyczaj przeprowadzana krotko i sprawnie np. przez skok warunkowy gdy akumulator jest (nie)zerowy. Jakos nie narzekam na te "nieproszona" konwersje typow, szczegolnie ze jest ona chyba najnaturalniejsza z naturalnych. Oczywiscie dotyczy to zapisu

if(x & y)

Spotkalem sie w jednym kompilatorze z bledem, polegajacym na rzutowaniu wektora bitowego na wartosc logiczna przez wziecie najmlodszego bitu (zrzutowanie bajtu na bit a potem na wartosc logiczna). W przypadku zapisu

if((x & y) == y)

ten problem oczywiscie nie istnieje. Nie spotkalem sie nigdy z sugerowanym bledem rzekomego usuwania "==y". Bylby to ewidentny blad kompilatora.

pozdrawiam entrop3r

Podobne ale równie wnerwiające :) : port=!port; // zmiana stanu portu port!=port; // skompiluje się ale...

W.

Mozna tez napisac ot tak sobie:

a;

i tez bledu nie bedzie:-)) pzdr JD

W tym, ze wektor bitowy to zupelnie inny typ niz jego skladowa i nie mozna wprost dokonac unifikacji obu typow. Tak samo jest np. w przypadku zbioru jednoelementowego. Jest jasne, ze _nie_ jest on rownowazny swojemu elementowi, {a} != a. Jest to po prostu niedopuszczalne na poziomie formalnym.

Ale to wymaga niejawnej konwersji typu {0,1}^n -> bool. Porzadne jezyki programowania nie zezwalaja na automatyczna zmiene typu argumentu i zglaszaja to uzytkownikowi jako blad systemu typow, wymuszajac jawne rzutowanie, a w C nawet nie dostaniesz informacji, ze cos jest nie tak. Zreszta takich kwiatkow jest wiecej.

Sa ludzie, ktorzy narzekaja, a ja sie do nich zaliczam, bo na pierwszym miejscu stawiam sobie elegancje i spojnosc formalna jezyka. Przyznaje, ze C jest wygodnym narzedziem, uzywam go i bede uzywal oraz uczyl tego jezyka studentow informatyki, ale nie zmienia to faktu, ze od strony formalnej to jest potworek.

A to jest niezgodnosc ze standardem, czyli blad kompilatora.

Pozdrawiam Piotr Wyderski

Piotr Wyderski napisa³(a) w wiadomoœci: ...

Jak dla kogo, w kazdym razie jeszcze mi sie bity z bajtami nie myla :)

Owszem, wymaga. Ale jesli ktos pisze "if" to powinien miec swiadomosc, ze wszystko co w nim jest zostanie zrzutowane na warunek logiczny. Jesli nie zrobi tego jawnie, wtedy bez jego kontroli zrobi sie to niejawnie, niekoniecznie w sposob w jaki by chcial. Nie sadze zeby to byla jakas wielka wada jezyka C. Poza tym rzutowanie czegokolwiek na wartosc logiczna jest umowne i w tym sensie uzywanie konwersji jawnej przed niczym nas nie chroni - w pewnym sensie porownujemy slodkie z niebieskim - ale jakos trzeba to zrobic. Dlatego umowa zwykle jest taka, ze 0x00 to false a reszta wartosci to true i ma to scisly zwiazek z traktowaniem bajtu(ow) jako warunku skoku przez CPU. Rownie dobrze mogloby byc odwrotnie, bo sa i instrukcje JNZ, mozna wymyslic tez jakies bzdurne formalizmy typu true = parity(x) (w koncu flaga parzystosci tez jest) ale jednak jest naturalnie i w miare blisko sprzetu, za co sobie C cenie.

uwazasz C za nieporzadny jezyk ? No coz, polecam assembler, tam sa sztywne konwencje, jasne konstrukcje i nie mozna sie niczego uczepic.

I wszystko jasne :)

pozdrawiam entrop3r

Join the Discussion

Have something to add? Share your thoughts — no account required.

Didn't find your answer?

Ask the community — no account required