Serwis Edukacyjny w I-LO w Tarnowie Materiały dla uczniów liceum |
Wyjście Spis treści Wstecz Dalej
Autor artykułu: mgr Jerzy
Wałaszek |
©2024 mgr Jerzy Wałaszek
|
W tym rozdziale zajmiemy się podstawowymi operacjami na bitach w języku C. Na szczęście są one bardzo dobrze obsługiwane.
Aby prezentować informację w postaci bitów, zdefiniujemy funkcję pbn(), która przyjmuje argument typu unsigned, a następnie wyświetla zadaną liczbę bitów tego argumentu. Wyjaśnienie użytych w niej operacji znajdziesz dalej w tym rozdziale. jej działanie opiera się na tym, iż wszelkie dane są wewnętrznie reprezentowane bitami. Nie musimy zatem dokonywać żadnych konwersji, po prostu odczytujemy stan kolejnych bitów, zamieniamy go w cyfry 0 lub 1 w kodzie ASCII i wyświetlamy w oknie konsoli.
/* Bity (C)2016 mgr Jerzy Wałaszek Data utworzenia: 16.10.2016 */ #include <stdio.h> #include <stdlib.h> #include <locale.h> // Wyświetla b końcowych bitów argumentu v //---------------------------------------- void pbn(unsigned v, unsigned b) { unsigned maska; for(maska = 1 << (b - 1); maska; maska >>= 1) if(v & maska) putchar('1'); else putchar('0'); } int main() { unsigned i; setlocale(LC_ALL,""); for(i = 0; i <= 16; i++) { pbn(i,5); putchar('\n'); } return 0; } |
Składają się z ciągu cyfr 0...9, które można poprzedzić znakiem + lub -. Stałe te reprezentują dziesiętne liczby całkowite, czyli takie, do których jesteśmy przyzwyczajeni.
/* Stałe (C)2016 mgr Jerzy Wałaszek Data utworzenia: 17.10.2016 */ #include <stdio.h> #include <stdlib.h> #include <locale.h> int main() { setlocale(LC_ALL,""); printf("%d %d\n\n",+5,-8); return 0; } |
Jeśli stała jest wielocyfrowa, to pierwszą cyfrą nie może być cyfra 0.
Czasem pewne wartości wygodniej jest zapisywać programiście w systemie ósemkowym. Do tego celu służą stałe ósemkowe, oktalne. Zapisujemy je przy pomocy cyfr 0...7. Pierwszą cyfrą musi być cyfra 0, inaczej stała będzie rozpoznana jako dziesiętna. Cyfry można poprzedzić znakami + lub -.
/* Stałe (C)2016 mgr Jerzy Wałaszek Data utworzenia: 17.10.2016 */ #include <stdio.h> #include <stdlib.h> #include <locale.h> int main() { unsigned a = 0377; setlocale(LC_ALL,""); printf("OCT = %o DEC = %d\n", a, a); return 0; } |
Dane zapisywane w komórkach 8-bitowych lepiej wyglądają w systemie szesnastkowym. Dlatego system ten jest bardzo popularny wśród programistów. Stałą szesnastkową, heksadecymalną, zapisujemy z prefiksem 0x lub 0X, po którym umieszczamy cyfry szesnastkowe, czyli znaki 0...9 i A...F lub a...f. Stałą można poprzedzić znakami + lub -.
/* Stałe (C)2016 mgr Jerzy Wałaszek Data utworzenia: 17.10.2016 */ #include <stdio.h> #include <stdlib.h> #include <locale.h> int main() { unsigned a = 0x1f; setlocale(LC_ALL,""); printf("HEX = %X DEC = %d\n", a, a); return 0; } |
Dane binarne możemy zapisywać za pomocą stałych binarnych. Składają się one z prefiksu 0b lub 0B, po którym umieszczamy cyfry 0..1 tworzące wartość binarną. Przed stałą można umieścić znaki + lub -.
/* Stałe (C)2016 mgr Jerzy Wałaszek Data utworzenia: 17.10.2016 */ #include <stdio.h> #include <stdlib.h> #include <locale.h> // Wyświetla b końcowych bitów argumentu v //---------------------------------------- void pbn(unsigned v, unsigned b) { unsigned maska; for(maska = 1 << (b - 1); maska; maska >>= 1) if(v & maska) putchar('1'); else putchar('0'); } int main() { unsigned a = 0b10111; setlocale(LC_ALL,""); printf("BIN = "); pbn(a, 5); printf(" DEC = %d\n", a); return 0; } |
Liczby dziesiętne ułamkowe zapisujemy za pomocą stałych zmiennoprzecinkowych. Stałą mogą poprzedzać znaki + lub -. Sama stała składa się z części całkowitej, kropki pełniącej rolę przecinka dziesiętnego, części ułamkowej. Dodatkowo może pojawić się litera e lub E z liczbą całkowitą, która określa wykładnik potęgowy liczby 10, przez co jest mnożona wcześniejsza liczba. Część całkowita lub ułamkowa może być pominięta, jeśli wynosi zero. Oto kilka przykładów stałych zmiennoprzecinkowych:
1.5 (równe 1,5) .5 (równe 0,5) 2. (równe 2,0) -3.14 (równe -3,14) 1e3 (równe 1 · 103 = 1000) -7.12e-5 (równe -7,12 · 10-5 = -0,0000712) |
/* Stałe (C)2016 mgr Jerzy Wałaszek Data utworzenia: 17.10.2016 */ int main() { float a = .5; setlocale(LC_ALL,""); printf("FP = %f\n", a); return 0; } |
Do przedstawiania pojedynczych znaków ASCII używamy stałych znakowych, które powstają przez umieszczenie znaku w apostrofach. Taka stała jest równoważna liczbowo kodowi ASCII danego znaku.
/* Stałe (C)2016 mgr Jerzy Wałaszek Data utworzenia: 17.10.2016 */ #include <stdio.h> #include <stdlib.h> #include <locale.h> int main() { char a = 'Z'; setlocale(LC_ALL,""); printf("Znak %c ma kod ASCII %d\n", a, a); return 0; } |
W stałej znakowej można umieszczać znaki specjalne, które zapisujemy przy pomocy znaku backslash: \. Ich znaczenie zależy od urządzenia służącego do wyświetlania tekstu. Najczęściej używane znaki, to:
\n | – | przejście na początek nowego wiersza |
\r | – | powrót na początek bieżącego wiersza |
\t | – | tabulacja pozioma |
\ooo | – | znak o kodzie ósemkowym, np. \245 |
\xhh | – | znak o kodzie szesnastkowym, np. \xf4 |
\\ | – | znak \ |
\' | – | znak apostrofu |
\" | – | znak cudzysłowu |
W programach napisanych w języku C często posługujemy się różnymi tekstami, np. wypisując wyniki obliczeń lub opisując dane do wprowadzenia. Do tego celu używamy stałych łańcuchowych, które nazywa się również stałymi tekstowymi. Stałą łańcuchową tworzy się przez umieszczenie tekstu w cudzysłowach, np. "Jacek i Agata". Wartością stałej łańcuchowej jest adres pierwszego znaku w tekście. Stałe łańcuchowe można przypisywać zmiennym wskaźnikowym lub używać do inicjalizacji tablicy znakowej. W tekście można używać znaki specjalne, które podaliśmy dla stałych znakowych.
/* Stałe (C)2016 mgr Jerzy Wałaszek Data utworzenia: 17.10.2016 */ #include <stdio.h> #include <stdlib.h> #include <locale.h> int main() { char * a = "Aleksander IX"; // wskaźnik char b[] = "Jan Kowalski"; // tablica znakowa setlocale(LC_ALL,""); printf("Tekst nr 1: %s\n" "Tekst nr 2: %s\n", a, b); return 0; } |
Tutaj muszę wyjaśnić różnicę pomiędzy stałą znakową 'A' a stałą łańcuchową "A", ponieważ uczniowie często je mylą. Różnica jest zasadnicza. Wartością stałej znakowej 'A' jest kod ASCII litery A, czyli 65. Wartością stałej łańcuchowej "A" jest adres literki A, czyli wskaźnik do komórki pamięci, która przechowuje ten znak. Dodatkowo łańcuch "A" zajmuje w pamięci 2 komórki, ponieważ po A jest umieszczany znak NUL (o kodzie ASCII równym zero), który pełni rolę znacznika końca tekstu. Wynika z tego jasno, że 'A' jest czymś zupełnie innym od "A" i nie można stosować ich zamiennie. Zapamiętaj to na przyszłość.
Brzmi to dziwnie, ale coś takiego istnieje w języku C. Jeśli definicję zmiennej poprzedzimy słowem const, to powstanie zmienna, której możemy w definicji nadać wartość, lecz wartości tej program nie będzie już mógł zmieniać.
/* Stałe (C)2016 mgr Jerzy Wałaszek Data utworzenia: 17.10.2016 */ #include <stdio.h> #include <stdlib.h> #include <locale.h> int main() { const float pi = 3.1415629535; setlocale(LC_ALL,""); pi++; // tu otrzymasz błąd!!! return 0; } |
Po co nam zmienne, których wartości nie daje się zmieniać? Musisz uwierzyć na słowo, czasem są potrzebne. Poza tym pozwalają one zastępować wartości liczbowe, które są długie, a musimy je często używać w programie. Użycie const przy definicji takiej zmiennej gwarantuje nam, że w programie jej wartość nie ulegnie zmianie, np. przez nieuwagę.
Istnieje jeszcze jedna możliwość zdefiniowania stałej za pomocą preprocesora. Przypomnijmy: preprocesor jest częścią kompilatora, która wstępnie przetwarza plik źródłowy programu zanim zostanie on poddany kompilacji. Preprocesor wyszukuje w tekście źródłowym swoje dyrektywy i je wykonuje. Np. praktycznie każdy program w języku C posiada na początku dyrektywę #include, która dołącza plik z definicjami. Tych dyrektyw jest więcej.
Za pomocą dyrektywy #define możesz zdefiniować dowolną nazwę i przypisać jej dowolny tekst. Działa to w ten sposób, iż po zdefiniowaniu takiej nazwy preprocesor zastąpi wszystkie jej wystąpienia w programie tekstem, który tej nazwie przypiszemy. Składnia polecenia jest następująca:
#define nazwa dowolny tekst |
Reguły tworzenia nazw stałych symbolicznych są takie same jak dla zmiennych. Programiści stosują zwykle nazwy zbudowane z dużych liter i tobie też proponuję przyjąć tę konwencję.
/* Stałe (C)2016 mgr Jerzy Wałaszek Data utworzenia: 17.10.2016 */ #include <stdio.h> #include <stdlib.h> #include <locale.h> #define POWITANIE "Witamy w C\n" #define MAXU 4294967295 int main() { setlocale(LC_ALL,""); printf(POWITANIE); printf("Maksymalna liczba bez znaku to %u\n", MAXU); return 0; } |
Dyrektywa #define pochodzi z początków stosowania języka C. Często jest bardzo użyteczna w rękach doświadczonego programisty. Jednak stwarza różne problemy, chociażby takie, że zamiana stałej symbolicznej na tekst odbywa się poza kompilatorem – kompilator nawet się nie dowie, że w programie była taka stała, ponieważ preprocesor zastąpi ją tekstem zanim plik źródłowy przekaże do kompilacji.
Obecnie zaleca się używanie stałych zmiennych zamiast stałych symbolicznych. Zmienne są obiektami, które tworzy program i kompilator może sprawdzić poprawność ich użycia. Oczywiście nikt cię do tego nie zmusi.
Wyrażenie stałe (ang. constant expression) jest wyrażeniem, którego wartość zależy tylko od użytych w nim stałych. Wartość takiego wyrażenia jest wyliczana w trakcie kompilacji. Dzięki temu bez względu na złożoność wyrażenia w czasie wykonania komputer nie traci już czasu na obliczenia, ponieważ całe wyrażenie zostaje zastąpione jego wartością. Na przykład:
a = 5 * (12 - 2) / 10; |
zostaje zastąpione w czasie kompilacji przez:
a = 5; |
Z tego względu programiści często używają wyrażeń stałych, ponieważ są one bardziej czytelne od ich wartości, a mamy je za darmo, bez utraty szybkości działania lub zwiększenia wielkości programu, co jest bardzo istotne w mikrokontrolerach posiadających niewiele pamięci.
Operator negacji bitowej ~ daje w wyniku argument ze zmienionymi na przeciwne wartościami wszystkich bitów:
b | ~b |
0 | 1 |
1 | 0 |
Na przykład:
~ | 0011010111 |
1100101000 |
Uruchom program:
/* Bity (C)2016 mgr Jerzy Wałaszek Data utworzenia: 19.10.2016 */ #include <stdio.h> #include <stdlib.h> #include <locale.h> // Wczytuje liczbę dwójkową //------------------------- char rb() { char w = 0, *p, t[9]; printf("Liczba binarna (max 8 cyfr) = "); scanf("%8s",t); for (p = t; *p; p++) { w += w; w += *p - '0'; } return w; } // Wyświetla v jako liczbę dwójkową //-------------------------------- void pb(char v) { unsigned maska; for(maska = 0b10000000; maska; maska >>= 1) if(v & maska) putchar('1'); else putchar('0'); } int main() { char a = rb(); setlocale(LC_ALL,""); printf("\n~ "); pb(a); printf("\n-----------\n"); printf(" "); pb(~a); printf("\n\n"); return 0; } |
Jest to funkcja dwuargumentowa. Odpowiadające sobie bity obu argumentów są poddawane operacji logicznej alternatywy:
a | b | a | b |
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 1 |
Na przykład:
1110001001 | |
| | 0011010110 |
1111011111 |
Uruchom program:
/* Bity (C)2016 mgr Jerzy Wałaszek Data utworzenia: 19.10.2016 */ #include <stdio.h> #include <stdlib.h> #include <locale.h> // Wczytuje liczbę dwójkową //------------------------- char rb() { char w = 0, *p, t[9]; printf("Liczba binarna (max 8 cyfr) = "); scanf("%8s",t); for (p = t; *p; p++) { w += w; w += *p - '0'; } return w; } // Wyświetla v jako liczbę dwójkową //-------------------------------- void pb(char v) { unsigned maska; for(maska = 0b10000000; maska; maska >>= 1) if(v & maska) putchar('1'); else putchar('0'); } int main() { char a = rb(); char b = rb(); setlocale(LC_ALL,""); printf("\n "); pb(a); printf("\n| "); pb(b); printf("\n-----------"); printf("\n "); pb(a | b); printf("\n\n"); return 0; } |
Podobnie jak alternatywa bitowa jest to operacja dwuargumentowa na odpowiadających sobie bitach obu argumentów.
a | b | a & b |
0 | 0 | 0 |
0 | 1 | 0 |
1 | 0 | 0 |
1 | 1 | 1 |
Na przykład:
1110001001 | |
& | 0011010111 |
0010000001 |
Uruchom program:
/* Bity (C)2016 mgr Jerzy Wałaszek Data utworzenia: 19.10.2016 */ #include <stdio.h> #include <stdlib.h> #include <locale.h> // Wczytuje liczbę dwójkową //------------------------- char rb() { char w = 0, *p, t[9]; printf("Liczba binarna (max 8 cyfr) = "); scanf("%8s",t); for (p = t; *p; p++) { w += w; w += *p - '0'; } return w; } // Wyświetla v jako liczbę dwójkową //-------------------------------- void pb(char v) { unsigned maska; for(maska = 0b10000000; maska; maska >>= 1) if(v & maska) putchar('1'); else putchar('0'); } int main() { char a = rb(); char b = rb(); setlocale(LC_ALL,""); printf("\n "); pb(a); printf("\n& "); pb(b); printf("\n-----------"); printf("\n "); pb(a & b); printf("\n\n"); return 0; } |
W języku C dla bitów zdefiniowana jest dodatkowa funkcja, zwana różnicą symetryczną lub sumą modulo 2. Jest to operacja dwuargumentowa wykonywana na odpowiadających sobie bitach obu argumentów. Tabelka jest następująca:
a | b | a ^ b |
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 0 |
Na przykład:
1110001001 | |
^ | 0011010111 |
1101011110 |
Uruchom program:
/* Bity (C)2016 mgr Jerzy Wałaszek Data utworzenia: 19.10.2016 */ #include <stdio.h> #include <stdlib.h> #include <locale.h> // Wczytuje liczbę dwójkową //------------------------- char rb() { char w = 0, *p, t[9]; printf("Liczba binarna (max 8 cyfr) = "); scanf("%8s",t); for (p = t; *p; p++) { w += w; w += *p - '0'; } return w; } // Wyświetla v jako liczbę dwójkową //-------------------------------- void pb(char v) { unsigned maska; for(maska = 0b10000000; maska; maska >>= 1) if(v & maska) putchar('1'); else putchar('0'); } int main() { char a = rb(); char b = rb(); setlocale(LC_ALL,""); printf("\n "); pb(a); printf("\n^ "); pb(b); printf("\n-----------"); printf("\n "); pb(a ^ b); printf("\n\n"); return 0; } |
Operator przesunięcia bitowego w lewo << ma następującą składnię:
wyrażenie1 << wyrażenie2 |
Działanie jest następujące:
Komputer wylicza wartości wyrażenia1 i wyrażenia2. Następnie bity wyrażenia1 są przesuwane w lewo o tyle pozycji, ile wynosi wyrażenie2. Na pozycje najmłodszych bitów wstawiane są bity o wartości 0.Na przykład:
00000101 << 0 = 00000101 00000101 << 1 = 00001010 00000101 << 2 = 00010100 00000101 << 3 = 00101000 ... |
Uruchom program:
/* Bity (C)2016 mgr Jerzy Wałaszek Data utworzenia: 19.10.2016 */ #include <stdio.h> #include <stdlib.h> #include <locale.h> // Wczytuje liczbę dwójkową //------------------------- unsigned rb() { char *p, t[9]; unsigned w = 0; printf("Liczba binarna (max 8 cyfr) = "); scanf("%8s",t); for (p = t; *p; p++) { w += w; w += *p - '0'; } return w; } // Wyświetla v jako liczbę dwójkową //-------------------------------- void pbn(unsigned v, unsigned b) { unsigned maska; for(maska = 1 << b; maska; maska >>= 1) if(v & maska) putchar('1'); else putchar('0'); } int main() { unsigned a = rb(); int i; setlocale(LC_ALL,""); printf("\n"); for(i = 0; i <= 7; i++) { pbn(a,8); printf(" << %d = ",i); pbn(a << i, 16); printf("\n"); } return 0; } |
Jeśli argument1 potraktujemy jako liczbę, to przesunięcie bitów o 1 pozycję w lewo odpowiada pomnożeniu przez 2, o 2 pozycje w lewo odpowiada pomnożeniu przez 4, a przesunięcie o k pozycji w lewo odpowiada pomnożeniu przez 2k. Operacja przesunięcia bitowego jest wykonywana bardzo szybko w przeciwieństwie do operacji mnożenia i warto o tym pamiętać, szczególnie przy programowaniu prostych mikrokontrolerów, które mogą nie posiadać sprzętowego mnożenia i trzeba je wykonywać programowo, co zajmuje cenny czas.
Przesunięcie bitowe, jak każdy operator dwuargumentowy, można stosować w operacji modyfikacji zmiennej:
Operator przesunięcia bitowego w prawo posiada następującą składnię:
wyrażenie1 >> wyrażenie2 |
Działanie operatora jest następujące:
Obliczane są oba wyrażenia. Wynikiem jest wyrazenie1 z przesuniętymi w prawo bitami o tyle pozycji, ile wynosi wartość wyrażenia2. Bity, które wysuwają sie poza bit b0 są tracone. Na pozycję najstarszych bitów są wprowadzane:Na przykład:
00011101 >> 0 = 00011101 00011101 >> 1 = 00001110 00011101 >> 2 = 00000111 00011101 >> 3 = 00000011 ... |
Przesunięcie bitów o 1 pozycję w prawo odpowiada podzieleniu całkowitoliczbowemu argumentu1 przez 2, przesunięcie o k bitów w prawo odpowiada podzieleniu przez 2k. Zwróć uwagę, że liczby int w kodzie U2 są przesuwane z kopiowaniem bitu znakowego.
Na przykład:
10011100 >> 1 = 11001110 : -100 / 2 =
-50 10011100 >> 2 = 11100111 : -100 / 4 = -25 10011100 >> 3 = 11110011 : -100 / 8 = -13 |
Wynik jest zaokrąglany do liczby całkowitej.
/* Bity (C)2016 mgr Jerzy Wałaszek Data utworzenia: 19.10.2016 */ #include <stdio.h> #include <stdlib.h> #include <locale.h> // Wczytuje liczbę dwójkową //------------------------- unsigned rb() { char *p, t[17]; unsigned w = 0; printf("Liczba binarna (max 16 cyfr) = "); scanf("%16s",t); for (p = t; *p; p++) { w += w; w += *p - '0'; } return w; } // Wyświetla v jako liczbę dwójkową //-------------------------------- void pb(unsigned v) { unsigned maska; for(maska = 1 << 15; maska; maska >>= 1) if(v & maska) putchar('1'); else putchar('0'); } int main() { unsigned a = rb(); int i; setlocale(LC_ALL,""); printf("\n"); for(i = 0; i <= 7; i++) { pb(a); printf(" >> %d = ",i); pb(a >> i); printf("\n"); } return 0; } |
Operacje te realizujemy za pomocą poznanych funkcji bitowych. Wprowadźmy pojęcie maski bitowej. Jest to wartość binarna, w której tylko jeden bit jest ustawiony na 1 lub tylko 1 bit ma wartość 0. Maska posiada zawsze tyle samo bitów, co dana, an której wykonujemy określoną operację bitową.
Przykład:
00010000 – 8-bitowa maska z ustawionym bitem
b4. 11111011 – 8-bitowa maska z wyzerowanym bitem b2. |
Maskę pierwszego typu tworzymy za pomocą operacji przesunięcia bitowego w lewo:
1 << nr_bitu |
Maskę drugiego typu tworzymy podobnie, lecz dodatkowo negujemy jej bity:
~(1 << nr_bitu) |
Aby odczytać stan bitu o numerze b (bity numerujemy od 0) w argumencie a, tworzymy maskę z ustawionym bitem o numerze b (pierwszy typ maski), a następnie wykonujemy bitową koniunkcję maski oraz argumentu a. Jeśli wynik jest różny od 0, to bit b argumentu a ma wartość 1, w przeciwnym razie bit b argumentu a ma wartość 0:
a & (1 << b) |
Na przykład:
|
|
Uruchom program:
/* Bity (C)2016 mgr Jerzy Wałaszek Data utworzenia: 21.10.2016 */ #include <stdio.h> #include <stdlib.h> #include <locale.h> // Wczytuje liczbę dwójkową //------------------------- unsigned rb() { char *p, t[9]; unsigned w = 0; printf("Liczba binarna (max 8 cyfr) = "); scanf("%8s", t); for (p = t; *p; p++) { w += w; w += *p - '0'; } return w; } // Wyświetla v jako liczbę dwójkową //-------------------------------- void pb(unsigned v) { unsigned maska; for(maska = 1 << 7; maska; maska >>= 1) if(v & maska) putchar('1'); else putchar('0'); } int main() { unsigned a = rb(); int i; setlocale(LC_ALL,""); printf("\n"); for(i = 0; i <= 7; i++) { pb(a); printf(" : bit nr %d = %d\n", i, a & (1 << i) ? 1 : 0); } return 0; } |
Operacja negacji bitu b argumentu a polega na wykonaniu nad argumentem a i maską z stawionym bitem na pozycji b (pierwszy typ maski) operacji bitowej różnicy symetrycznej:
a ^ (1 << b) |
Na przykład:
|
|
Uruchom program:
/* Bity (C)2016 mgr Jerzy Wałaszek Data utworzenia: 21.10.2016 */ #include <stdio.h> #include <stdlib.h> #include <locale.h> // Wczytuje liczbę dwójkową //------------------------- unsigned rb() { char *p, t[9]; unsigned w = 0; printf("Liczba binarna (max 8 cyfr) = "); scanf("%8s", t); for (p = t; *p; p++) { w += w; w += *p - '0'; } return w; } // Wyświetla v jako liczbę dwójkową //-------------------------------- void pb(unsigned v) { unsigned maska; for(maska = 1 << 7; maska; maska >>= 1) if(v & maska) putchar('1'); else putchar('0'); } int main() { unsigned a = rb(); int i; setlocale(LC_ALL,""); printf("\n"); for(i = 0; i <= 7; i++) { pb(a); printf(" : negacja bitu nr %d = ", i); pb(a ^ (1 << i)); printf("\n"); } return 0; } |
Wyzerowanie bitu b argumentu a uzyskamy wykonując bitową operację koniunkcji nad argumentem a i maską z wyzerowanym bitem na pozycji b (drugi typ maski):
a & ~(1 << b) |
Na przykład:
|
|
Uruchom program:
/* Bity (C)2016 mgr Jerzy Wałaszek Data utworzenia: 21.10.2016 */ #include <stdio.h> #include <stdlib.h> #include <locale.h> // Wczytuje liczbę dwójkową //------------------------- unsigned rb() { char *p, t[9]; unsigned w = 0; printf("Liczba binarna (max 8 cyfr) = "); scanf("%8s", t); for (p = t; *p; p++) { w += w; w += *p - '0'; } return w; } // Wyświetla v jako liczbę dwójkową //-------------------------------- void pb(unsigned v) { unsigned maska; for(maska = 1 << 7; maska; maska >>= 1) if(v & maska) putchar('1'); else putchar('0'); } int main() { unsigned a = rb(); int i; setlocale(LC_ALL,""); printf("\n"); for(i = 0; i <= 7; i++) { pb(a); printf(" : zerowanie bitu nr %d = ", i); pb(a & ~(1 << i)); printf("\n"); } return 0; } |
Ustawienie na 1 bitu b argumentu a uzyskamy wykonując bitową operację alternatywy nad argumentem a i maską z ustawionym bitem na pozycji b (pierwszy typ maski):
a | (1 << b) |
Na przykład:
|
|
Uruchom program:
/* Bity (C)2016 mgr Jerzy Wałaszek Data utworzenia: 21.10.2016 */ #include <stdio.h> #include <stdlib.h> #include <locale.h> // Wczytuje liczbę dwójkową //------------------------- unsigned rb() { char *p, t[9]; unsigned w = 0; printf("Liczba binarna (max 8 cyfr) = "); scanf("%8s", t); for (p = t; *p; p++) { w += w; w += *p - '0'; } return w; } // Wyświetla v jako liczbę dwójkową //-------------------------------- void pb(unsigned v) { unsigned maska; for(maska = 1 << 7; maska; maska >>= 1) if(v & maska) putchar('1'); else putchar('0'); } int main() { unsigned a = rb(); int i; setlocale(LC_ALL,""); printf("\n"); for(i = 0; i <= 7; i++) { pb(a); printf(" : ustawienie bitu nr %d = ", i); pb(a | (1 << i)); printf("\n"); } return 0; } |
Zwróć uwagę, że we wszystkich wyrażeniach argument a występuje na początku wyrażenia. Umożliwia to zastosowanie modyfikacji zmiennej:
Zapraszam do następnego rozdziału.
Zespół Przedmiotowy Chemii-Fizyki-Informatyki w I Liceum Ogólnokształcącym im. Kazimierza Brodzińskiego w Tarnowie ul. Piłsudskiego 4 ©2024 mgr Jerzy Wałaszek |
Materiały tylko do użytku dydaktycznego. Ich kopiowanie i powielanie jest dozwolone
pod warunkiem podania źródła oraz niepobierania za to pieniędzy.
Pytania proszę przesyłać na adres email:
Serwis wykorzystuje pliki cookies. Jeśli nie chcesz ich otrzymywać, zablokuj je w swojej przeglądarce.
Informacje dodatkowe.