Serwis Edukacyjny
w I-LO w Tarnowie
obrazek

Materiały dla uczniów liceum

  Wyjście       Spis treści       Wstecz       Dalej  

obrazek

Autor artykułu: mgr Jerzy Wałaszek
Konsultacje: Wojciech Grodowski, mgr inż. Janusz Wałaszek

©2024 mgr Jerzy Wałaszek
I LO w Tarnowie

obrazek

Warsztat

Kurs języka C

Zmienne

SPIS TREŚCI
Podrozdziały

Definicja zmiennej

Prawie każdy program komputerowy przetwarza jakieś dane. Wymaga to przechowywania tych danych w pamięci komputera. Do tego celu w języku C służą zmienne (ang. variables). Przed pierwszym użyciem w programie zmienną należy odpowiednio zdefiniować. Dlaczego? Odpowiedź jest prosta, jeśli zrozumiałeś materiał z poprzedniego rozdziału. W pamięci komputera przechowywane są tylko bity. Z bitów jako takich nie wynika, jakiego rodzaju informację one przechowują. Dlatego musimy poinformować kompilator, co zamierzamy w zmiennej przechowywać, aby mógł przydzielić dla zmiennej odpowiednią ilość pamięci oraz później odpowiednio interpretować informację przechowywaną w zmiennej.

Definicja zmiennej w języku C wygląda następująco:

typ nazwa;

Typ określa rodzaj informacji przechowywanych w zmiennych. Mamy następujące typy podstawowe (typów jest więcej, zajmiemy się nimi dokładniej w dalszej części kursu):

char znak
int liczba całkowita ze znakiem w kodzie U2
unsigned int liczba całkowita bez znaku w kodzie NBC
float liczba zmiennoprzecinkowa

Nazwa określa identyfikator, za pomocą którego będziemy się do zmiennej odwoływać. Nazwy zmiennych w języku C należy budować wg następujących reguł:

Przykłady poprawnych nazw:

a, x, nazwisko, Wynik, qM65, _, _12

Przykłady niepoprawnych nazw zmiennych (zły znak zaznaczony na czerwono):

3B, Imię, _a%7, $d

Przykładowe definicje zmiennych wyglądają następująco:

int a; // Zmienna całkowita U2 o nazwie a
unsigned b; // Zmienna całkowita NBC o nazwie b
char c; // Zmienna znakowa o nazwie c
float x; // Zmienna zmiennoprzecinkowa o nazwie x

Na początek:  podrozdziału   strony 

Operacje na zmiennych

Gdy zmienna została zdefiniowana na początku funkcji w programie, to można jej użyć do przetwarzania danych. Tutaj pojawia się kolejne pojęcie: wyrażenie (ang. expression). Wyrażenie jest zapisem pewnego działania matematycznego, które daje w wyniku jakąś wartość. Najprostszym wyrażeniem jest liczba:
7
-12
3.76

Powyżej mamy liczby: całkowitą dodatnią (nadaje się do wszystkich zmiennych), całkowitą ujemną (nadaje się do zmiennych typu int i float), zmiennoprzecinkową (nadaje się do zmiennej typu float).

W wyrażeniach możemy stosować operatory matematyczne – operator jest symbolem operacji:

+ dodawanie
- odejmowanie
* mnożenie
/ dzielenie
% reszta z dzielenia

Przykłady wyrażeń z operatorami:

7 + 4
8 * -5
3.5 / 2.75

Oprócz liczb w wyrażeniach mogą pojawić się nazwy zmiennych. W takim przypadku w wyrażeniu zostanie użyta aktualna zawartość danej zmiennej:

7 + b
4 * mnożnik
x / 12.6

Wynik wyrażenia możesz umieścić w wybranej zmiennej za pomocą instrukcji przypisania (ang. assignment statement), która posiada następującą składnię:

zmienna = wyrażenie;

Znak równości, który występuje w tym poleceniu nie oznacza równości. Komputer oblicza wartość wyrażenia, a wynik umieszcza w zmiennej, której nazwę podasz po lewej stronie znaku równości.

Na przykład:

a = 10;
b = a * 15; // Do zmiennej b trafi wartość 150

Instrukcja przypisania umożliwia ci wykonywanie obliczeń. Pozostaje problem wyświetlenia wyników tych obliczeń. Do tego celu używamy funkcji printf. Dotychczas wykorzystywana była do wyświetlania tekstów. Jednak funkcja ta potrafi dużo więcej. Nazwa printf pochodzi od nazwy angielskiej print formatted data to stdout (wypisz sformatowane dane na standardowe wyjście). Jej właściwa składnia wygląda następująco:

printf(format,dana1,dana2,...);

format – tekst formatujący, który określa sposób interpretacji i prezentacji danych
dana – wyrażenie lub zmienna, których wartość zostanie zinterpretowana przez funkcję i wyświetlona zgodnie z zawartością tekstu formatującego.

Najlepiej zobrazować działanie printf odpowiednimi przykładami. Utwórz nowy projekt w CodeBlocks i skopiuj do niego poniższy program (w Linuxie zastosuj polskie litery):

/*
 Funkcja printf
 (C)2016 mgr Jerzy Wałaszek
 Data utworzenia: 14.09.2016
*/

#include <stdio.h>
#include <stdlib.h>
#include <locale.h>

int main()
{
  int a;
  char b;
  float c;

  setlocale(LC_ALL,"");

  a = 27;
  b = 65; // Kod litery A
  c = 3.141592;

  printf("Liczba dziesiętna : %d\n",136);
  printf("Dwie liczby dziesiętne : %d %d\n",524,a);
  printf("Liczba ósemkowa : %o\n",12);
  printf("Liczba dziesiętna i ósemkowa: %d %o\n",9,9);
  printf("dec oct hex : %d %o %x\n",100,100,100);
  printf("Znak i jego kod : %c %d\n",b,b);
  printf("Liczba pi : %f\n\n",c);

  return 0;
}

Wynik:

Liczba dziesiętna           : 136
Dwie liczby dziesiętne      : 524 27
Liczba ósemkowa             : 14
Liczba dziesiętna i ósemkowa: 9 11
dec oct hex                 : 100 144 64
Znak i jego kod             : A 65
Liczba pi                   : 3.141592

A teraz wyjaśniamy. Tekst formatujący w funkcji printf jest wyświetlany aż do napotkania znaku %, który posiada znaczenie specjalne (jeśli zechcesz po prostu wyświetlić znak %, to musisz użyć dwóch: %%). Za znakiem % umieszczamy kod formatu kolejnej danej, która zostanie wyświetlona w tym dokładnie miejscu. Np. %d oznacza liczbę dziesiętną, czyli kolejna dana zostanie pobrana i wypisana w postaci liczby dziesiętnej. Zwróć uwagę, że w jednym tekście formatującym może być kilka takich elementów. Np. %d %o %x powoduje pobranie trzech danych z listy argumentów funkcji i wyświetlenie pierwszej jako liczby dziesiętnej, drugiej jako liczby ósemkowej i trzeciej jako liczby szesnastkowej.

Kod Wynik Przykład
%d lub %i Liczba całkowita ze znakiem 12, -55
%u Liczba całkowita bez znaku 653
%o Liczba ósemkowa bez znaku 7255
%x Liczba szesnastkowa bez znaku z małymi literami cyfr 1fa5
%X Liczba szesnastkowa bez znaku z dużymi literami cyfr 1FA5
%f Liczba zmiennoprzecinkowa, małe e 6.57, 1.3e+2
%F Liczba zmiennoprzecinkowa, duże E 6.57, 1.3E+2
%c Znak A, c
%s Tekst Hello

Kodów formatów dla printf jest dużo więcej, jednak na razie wystarczą te, które podałem (pozostałe wprowadzimy w miarę potrzeb). Zapisz je sobie na boku, abyś miał je pod ręką.

Mamy już wszystkie podstawowe elementy, które pozwolą napisać prosty program obliczeniowy:

/*
 Zmienne i printf
 (C)2016 mgr Jerzy Wałaszek
 Data utworzenia: 14.09.2016
 Program oblicza obwód i pole koła
*/

#include <stdio.h>
#include <stdlib.h>
#include <locale.h>

int main()
{
  // Definicja zmiennych używanych w programie

  float promien,pi,obwod,pole;

  setlocale(LC_ALL,"");

  printf("Obliczanie obwodu i pola koła o zadanym promieniu\n"
         "-------------------------------------------------\n\n");

  // Zmiennym nadajemy wartości początkowe, nazywa się to inicjalizacją

  pi = 3.14159265;
  promien = 22.7;

  // Obliczenia

  obwod = 2 * pi * promien;
  pole = pi * promien * promien;

  // Wypisujemy wyniki

  printf("Promień : %f\n"
         "Obwód : %f\n"
         "Pole : %f\n\n"
         "Dziękujemy i zapraszamy ponownie\n\n", promien, obwod, pole);

  return 0;
}

Wynik:

Obliczanie obwodu i pola koła o zadanym promieniu
-------------------------------------------------

Promień : 22.700001
Obwód   : 142.628311
Pole    : 1618.831421

Dziękujemy i zapraszamy ponownie

Popracujemy teraz nad ulepszeniem tego programu. Przyjrzyj się wyświetlonym liczbom. Lepiej byłoby, aby wszystkie punkty dziesiętne znalazły się jeden pod drugim, co zwiększyłoby czytelność tych wyników. Aby to osiągnąć, musisz nieco zmodyfikować kod formatu. Pomiędzy znakami % a f dodasz szerokość pola liczby (czyli z ilu maksymalnie znaków ma się składać) oraz precyzję (czyli liczbę cyfr po przecinku). Jeśli tego nie określisz, to printf wyświetla liczby zmiennoprzecinkowe z 6 cyframi po przecinku. Powiedzmy, że nie interesuje nas taka dokładność i wystarczą nam dwie cyfry po przecinku. Natomiast część całkowita liczby ma być ograniczona do maksymalnie 5 cyfr (typ float posiada dokładność 7...8 cyfr, co widać w pierwszym wyniku, gdzie na końcu pojawiła się cyfra 1, której w programie nie wstawialiśmy). Kod formatu będzie wyglądał następująco:

%8.2f

8 – 5 cyfr całkowitych + kropka dziesiętna + 2 cyfry ułamkowe wymaga pola o szerokości 8 znaków
2 – 2 cyfry po kropce dziesiętnej

W programie zmień ostatnie printf na:

  printf("Promień : %8.2f\n"
         "Obwód : %8.2f\n"
         "Pole : %8.2f\n\n"
         "Dziękujemy i zapraszamy ponownie\n\n", promien, obwod, pole);

Wynik:

Obliczanie obwodu i pola koła o zadanym promieniu
-------------------------------------------------

Promień :    22.70
Obwód   :   142.63
Pole    :  1618.83

Dziękujemy i zapraszamy ponownie

Teraz wyniki są prezentowane w sposób bardziej czytelny.


Operacje arytmetyczne w języku C są wykonywane w wyrażeniu w następującej kolejności:

Przykład:

7 + 5 * 2 - 6 / 3 najpierw mnożenia/dzielenia
7 +   10  -    2 teraz dodawania/odejmowania
15    
/*
 Kolejność działań
 (C)2016 mgr Jerzy Wałaszek
 Data utworzenia: 14.09.2016
*/

#include <stdio.h>
#include <stdlib.h>
#include <locale.h>

int main()
{
  setlocale(LC_ALL,"");

  printf("Wynik: %d\n\n",7 + 5 * 2 - 6 / 3);
  return 0;
}

Jeśli zechcesz zmienić kolejność działań, to musisz zastosować nawiasy okrągłe. Część wyrażenia w nawiasach jest obliczana najpierw:

Przykład:

3 + (2 + 4) * (7 - 1) / 3 - 11 najpierw wyrażenia w nawiasach
3 +     6 *         6 / 3      - 11 mnożenia/dzielenia
3 + 12 - 11 dodawania/odejmowania
4    
/*
 Kolejność działań
 (C)2016 mgr Jerzy Wałaszek
 Data utworzenia: 14.09.2016
*/

#include <stdio.h>
#include <stdlib.h>
#include <locale.h>

int main()
{
  setlocale(LC_ALL,"");

  printf("Wynik: %d\n\n",3 + (2 + 4) * (7 - 1) / 3 - 11);
  return 0;
}

Przykładu zapisu wyrażeń w C:

Przy obliczaniu wyrażeń musisz zwrócić uwagę na operację dzielenia. Dzielenie przez 0 spowoduje błąd w czasie działania programu (ang. run time error).

/*
 Dzielenie przez zero
 (C)2016 mgr Jerzy Wałaszek
 Data utworzenia: 14.09.2016
*/

#include <stdio.h>
#include <stdlib.h>
#include <locale.h>

int main()
{
  int a,b;

  setlocale(LC_ALL,"");

  a = 5;
  b = 0;
  printf("Wynik: %d\n\n",a / b);
  return 0;
}

Tego typu błędy nie są wykrywane w czasie kompilacji, tzn. program jest poprawny pod względem składniowym, lecz zawiera błąd logiczny – dzielenie przez zero.

Druga własność dzielenia pojawia się wtedy, gdy oba argumenty są typu całkowitego. Wtedy wynik dzielenia również jest całkowity:

/*
 Dzielenie całkowitoliczbowe
 (C)2016 mgr Jerzy Wałaszek
 Data utworzenia: 14.09.2016
*/

#include <stdio.h>
#include <stdlib.h>
#include <locale.h>

int main()
{
  int a;
  float x;

  setlocale(LC_ALL,"");

  a = 5;
  x = a / 2;
  printf("Wynik: %f\n\n",x);
  return 0;
}

W tym programie dzielimy 5 przez 2 i wynik umieszczamy w zmiennej x. Ponieważ oba argumenty dzielenia (zmienna a oraz liczba 2) są liczbami całkowitymi, to wynik też jest liczbą całkowitą. Nie zmienia tutaj niczego fakt, iż wynik zapisujemy w zmiennej typu float. Otrzymujemy 2.000000, a powinno być 2.500000. Aby otrzymać wynik poprawny, zmień liczbę 2 na jej odpowiednik zmiennoprzecinkowy 2.0:

/*
 Dzielenie zmiennoprzecinkowe
 (C)2016 mgr Jerzy Wałaszek
 Data utworzenia: 14.09.2016
*/

#include <stdio.h>
#include <stdlib.h>
#include <locale.h>

int main()
{
  int a;
  float x;

  setlocale(LC_ALL,"");

  a = 5;
  x = a / 2.0;
  printf("Wynik: %f\n\n",x);
  return 0;
}

Jeśli jeden z argumentów dzielenia jest liczbą zmiennoprzecinkową, to wynik również będzie zmiennoprzecinkowy. Zapamiętaj dobrze tę własność dzielenia – unikniesz wielu błędów i frustracji w swoich przyszłych programach. Poniżej jest pouczający przykład. Program przelicza temperaturę w skali Celsjusza TC na temperaturę w skali Fahrenheita TF. Wzór jest następujący:

/*
 Celsjusz --> Fahrenheit
 (C)2016 mgr Jerzy Wałaszek
 Data utworzenia: 14.09.2016
*/

#include <stdio.h>
#include <stdlib.h>
#include <locale.h>

int main()
{
  int Tf,Tc;

  setlocale(LC_ALL,"");

  printf("Przeliczanie temperatury w skali Celsjusza na temperaturę w skali Fahrenheita\n"
         "-----------------------------------------------------------------------------\n\n");
  Tc = 25;
  Tf = 32 + 9 / 5 * Tc;

  printf("Temperatura w skali Celsjusza : %4d\n"
         "Temperatura w skali Fahrenheita: %4d\n\n", Tc, Tf);
  return 0;
}

Wynik:

Przeliczanie temperatury w skali Celsjusza na temperaturę w skali Fahrenheita
-----------------------------------------------------------------------------

Temperatura w skali Celsjusza  :   25
Temperatura w skali Fahrenheita:   57

Format %4d oznacza liczbę dziesiętną ze znakiem, która będzie wypisana w polu o szerokości 4 znaków z dosunięciem do prawej krawędzi tego pola.

Otrzymujemy zły wynik, ponieważ 25ºC = 77ºF, a nie 57ºF! Skąd ten błąd? Z dzielenia 9 przez 5! Ponieważ obie liczby są całkowite, to wynikiem będzie 1, ponieważ 5 mieści się 1 raz w 9 i zostaje reszta 4. Program możesz naprawić na dwa sposoby, zmieniając nieco wzór obliczeniowy:
 Tf = 32 + 9.0 / 5 * Tc;

Tutaj zmieniamy jeden z argumentów dzielenia na liczbę zmiennoprzecinkową.
 Tf = 32 + (9 * Tc) / 5;

Tutaj reorganizujemy obliczenia. Teraz 5 dzieli większą liczbę.


W języku C instrukcja przypisania posiada wartość: jest równa temu, co zostaje umieszczone w zmiennej. Dzięki tej własności możemy sobie uprościć inicjalizację zmiennych.

Zamiast:

a = 5; b = 5; c = 5;

Piszemy:
a = b = c = 5;

Wartość 5 zostanie umieszczona w zmiennych a, b i c. Działa to tak, że komputer wylicza wartość wyrażenia na końcu po prawej stronie, następnie zostaje wykonane przypisanie c = 5; czyli w zmiennej c znajdzie się liczba 5. Jednak to przypisanie jest nowym wyrażeniem w instrukcji przypisania: b = (c = 5); a jego wartością jest 5, które trafi do b. Ta instrukcja przypisania jest dalej wyrażeniem dla a = (b = (c =5)); i ma wartość wciąż 5, ponieważ tyle zostało przypisane (umieszczone w) zmiennej b. Zatem do zmiennej a również trafi 5. Taki ciąg przypisań można rozszerzać na dowolną liczbę zmiennych.

Język C potrafi być zarówno przejrzysty jak i zagmatwany. Ponieważ przypisanie posiada wartość, to można go użyć jak liczby w dowolnym wyrażeniu arytmetycznym:
a = 3 * (b = 5) + 4; // do a trafi 19, do b trafi 5

Odradzam stosowanie takich konstrukcji, chociaż mogą się wydawać kuszące.


Na początek:  podrozdziału   strony 

Modyfikacja zmiennej

Modyfikacja zmiennej polega na zmianie jej zawartości, lecz nie na zupełnie nową, lecz na taką, która wiąże się z poprzednią zawartością, np. zwiększenie o 2, pomnożenie przez 4. Każdą modyfikację możesz wykonać za pomocą instrukcji przypisania.
a = a + 1; // w a znajdzie się o 1 więcej niż było
b = b - 5; // w b będzie o 5 mniej niż było
c = c * 3; // zawartość c zostanie potrojona
d = d % 4; // w d znajdzie się reszta z dzielenia d przez 4

Ponieważ modyfikacje zmiennych są często wykonywane w programach, język C posiada specjalne operatory, które ułatwiają ich wykonywanie. Najpierw omówimy dokładnie operatory zwiększania i zmniejszania o 1:

++ zwiększa zawartość zmiennej o 1
-- zmniejsza zawartość zmiennej o 1

Operator zwiększania/zmniejszania o 1 możesz umieścić przed lub za zmienną. Działanie będzie takie samo w obu wypadkach:

/*
 Operatory ++ i --
 (C)2016 mgr Jerzy Wałaszek
 Data utworzenia: 16.09.2016
*/

#include <stdio.h>
#include <stdlib.h>
#include <locale.h>

int main()
{
  int a, b, c, d;

  setlocale(LC_ALL,"");

  a = b = c = d = 5;
  printf("Przed: a = %d, b = %d, c = %d, d = %d\n",a,b,c,d);
  ++a; b++; --c; d--;
  printf("Po : a = %d, b = %d, c = %d, d = %d\n",a,b,c,d);
  return 0;
}

Przeanalizuj powyższy program.

Jeśli operacje ++zmienna, zmienna++, --zmienna, zmienna++ występują samodzielnie, to nie ma znaczenia, czy operator ++/-- zapiszesz przed, czy za zmienną. Musisz jednak wiedzieć, że operacja modyfikacji ma wartość i może zostać użyta w dowolnym wyrażeniu arytmetycznym, z czego programiści chętnie korzystają. W takim przypadku nie jest obojętne, czy jest przed zmienną, czy za nią, ponieważ wartość operacji będzie inna:

++zmienna zwiększa zawartość zmiennej o 1, wartością jest zmienna zmodyfikowana
zmienna++ zwiększa zawartość zmiennej o 1, wartością jest zmienna niezmodyfikowana.

Poniżej mamy przykład obrazujący to zachowanie się operatora ++ (operator -- zachowuje się identycznie):

/*
 Operatory ++ i --
 (C)2016 mgr Jerzy Wałaszek
 Data utworzenia: 16.09.2016
*/

#include <stdio.h>
#include <stdlib.h>
#include <locale.h>

int main()
{
  int a,b,c,d;

  setlocale(LC_ALL,"");

  c = d = 5;
  a = c++; b = ++d;
  printf("a = %d, b = %d, c = %d, d = %d\n",a,b,c,d);
  return 0;
}

W obu przypadkach zmienne c i d zostały zwiększone o 1. Jednak do zmiennej a trafiła wartość 5, a do zmiennej b trafiła wartość 6. Stało się tak dlatego, iż wartością wyrażenia c++ jest zawartość zmiennej c przed modyfikacją, która zostanie wykonana dopiero po przypisania. Z kolei wartością wyrażenia ++d jest zmodyfikowana zmienna d, czyli zwiększona o 1. Mnemotechnicznie zapamiętasz to działanie, jeśli będziesz analizował wyrażenie od strony lewej do prawej: jeśli najpierw napotkasz zmienną, to wartością będzie zawartość zmiennej przed modyfikacją (operator ++/-- znajduje się za zmienną, więc zmienna zostanie zmodyfikowana później). Jeśli najpierw napotkasz operator, to zmienna zostanie zmodyfikowana, a następnie w wyrażeniu będzie użyta jej nowa wartość.

Nie nadużywaj operatorów ++/-- w wyrażeniach, bo łatwo uczynić je trudnymi w analizie:

a = b++ - --c * ++a;

Oprócz operatorów modyfikacji ++ i -- mamy operatory dla każdej operacji dwuargumentowej:

zmienna operacja= wyrażenie;

Jest to odpowiednik operacji:

zmienna = zmienna operacja wyrażenie;

Na przykład:

a += 7;

Odpowiada:

a = a + 7;

Podsumowując

+= zwiększa zawartość zmiennej
a += 5; // w zmiennej a o 5 więcej
-= zmniejsza zawartość zmiennej
a -= 4; // w zmiennej a o 4 mniej
*= mnoży zawartość zmiennej
a *= 3; // potraja zmienną a
/= dzieli zawartość zmiennej
a /= 2; // w zmiennej a będzie połowa a
%= reszta z dzielenia zmiennej
a %= 8; // w zmiennej a będzie reszta z dzielenia a przez 8

Poniższy program oblicza 35:

/*
 Operatory modyfikacji
 (C)2016 mgr Jerzy Wałaszek
 Data utworzenia: 16.09.2016
*/

#include <stdio.h>
#include <stdlib.h>
#include <locale.h>

int main()
{
  int p3;

  setlocale(LC_ALL,"");

  p3 = 3;  // 3 = 3^1
  p3 *= 3; // 3 * 3 = 3^2
  p3 *= 3; // 3 * 3 * 3 = 3^3
  p3 *= 3; // 3 * 3 * 3 * 3 = 3^4
  p3 *= 3; // 3 * 3 * 3 * 3 * 3 = 3^5, dosyć
  printf("3^5 = %d\n",p3);
  return 0;
}

Zapraszam do następnego rozdziału.


Na początek:  podrozdziału   strony 

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: i-lo@eduinf.waw.pl

Serwis wykorzystuje pliki cookies. Jeśli nie chcesz ich otrzymywać, zablokuj je w swojej przeglądarce.

Informacje dodatkowe.