Jak pracować ze zmiennymi, typami danych i wyrażeniami arytmetycznymi w języku programowania C

Język C posiada bogatą gamę operatorów matematycznych, których można używać do manipulowania danymi. W tym rozdziale z podręcznika Programming in C, 4th Edition, Stephen G. Kochan omawia typy danych int, float, double, char i _Bool, modyfikowanie typów danych za pomocą short, long i long long, zasady nazywania zmiennych, podstawowe operatory matematyczne i wyrażenia arytmetyczne oraz rzutowanie typów.

Prawdziwą siłą tworzonych programów jest manipulowanie danymi. Aby naprawdę skorzystać z tej mocy, musisz lepiej zrozumieć różne typy danych, których możesz używać, a także jak tworzyć i nazywać zmienne. Język C posiada bogatą gamę operatorów matematycznych, których można używać do manipulowania danymi. W tym rozdziale omówione zostaną:

  • Typy danych int, float, double, char i _Bool
  • Modyfikowanie typów danych za pomocą short, long, i long long
  • Zasady nazywania zmiennych
  • Podstawowe operatory matematyczne i wyrażenia arytmetyczne
  • Rzutowanie typów

Zrozumienie typów danych i stałych

Poznałeś już podstawowy typ danych C int. Jak pamiętasz, zmienna zadeklarowana jako typ int może być używana tylko do przechowywania wartości całkowitych, czyli takich, które nie zawierają miejsc dziesiętnych.

Język programowania C udostępnia cztery inne podstawowe typy danych: float, double, char i _Bool. Zmienna zadeklarowana jako typ float może być użyta do przechowywania liczb zmiennoprzecinkowych (wartości zawierających miejsca dziesiętne). Typ double jest taki sam jak typ float, tylko z mniej więcej dwukrotnie większą precyzją. Typ danych char może być użyty do przechowywania pojedynczych znaków, takich jak litera 'a’, cyfra '6′ lub średnik (’;’) (więcej na ten temat w dalszej części). Wreszcie, typ danych _Bool może być używany do przechowywania wartości 0 lub 1. Zmienne tego typu są używane do wskazywania sytuacji typu włącz/wyłącz, tak/nie, lub prawda/fałsz. Te wybory jeden-albo-inny są również znane jako wybory binarne.

W C, każda liczba, pojedynczy znak lub ciąg znaków jest znany jako stała. Na przykład, liczba 58 reprezentuje stałą wartość całkowitą. Ciąg znaków „Programowanie w C jest zabawne” jest przykładem stałego ciągu znaków. Wyrażenia składające się wyłącznie z wartości stałych są nazywane wyrażeniami stałymi. Tak więc, wyrażenie

128 + 7 - 17

jest wyrażeniem stałym, ponieważ każdy z warunków tego wyrażenia jest wartością stałą. Gdyby jednak i zostało zadeklarowane jako zmienna całkowita, wyrażenie

128 + 7 – i

nie byłoby wyrażeniem stałym, ponieważ jego wartość zmieniałaby się w zależności od wartości i. Jeśli i wynosi 10, wyrażenie jest równe 125, ale jeśli i wynosi 200, wyrażenie jest równe -65.

Typ całkowity int

W języku C stała całkowita składa się z sekwencji jednej lub więcej cyfr. Znak minus poprzedzający sekwencję wskazuje, że wartość jest ujemna. Wartości 158, -10, oraz 0 są poprawnymi przykładami stałych całkowitych. Między cyframi nie są dozwolone spacje, a wartości większe niż 999 nie mogą być wyrażane za pomocą przecinków. (Tak więc, wartość 12,000 nie jest poprawną stałą całkowitą i musi być zapisana jako 12000.)

Dwa specjalne formaty w C umożliwiają wyrażanie stałych całkowitych w podstawie innej niż dziesiętna (podstawa 10). Jeśli pierwszą cyfrą wartości całkowitej jest 0, to liczba ta jest traktowana jako wyrażona w notacji ósemkowej – czyli w podstawie 8. W takim przypadku, pozostałe cyfry wartości muszą być poprawnymi cyframi podstawy 8, a więc muszą być to cyfry 0-7. Tak więc, aby wyrazić wartość 50 w podstawie 8 w C, która jest równoważna wartości 40 w systemie dziesiętnym, stosuje się notację 050. Podobnie stała oktalna 0177 reprezentuje wartość dziesiętną 127 (1 × 64 + 7 × 8 + 7). Wartość całkowita może być wyświetlana na terminalu w notacji ósemkowej przez użycie znaków formatu %o w łańcuchu formatu instrukcji printf(). W takim przypadku wartość jest wyświetlana w systemie ósemkowym bez zera wiodącego. Znak formatu %#o powoduje wyświetlenie zera przed wartością ósemkową.

Jeśli stała całkowita jest poprzedzona zerem i literą x (małą lub wielką), to wartość jest traktowana jako wyrażona w notacji szesnastkowej (podstawa 16). Bezpośrednio za literą x znajdują się cyfry wartości szesnastkowej, które mogą składać się z cyfr 0-9 i liter a-f (lub A-F). Litery te reprezentują odpowiednio wartości 10-15. Tak więc, aby przypisać wartość szesnastkową FFEF0D do zmiennej całkowitej o nazwie rgbColor, można użyć instrukcji

rgbColor = 0xFFEF0D;

. Znaki formatu %x wyświetlają wartość w formacie szesnastkowym bez początkowego 0x, a dla cyfr szesnastkowych używają małych liter a-f. Aby wyświetlić wartość z początkowym 0x, należy użyć znaków formatu %#x, jak w poniższym przykładzie:

printf ("Color is %#x\n", rgbColor);

Duża litera x, jak w %X lub %#X, może być użyta do wyświetlenia początkowego x i następujących po nim cyfr szesnastkowych przy użyciu dużych liter.

Rozmiary i zakresy pamięci

Każda wartość, niezależnie od tego, czy jest to znak, liczba całkowita, czy zmiennoprzecinkowa, ma związany z nią zakres wartości. Zakres ten ma związek z ilością pamięci masowej, która jest przydzielona do przechowywania określonego typu danych. Ogólnie rzecz biorąc, ilość ta nie jest zdefiniowana w języku. Zazwyczaj zależy ona od komputera, na którym pracujesz i dlatego nazywana jest zależną od implementacji lub maszyny. Na przykład, liczba całkowita może zajmować 32 bity na twoim komputerze, lub może być przechowywana w 64. Nigdy nie powinieneś pisać programów, które przyjmują jakiekolwiek założenia dotyczące rozmiaru typów danych. Masz jednak gwarancję, że dla każdego podstawowego typu danych zostanie zarezerwowana pewna minimalna ilość miejsca. Na przykład, gwarantowane jest, że wartość całkowita będzie przechowywana w minimum 32 bitach pamięci, co jest rozmiarem „słowa” na wielu komputerach.

Typ liczb zmiennoprzecinkowych float

Zmienna zadeklarowana jako typu float może być używana do przechowywania wartości zawierających miejsca dziesiętne. Stała zmiennoprzecinkowa jest wyróżniona przez obecność kropki dziesiętnej. Można pominąć cyfry przed kropką dziesiętną lub cyfry po kropce dziesiętnej, ale oczywiście nie można pominąć obu. Wartości 3., 125.8, oraz -.0001 są poprawnymi przykładami stałych zmiennoprzecinkowych. Aby wyświetlić wartość zmiennoprzecinkową na terminalu, używa się znaków konwersji printf %f.

Stałe zmiennoprzecinkowe mogą być również wyrażone w notacji naukowej. Wartość 1.7e4 jest wartością zmiennoprzecinkową wyrażoną w tej notacji i reprezentuje wartość 1.7 × 104. Wartość przed literą e jest znana jako mantysa, podczas gdy wartość, która następuje po niej, jest nazywana wykładnikiem. Wykładnik ten, który może być poprzedzony opcjonalnym znakiem plus lub minus, reprezentuje potęgę 10, przez którą mantysa ma być pomnożona. Tak więc, w stałej 2.25e-3, 2.25 jest wartością mantysy a -3 jest wartością wykładnika. Stała ta reprezentuje wartość 2,25 × 10-3, czyli 0,00225. Nawiasem mówiąc, litera e, która oddziela mantysę od wykładnika, może być pisana małymi lub dużymi literami.

Aby wyświetlić wartość w notacji naukowej, w łańcuchu formatu printf() należy podać znaki formatu %e. Znaki formatu %g mogą być użyte, aby pozwolić funkcji printf() zdecydować, czy wyświetlić wartość zmiennoprzecinkową w normalnej notacji zmiennoprzecinkowej czy w notacji naukowej. Decyzja ta jest oparta na wartości wykładnika: Jeśli jest on mniejszy niż -4 lub większy niż 5, używany jest format %e (notacja naukowa); w przeciwnym razie używany jest format %f.

Do wyświetlania liczb zmiennoprzecinkowych używaj znaków formatu %g – daje on najbardziej estetyczne wyjście.

Szesnastkowa stała zmiennoprzecinkowa składa się z początkowego 0x lub 0X, po którym następuje jedna lub więcej cyfr dziesiętnych lub szesnastkowych, po których następuje p lub P, a następnie opcjonalnie podpisany wykładnik binarny. Na przykład, 0x0.3p10 reprezentuje wartość 3/16 × 210 = 0.5.

Typ o rozszerzonej precyzji double

Typ double jest bardzo podobny do typu float, ale jest używany zawsze wtedy, gdy zakres zapewniany przez zmienną float nie jest wystarczający. Zmienne zadeklarowane jako typu double mogą przechowywać mniej więcej dwa razy więcej cyfr znaczących niż zmienne typu float. Większość komputerów reprezentuje wartości podwójne przy użyciu 64 bitów.

Jeśli nie powiedziano inaczej, wszystkie stałe zmiennoprzecinkowe są traktowane przez kompilator C jako wartości podwójne. Aby jawnie wyrazić stałą zmiennoprzecinkową, dołącz do końca liczby literę f lub F, jak poniżej:

12.5f

Aby wyświetlić wartość podwójną, można użyć znaków formatu %f, %e lub %g, które są tymi samymi znakami formatu używanymi do wyświetlania wartości zmiennoprzecinkowej.

Typ pojedynczego znaku char

Zmienna char może być używana do przechowywania pojedynczego znaku.1 Stała znakowa jest tworzona przez zawarcie znaku w parze pojedynczych cudzysłowów. Tak więc 'a’, ’;’ i '0′ są poprawnymi przykładami stałych znakowych. Pierwsza stała reprezentuje literę a, druga jest średnikiem, a trzecia jest znakiem zero, który nie jest tym samym co cyfra zero. Nie należy mylić stałej znakowej, która jest pojedynczym znakiem ujętym w pojedynczy cudzysłów, z łańcuchem znaków, który jest dowolną liczbą znaków ujętych w podwójny cudzysłów.

Stała znakowa 'n’ – znak nowej linii – jest poprawną stałą znakową, mimo że wydaje się być sprzeczna z regułą przytoczoną poprzednio. Dzieje się tak dlatego, że znak odwrotnego ukośnika jest znakiem specjalnym w systemie C i w rzeczywistości nie jest liczony jako znak. Innymi słowy, kompilator C traktuje znak '\n’ jako pojedynczy znak, mimo że w rzeczywistości tworzą go dwa znaki. Istnieją inne znaki specjalne, które są inicjowane znakiem odwrotnego ukośnika. Pełna lista znajduje się w dodatku A, „Podsumowanie języka C”.

Znaki formatu %c mogą być użyte w wywołaniu printf() do wyświetlenia wartości zmiennej char na terminalu.

Typ danych boolean _Bool

Zmienna _Bool jest zdefiniowana w języku jako wystarczająco duża, aby przechowywać tylko wartości 0 i 1. Dokładna ilość pamięci, która jest używana, jest nieokreślona. Zmienne _Bool są używane w programach, które muszą wskazać warunek Boolean. Na przykład, zmienna tego typu może być użyta do wskazania, czy wszystkie dane zostały odczytane z pliku.

Zgodnie z konwencją, 0 jest używane do wskazania wartości fałszywej, a 1 wskazuje wartość prawdziwą. Podczas przypisywania wartości do zmiennej _Bool, wartość 0 jest przechowywana jako 0 wewnątrz zmiennej, podczas gdy każda niezerowa wartość jest przechowywana jako 1.

Aby ułatwić pracę ze zmiennymi _Bool w twoim programie, standardowy plik nagłówkowy <stdbool.h> definiuje wartości bool, true i false. Przykład tego pokazano w Programie 5.10A w rozdziale 5, „Podejmowanie decyzji.”

W Programie 3.1 używane są podstawowe typy danych języka C.

Program 3.1 Korzystanie z podstawowych typów danych

Program 3.1 Wyjście

integerVar = 100floatingVar = 331.790009doubleVar = 8.440000e+11doubleVar = 8.44e+11charVar = WboolVar = 0;

Pierwsza instrukcja Programu 3.1 deklaruje zmienną integerVar jako zmienną całkowitą i przypisuje jej wartość początkową 100, tak jakby zamiast niej użyto dwóch poniższych instrukcji:

int integerVar;integerVar = 100;

W drugim wierszu wyjścia programu zauważ, że wartość 331.79, która jest przypisana do zmiennej floatingVar, jest w rzeczywistości wyświetlana jako 331.790009. W rzeczywistości, rzeczywista wyświetlana wartość zależy od konkretnego systemu komputerowego, z którego korzystasz. Powodem tej nieścisłości jest szczególny sposób, w jaki liczby są wewnętrznie reprezentowane w komputerze. Prawdopodobnie zetknąłeś się z tym samym rodzajem nieścisłości podczas pracy z liczbami na kalkulatorze kieszonkowym. Jeśli podzielisz 1 przez 3 na kalkulatorze, otrzymasz wynik .33333333, z być może kilkoma dodatkowymi 3s dołożonymi na końcu. Ciąg cyfr 3 to przybliżenie kalkulatora do jednej trzeciej. Teoretycznie powinno być ich nieskończenie wiele. Ale kalkulator może pomieścić tylko tyle cyfr, stąd nieodłączna niedokładność maszyny. Ten sam rodzaj niedokładności ma zastosowanie tutaj: Pewne wartości zmiennoprzecinkowe nie mogą być dokładnie reprezentowane wewnątrz pamięci komputera.

Podczas wyświetlania wartości zmiennych typu float lub double masz do wyboru trzy różne formaty. Znaki %f są używane do wyświetlania wartości w standardowy sposób. Jeśli nie podano inaczej, funkcja printf() zawsze wyświetla wartość zmiennej typu float lub double z zaokrągleniem do sześciu miejsc po przecinku. W dalszej części tego rozdziału zobaczysz, jak wybrać liczbę miejsc dziesiętnych, które są wyświetlane.

Znaki %e służą do wyświetlania wartości zmiennej typu float lub double w notacji naukowej. Ponownie, sześć miejsc dziesiętnych jest automatycznie wyświetlanych przez system.

W przypadku znaków %g, funkcja printf() wybiera pomiędzy %f i %e, a także automatycznie usuwa z wyświetlacza wszelkie końcowe zera. Jeśli po kropce dziesiętnej nie ma cyfr, to również nie jest ona wyświetlana.

W przedostatniej instrukcji printf(), znaki %c są używane do wyświetlenia pojedynczego znaku 'W’, który przypisałeś do charVar podczas deklarowania zmiennej. Pamiętaj, że podczas gdy łańcuch znaków (taki jak pierwszy argument funkcji printf()) jest ujęty w parę podwójnych cudzysłowów, stała znakowa musi być zawsze ujęta w parę pojedynczych cudzysłowów.

Ostatnia instrukcja printf() pokazuje, że zmienna _Bool może mieć swoją wartość wyświetlaną przy użyciu znaków formatu liczb całkowitych %i.

Specyfikatory typu: long, long long, short, unsigned i signed

Jeśli specyfikator long jest umieszczony bezpośrednio przed deklaracją int, zadeklarowana zmienna całkowita ma rozszerzony zakres w niektórych systemach komputerowych. Przykładem deklaracji long int może być

long int factorial;

To deklaruje zmienną factorial jako długą zmienną całkowitą. Podobnie jak w przypadku float i double, szczególna dokładność zmiennej long zależy od konkretnego systemu komputerowego. W wielu systemach, int i long int mają ten sam zakres i mogą być używane do przechowywania wartości całkowitych o szerokości do 32 bitów (231 – 1, lub 2,147,483,647).

Wartość stała typu long int jest tworzona przez opcjonalne dołączenie litery L (dużej lub małej) na końcu stałej całkowitej. Między liczbą a L nie są dozwolone spacje. Tak więc deklaracja

long int numberOfPoints = 131071100L;

deklaruje zmienną numberOfPoints jako typ long int z wartością początkową 131,071,100.

Aby wyświetlić wartość long int przy użyciu printf(), litera l jest używana jako modyfikator przed znakami formatu liczb całkowitych i, o oraz x. Oznacza to, że znaki formatu %li mogą być użyte do wyświetlenia wartości długiej liczby całkowitej w formacie dziesiętnym, znaki %lo mogą wyświetlić wartość w formacie ósemkowym, a znaki %lx mogą wyświetlić wartość w formacie szesnastkowym.

Istnieje również typ danych long long integer, więc

long long int maxAllowedStorage;

deklaruje, że wskazana zmienna ma określoną rozszerzoną dokładność, która jest gwarantowana jako co najmniej 64 bity szerokości. Zamiast pojedynczej litery l, w łańcuchu printf używane są dwie ls do wyświetlania długich długich liczb całkowitych, jak w „%lli”.

Specyfikator long jest również dozwolony przed deklaracją double, jak poniżej:

long double US_deficit_2004;

Długa stała podwójna jest zapisywana jako stała zmiennopozycyjna z literą l lub L bezpośrednio po niej, np

1.234e+7L

Aby wyświetlić długą podwójną, używany jest modyfikator L. Tak więc, %Lf wyświetla wartość long double w notacji zmiennoprzecinkowej, %Le wyświetla tę samą wartość w notacji naukowej, a %Lg mówi funkcji printf(), by wybrała między %Lf i %Le.

Specyfikator short, umieszczony przed deklaracją int, mówi kompilatorowi C, że dana deklarowana zmienna jest używana do przechowywania dość małych wartości całkowitych. Motywacją do używania zmiennych short jest przede wszystkim oszczędność miejsca w pamięci, co może być problemem w sytuacjach, w których program potrzebuje dużo pamięci, a ilość dostępnej pamięci jest ograniczona.

Na niektórych maszynach, short int zajmuje połowę ilości pamięci, co zwykła zmienna int. W każdym razie, masz gwarancję, że ilość miejsca przydzielonego dla short int nie będzie mniejsza niż 16 bitów.

Nie ma sposobu, aby jawnie napisać stałą typu short int w C. Aby wyświetlić zmienną typu short int, umieść literę h przed dowolnym ze zwykłych znaków konwersji liczb całkowitych: %hi, %ho, lub %hx. Alternatywnie, możesz także użyć któregoś ze znaków konwersji liczb całkowitych do wyświetlania krótkich intów, ze względu na sposób, w jaki mogą być one konwertowane na liczby całkowite, gdy są przekazywane jako argumenty do procedury printf().

Ostatni specyfikator, który może być umieszczony przed zmienną int, jest używany, gdy zmienna całkowita będzie używana do przechowywania tylko liczb dodatnich. Deklaracja

unsigned int counter;

deklaruje kompilatorowi, że zmienna licznik jest używana do przechowywania tylko wartości dodatnich. Ograniczając użycie zmiennej całkowitej do przechowywania wyłącznie liczb całkowitych dodatnich, zwiększa się dokładność zmiennej całkowitej.

Stałą bez znaku int tworzy się przez umieszczenie litery u (lub U) po stałej, jak poniżej:

0x00ffU

Można łączyć litery u (lub U) i l (lub L) przy pisaniu stałej całkowitej, więc

20000UL

pozwala kompilatorowi traktować stałą 20000 jako unsigned long.

Stała całkowita, po której nie następuje żadna z liter u, U, l, lub L i która jest zbyt duża, by zmieścić się w normalnej wielkości int, jest traktowana przez kompilator jako unsigned int. Jeśli jest zbyt mały, by zmieścić się w unsigned int, kompilator traktuje go jak long int. Jeśli nadal nie mieści się w long int, kompilator robi z niego unsigned long int. Jeśli się tam nie zmieści, kompilator traktuje go jako long long int, jeśli się zmieści, i jako unsigned long int w przeciwnym wypadku.

Podczas deklarowania zmiennych typu long long int, long int, short int, lub unsigned int, można pominąć słowo kluczowe int. Dlatego zmienna licznik bez znaku mogła być zadeklarowana w następujący sposób:

unsigned counter;

Można również zadeklarować zmienne typu char jako niepodpisane.

Kwalifikator signed może być użyty do wyraźnego poinformowania kompilatora, że dana zmienna jest wielkością podpisaną. Jego użycie jest przede wszystkim przed deklaracją char, a dalsze omówienie jest odłożone do rozdziału 13, „Więcej o typach danych.”

Nie przejmuj się, jeśli dyskusje o tych specyfikatorach wydają Ci się w tym momencie nieco ezoteryczne. W dalszych częściach tej książki wiele z tych różnych typów jest zilustrowanych rzeczywistymi przykładami programowymi. Rozdział 13. zawiera więcej szczegółów na temat typów danych i konwersji.

Tabela 3.1 podsumowuje podstawowe typy danych i kwalifikatory.

Tabela 3.1 Podstawowe typy danych