• Nie Znaleziono Wyników

Modyfikatory

W dokumencie Stworzone na Wikibooks, (Stron 40-45)

Pierwszy program

7.4 Modyfikatory

7.4.1 volatile

volatileznaczy ulotny. Oznacza to, że kompilator wyłączy dla takiej zmiennej optymaliza-cje typu zastąpienia przez stałą lub zawartość rejestru, za to wygeneruje kod, który będzie odwoływał się zawsze do komórek pamięci danego obiektu. Zapobiegnie to błędowi, gdy obiekt zostaje zmieniony przez część programu, która nie ma zauważalnego dla kompilatora związku z danym fragmentem kodu lub nawet przez zupełnie inny proces.

volatile float liczba1;

float liczba2;

{

printf ("%f\n%f\n", liczba1, liczba2);

/* instrukcje nie związane ze zmiennymi */

printf ("%f\n%f", liczba1, liczba2);

}

Jeżeli zmienne liczba i liczba zmienią się niezauważalnie dla kompilatora to odczytując :

ˆ liczba — nastąpi odwołanie do komórek pamięci. Kompilator pobierze nową wartość zmiennej.

ˆ liczba — kompilator może wypisać poprzednią wartość, którą przechowywał w reje-strze.

Modyfikatorvolatilejest rzadko stosowany i przydaje się w wąskich zastosowaniach, jak współbieżność i współdzielenie zasobów oraz przerwania systemowe.

7.4.2 register

Jeżeli utworzymy zmienną, której będziemy używać w swoim programie bardzo często, mo-żemy wykorzystać modyfikatorregister. Kompilator może wtedy umieścić zmienną w re-jestrze, do którego ma szybki dostęp, co przyśpieszy odwołania do tej zmiennej

register int liczba ;

W nowoczesnych kompilatorach ten modyfikator praktycznie nie ma wpływu na pro-gram. Optymalizator sam decyduje czy i co należy umieścić w rejestrze. Nie mamy żadnej gwarancji, że zmienna tak zadeklarowana rzeczywiście się tam znajdzie, chociaż dostęp do niej może zostać przyspieszony w inny sposób. Raczej powinno się unikać tego typu kon-strukcji w programie.

1Wiąże się to z pewnymi uwarunkowaniami historycznymi. Podręcznik do języka C duetu K&R zakładał, że typ int miał się odnosić do typowej dla danego procesora długości liczby całkowitej. Natomiast, jeśli procesor mógł obsługiwać typy dłuższe lub krótsze stosownego znaczenia nabierały modyfikatory short i long. Dobrym przykładem może być architektura i386, która umożliwia obliczenia na liczbach 16-bitowych. Dlatego też modyfikator short powoduje skrócenie zmiennej do 16 bitów.

7.5. UWAGI 41

7.4.3 static

Pozwala na zdefiniowanie zmiennej statycznej. “Statyczność” polega na zachowaniu warto-ści pomiędzy kolejnymi definicjami tej samej zmiennej. Jest to przede wszystkim przydatne w funkcjach. Gdy zdefiniujemy zmienną w ciele funkcji, to zmienna ta będzie od nowa defi-niowana wraz z domyślną wartością (jeżeli taką podano). W wypadku zmiennej określonej jako statyczna, jej wartość się nie zmieni przy ponownym wywołaniu funkcji. Na przykład:

void dodaj(int liczba) {

int zmienna = 0; // bez static zmienna = zmienna + liczba;

printf ("Wartosc zmiennej %d\n", zmienna);

}

Gdy wywołamy tę funkcję np.  razy w ten sposób:

dodaj(3);

dodaj(5);

dodaj(4);

to ujrzymy na ekranie:

Wartosc zmiennej 3 Wartosc zmiennej 5 Wartosc zmiennej 4

jeżeli jednak deklarację zmiennej zmienimy nastatic int zmienna = 0, to wartość zmiennej zostanie zachowana i po podobnym wykonaniu funkcji powinnyśmy ujrzeć:

Wartosc zmiennej 3 Wartosc zmiennej 8 Wartosc zmiennej 12

Zupełnie co innego oznaczastaticzastosowane dla zmiennej globalnej. Jest ona wtedy widoczna tylko w jednym pliku. Zobacz też: rozdziałBiblioteki.

7.4.4 extern

Przezexternoznacza się zmienne globalne zadeklarowane w innych plikach — informujemy w ten sposób kompilator, żeby nie szukał jej w aktualnym pliku. Zobacz też: rozdział Biblio-teki.

7.4.5 auto

Zupełnym archaizmem jest modyfikatorauto, który oznacza tyle, że zmienna jest lokalna.

Ponieważ zmienna zadeklarowana w dowolnym bloku zawsze jest lokalna, modyfikator ten nie ma obecnie żadnego zastosowania praktycznego. autojest spadkiem po wcześniejszych językach programowania, na których oparty jest C (np.B).

7.5 Uwagi

ˆ JęzykC++pozwala na mieszanie deklaracji zmiennych z kodem. Więcej informacji w C++/Zmienne.

Rozdział 8

Operatory

8.1 Przypisanie

Operator przypisania (,,=”), jak sama nazwa wskazuje, przypisuje wartość prawego argu-mentu lewemu, np.:

int a = 5, b;

b = a;

printf("%d\n", b); /* wypisze 5 */

Operator ten ma łączność prawostronną tzn. obliczanie przypisań następuje z prawa na lewo i zwraca on przypisaną wartość, dzięki czemu może być użyty kaskadowo:

int a, b, c;

a = b = c = 3;

printf("%d %d %d\n", a, b, c); /* wypisze "3 3 3" */

8.1.1 Skrócony zapis

C umożliwia też skrócony zapis postacia #= b;, gdzie # jest jednym z operatorów: +, -, *, /,

&,|, ˆ, << lub >> (opisanych niżej). Ogólnie rzecz ujmując zapisa #= b;jest równoważny zapisowia = a # (b);, np.:

int a = 1;

a += 5; /* to samo, co a = a + 5; */

a /= a + 2; /* to samo, co a = a / (a + 2); */

a %= 2; /* to samo, co a = a % 2; */

Początkowo skrócona notacja miała następującą składnię: a =# b, co często prowadziło do niejasności, np. i =- (i = - czy też i = i-?). Dlatego też zdecydowano się zmienić kolejność operatorów.

43

8.2 Rzutowanie

Zadaniem rzutowania jest konwersja danej jednego typu na daną innego typu. Konwer-sja może być niejawna (domyślna konwerKonwer-sja przyjęta przez kompilator) lub jawna (podana explicite przez programistę). Oto kilka przykładów konwersji niejawnej:

int i = 42.7; /* konwersja z double do int */

float f = i; /* konwersja z int do float */

double d = f; /* konwersja z float do double */

unsigned u = i; /* konwersja z int do unsigned int */

f = 4.2; /* konwersja z double do float */

i = d; /* konwersja z double do int */

char *str = "foo"; /* konwersja z const char* do char* [1] */

const char *cstr = str; /* konwersja z char* do const char* */

void *ptr = str; /* konwersja z char* do void* */

Podczas konwersji zmiennych zawierających większe ilości danych do typów prostszych (np. double do int) musimy liczyć się z utratą informacji, jak to miało miejsce w pierwszej linijce — zmienna int nie może przechowywać części ułamkowej toteż została ona odcięta i w rezultacie zmiennej została przypisana wartość .

Zaskakująca może się wydać linijka oznaczona przez1. Niejawna konwersja z typu const char* do typu char* nie jest dopuszczana przez standard C. Jednak literały napisowe (które są typu const char*) stanowią tutaj wyjątek. Wynika on z faktu, że były one używane na długo przed wprowadzeniem słówka const do języka i brak wspomnianego wyjątku spowodowałby, że duża część kodu zostałaby nagle zakwalifikowana jako niepoprawny kod.

Do jawnego wymuszenia konwersji służy jednoargumentowy operator rzutowania, np.:

double d = 3.14;

int pi = (int)d; /* 1 */

pi = (unsigned)pi >> 4; /* 2 */

W pierwszym przypadku operator został użyty, by zwrócić uwagę na utratę precyzji. W drugim, dlatego że bez niego operator przesunięcia bitowego zachowuje się trochę inaczej.

Obie konwersje przedstawione powyżej są dopuszczane przez standard jako jawne kon-wersje (tj. konwersja z double do int oraz z int do unsigned int), jednak niektóre konkon-wersje są błędne, np.:

const char *cstr = "foo";

char *str = cstr;

W takich sytuacjach można użyć operatora rzutowania by wymusić konwersję:

const char *cstr = "foo";

char *str = (char*)cstr;

Należy unikać jednak takich sytuacji i nigdy nie stosować rzutowania by uciszyć kompi-lator. Zanim użyjemy operatora rzutowania należy się zastanowić co tak naprawdę będzie on robił i czy nie ma innego sposobu wykonania danej operacji, który nie wymagałby podej-mowania tak drastycznych kroków.

W dokumencie Stworzone na Wikibooks, (Stron 40-45)