2. Komentarze, funkcja printf(), zmienne i ich rodzaje - majsylw/Introduction-to-programming-in-C GitHub Wiki

Skoro pierwsze kroki mamy już za sobą i umiemy uruchomić samodzielnie napisany skrypt, przyszedł czas na poszerzenie wiedzy. Będzie to podstawa, na której będziemy później bazować budując dalsze, bardziej skomplikowane programy.

Komentarze

Jednak zanim poznamy masę konkretnych instrukcji, porozmawiajmy o komentarzach. Komentarz jest wydzielonym fragmentem tekstu, który jest ignorowany przez kompilator. Ma on służyć jedynie człowiekowi, po to aby kod był czytelniejszy. Zazwyczaj stosujemy koentarze, aby objaśnić mniej oczywiste partie kodu, to pewnego rodzaju notatka informująca czym jest dana zmienna, do czego konkretna funkcja służy, czy po prostu wyłączamy fragment kodu, którego aktualnie nie chcemy wykonywać.

W języku C mamy dwa podstawowe rodzaje komentarzy: jedno- oraz wielowierszowe. Te pierwsze zaczynamy znakiem //. Od tego znaku aż do końca wiersza kod nie jest pomijany na etapie kompilacji. Dodatkowo między znakiem // a komentarzem zwyczajowo zostawiamy odstęp (spację), aby podnieść czytelność programu.

#include <stdio.h>
// funkcja główna programu
int main(){
    printf("Hello world!"); // ta funkcja wypisuje napis
    return 0;
}

W przypadku komentarzy wielowierszowych korzystamy z dwóch znaków /* aby rozpocząć komentarz, oraz */ aby owy komentarz zakończyć.

/*
Tutaj następuje obszerne wyjaśnienie funkcji main(): 
Funkcja wypisuje na ekranie napis "Witaj świecie" oraz napis wpisany z klawiatury przez użytkownika.
Przyjmowane parametry: nic
Wartość zwracana: nic
*/
int main(){
    printf("Hello world!"); // ta funkcja wypisuje napis
    return 0;
}

Oczywiście możemy w ramach jednego kodu źródłowego dowolnie używać na przemian obu typów komentarzy.

Wypisywanie komunikatów - funkcja printf()

Przyjrzyjmy się jeszcze bliżej funkcji pirintf(). Jest to funkcja, czyli fragment kodu wykonujący określoną operację. W przypadku printf() jest to wypisanie komunikatu na standardowe wyjście, czyli po prostu konsolę, w której kazaliśmy wykonać nasz program. Uruchomienie funkcji nazywamy wywołaniem. W nawiasach () umieszczamy argument (może być ich kilka), który jest zmienną wykorzystywaną w wewnątrz naszej funkcji - wyszczególnionego za pomocą wcięcia bloku kodu. Jak już wiemy przykładowe wywołanie funkcji może wyglądać następująco:

printf("Hello world!");

spowoduje wypisanie napisu "Hello world!" na ekranie konsoli. Ogólny schemat wywołania funkcji printf() prezentuje się następująco: printf(łańcuchKontrolny [, dane]); Oznacza to, że funkcja printf() minimalnie przyjmuje jeden argument będący napisem (łańcuchem znaków), w który możemy wpleść dane innego typu, np. liczby czy znaki. Dla różnych typów danych taka instrukcja mogłaby wyglądać następująco:

printf("Liczba cztery numerycznie to %d", 4); // wywołanie tej instrukcji skutkuje wypisaniem na ekran napisu "Liczba cztery numerycznie to 4"
printf("Liczba cztery i pół numerycznie to %f", 4.5); // wywołanie tej instrukcji skutkuje wypisaniem na ekran napisu "Liczba cztery i pół numerycznie to 4.5"
printf("Znak z zapiszemy takze jako %c", 'z'); // wywołanie tej instrukcji skutkuje wypisaniem na ekran napisu "Znak z zapiszemy takze jako z"
printf("A napis zapiszemy tak %s", "napis"); // wywołanie tej instrukcji skutkuje wypisaniem na ekran napisu "A napis zapiszemy tak napis"

Cztery litery występujące w powyższym kodzie po znaku % to znaczniki konwersji. W celu wplecienia liczb i znaków w napis, należy użyć znaczników konwersji, odpowiadających odpowiednim typom danych.

znacznik konwersji opis
%d lub %i liczba całkowita
%f liczba zmiennoprzecinkowa
%E lub %e format naukowy (1e-3 lub 1E-3)
%G lub %g liczba w formacie f lub e
%c znak
%s łańcuch znaków

O typach danych będziemy jeszcze mówić za chwilę, na ten moment warto abyś zauważył pewną drobną różnicę. Dane znakowe (samotne znaki) umieszcza się między pojedynczymi apostrofami, podczas gdy łańczuch znaków (rozbudowane napisy) umieszcza się w cudzysłowie. Wartym poruszenia w tym miejscu tematem jest formatowanie. Jeśli będziemy wyświetlać zmienne liczbowe, możemy chcieć zaokrąglić je do odpowiedniej liczby miejsc po przecinku, czy też zapisać w notacji naukowej . W tym celu możemy skorzystać ze znacznika konwersji ze znakiem ., np:

float liczba = 1.23456;
printf("Twoja liczba to %f", liczba); // niesformatowane wyświetlenie liczby zmiennoprzecinkowej
printf("Twoja liczba to %.2f", liczba); // sformatowane wyświetlenie liczby zmiennoprzecinkowej - zaokrąglenie liczby typu float (f) do 2 miejsc po przecinku
printf("Twoja liczba to %12.2f", liczba); // oprócz ograniczenia się do dwóch liczb po przecinku, mozemy ustaliś szerokość pola, w obrębie którego tekst jest wyrównywany do prawej -> tutaj jest to 12 znaków, lajlepiej samodzielnie sprawdź to formułując kod własnego programu

Znakami najczęściej wykorzystywanymi do oddzielenia od siebie napisów są tzw. znaki sterujące, tu poznamy pięć podstawowych znaków sterujących: \n, \t, ', ", \.

#include <stdio.h>
/* Program pokazuje wykorzystanie znaków sterujących \n, \t, \', \", \\ */
int main(){
    printf("To jest napis"); //Funkcja printf() wyświetli napis
    
    // Znak sterujący \n powoduje przeniesienie kursora do nowej linii
    printf("\nTo\njest\nnapis\n");

    // Znak sterujący \t powoduje przejście do następnego położenia tabulatora
    printf("To\tjest\tnapis\n");
    // Może to być pomocne przy formatowaniu nagłówka tabeli
    printf("pn\twt\tśr\tcz\tpt\n");

    // Znaki specjalne \' oraz \" pozwalają uzyskać apostrof i cudzysłów
    printf("Apostrof: \' \n");
    printf("Cudzysłów: \" \n");

    // Skoro znaki sterujące wykorzystują ukośnik
    // To jak uzyskać ukośnik?
    printf("Ukosnik: \\\n");
    return 0;
}

Zmienne

W naszych programach często będziemy obliczać jakieś wartości lub wpisywać je za pomocą klawiatury, czyli wczytywać od użytkownika. Jednak samo obliczenie wartości to nie wszystko. W pamięci komputera musimy wydzielić specjalne miejse, aby te wartości zapisać i następnie wielokrotnie wykorzystywać. Aby to zrobić utworzymy zmienną. Zmienną możesz sobie wyobrazić jak pewnego rodzaju pudełeczko z etykietką (nazwą). To właśnie za pomocą tej nazwy będziemy się odwoływać do zmiennej w trakcie pisania programu. Ważnym jest, żeby rozróżniać dwie rzeczy: nazwę zmiennej (etykietę pudełka) i jej wartość (wnętrze pudełka). Pamiętajmy, że zmienna, jak sama nazwa wskazuje, może zmieniać swoją wartość w czasie trwania programu. W C istnieje kilka typów danych, takich jak liczby całkowite, zmiennoprzecinkowe (czyli rzeczywiste) itp. Przy tworzeniu zmiennej należy określić typ danych jaki będzie w niej przechowywany - jest to deklaracja zmiennej. W tym procesie po prostu wybieramy typ danych i nadajemy nazwę zmiennej. W kolejnym kroku możemy zmiennej przypisać jej konkretną wartość - jest to inicjalizacja zmiennej.

// tworzymy zmienną typu int o nazwie a i przechowujemy w niej wartość 7
int a = 7;
printf("%d", a); // 7 <- to jest odpowiedź programu
// teraz wsadzimy do pudeleczka inna wartość
a = 8; 
printf("%d", a); // 8 <- to jest nowa wartość zmiennej a

Styl nazewnictwa zmiennych.

To w jaki sposób będziemy nazywać nasze zmienne będzie wpływało na czytelność kodu a także nasze 'odnajdowanie się' w nim (np. czy łatwo będzie zmodyfikować kod w przyszłości, gdy zapomnimy, co dokładnie w nim napisaliśmy lub gdy ktoś inny będzie pracował na naszym kodzie). Dobrą praktyką jest nazywanie zmiennych zgodnie z ich przeznaczeniem, np. jeśli w zmiennej chcemy przechowywać nazwisko nazwijmy ją nazwisko, zgodnie z jej przeznaczeniem. Dodatkowo warto pamiętać o kilku zasadach:

  • na nazwę zmiennej mogą składać się małe i wielkie litery od a do z, znak podkreślenia _, a także cyfry od 0 do 9, przy czym nazwa zmiennej nie może się od cyfry zaczynać (może za to od litery i znaku podkreślenia),
  • język C rozróżnia wielkość liter, co znaczy tyle, że litera 'A' i 'a' to nie jest dla niego to samo,
  • nazwa zmiennej może mieć od 1 do 31 znaków,
  • nie możemy w nazwie zmiennej używać białych znaków, zamiast tego przyjeło się korzystać z _ lub tak zwanej czcionki kamelowej czyli stosowania dużych liter np. wiek_studenta lub WiekStudenta - warto trzymać się jednej konwencji.

Rodzaje danych

Każda zmienna, albo, mówiąc ogólniej, każda wartość w programie, ma swój typ. Możemy wyzczególnić liczby całkowite, zmiennoprzecinkowe, zespolone, napisy, czy też wartości logiczne. Jednak, jak już zostało wspomniane na początku tej lekcji, w języku Python występuje typowanie dynamiczne. Oznacza to, że konkretny identyfikator, konkretna zmienna, np. a, może raz przechowywać napis Ala a za chwilię liczbę całkowitą 4. Do sprawdzania typu danych służy funkcja type().

I tak najważniejszymi typami w języku C są:

// int lub integer, liczba całkowita
// w języku C liczba całkowita przyjmuje także wartość logiczną - 0 oznacza nieprawdę
int calkowita = -11;
// float i double, czyli zmienna zmiennoprzecinkowa/liczba rzeczywista - w C separatorem dziesiętnym jest .
float zmiennoprzecinkowa = -9.4
// char, znak
char znak = 'c';
// łańcuch znaków, czyli napis - napis poznajemy głównie po tym, że jest zapisany w cudzysłowie ("):
char napis[] = "ala ma kota";

Ponownie zwrócę uwagę na różnicę w deklarowaniu znaków, a łańcuchów znakowych.

char znak = 'c';
char slowo[] = "c";

Zmienna znak to pojedyncza litera c, natomiast zmienna slowo to napis - 2 znaki litera c oraz znak specjalny null \0 oznaczający koniec zmiennej łańcuchowej. Znak końca łańcucha '\0' nie jest znakiem '0'. Program wykorzystuje znak '\0' do rozpoznawania końca łańcucha - długość łańcucha to liczba znaków do znaku końca łańcucha (wliczając spacje i inne białe znaki, czyli innego rodzaju przerwy, np. tabulacje). Do przechowywania łańcuchów w pamięci służą tablice znaków. Tablica danych danego typu to ciąg zmiennych w pamięci, deklarujemy ją poprzez instrukcję: typ nazwa[rozmiar];. Zmienna rozmiar musi być typu całkowitoliczbowego i wynosi tyle ile długość najdłuższego łańcucha, jaki chcemy przechować w tablicy nazwa + 1, gdyż potrzebujemy dodatkowego miejsca na znak końca łańcucha \0. Tablicę możemy zainicjalizować w miejscu jej deklaracji, wtedy nie musimy podawać rozmiaru tablicy, ale jednocześnie w przyszłości nie będziemy mogli wpisać w tą tablicę dłuższego łańcucha niż rozmiar napisu, który zainicjalizował tablicę.

char miesiac[8] = "styczen";
char miesiac[] = "styczen";
//więcej miejsca na dłuższe łańcuchy
char miesiac[25] = "styczen";

Do tablicy nie można przypisać łańcucha bezpośrednio operatorem przypisania, możemy to zrobić literka po literce (odliczanie następuje od 0) lub korzystając z funkcji z biblioteki , którą musimy załączyć na początku programu #include <string.h>.

char miesiac[6]; // sama deklaracja zmiennej miesiac - musimy podać rozmiar tablicy
// miesiac = "lipiec"; /*źle*/
// Można przypisać litera po literze
miesiac[0] = 'l';
miesiac[1] = 'i';
miesiac[2] = 'p';
miesiac[3] = 'i';
miesiac[4] = 'e';
miesiac[5] = 'c';
miesiac[6] = '\0';
// lub za pomocą funkcji strcpy()
strcpy(miesiac,"lipiec");

Konwersje między różnymi typami

Często zachodzi potrzeba zamiany typów danych. Do tego służą jednoargumentowe operatory rzutowania, które nazywają się dokładnie tak, jak wymienione wcześniej typy. Przyjrzyjmy się prostej konwersji z float na int:

float d = 3.14;
int pi = (int)d;         // jawna konwersja z float do int
int i = 42.7;            // konwersja z float do int - pozbywamy się częsci dziesiętnej
float f = i;             // konwersja z int do float - dodajemy do liczby 0.0

Operacji rzutowania będziemy dokonywać jeszcze korzystając z operatorów matematycznych, już na kolejnych lekcjach.

⚠️ **GitHub.com Fallback** ⚠️