C/Podstawowe procedury wejścia i wyjścia: Różnice pomiędzy wersjami

Z Wikibooks, biblioteki wolnych podręczników.
< C
Usunięta treść Dodana treść
Kj (dyskusja | edycje)
m popr. nawigacji
Mina86 (dyskusja | edycje)
sporo tego ;)
Linia 10: Linia 10:
żeby było jasne, że chodzi o funkcję, a nie o coś innego.
żeby było jasne, że chodzi o funkcję, a nie o coś innego.


Wyżej wymienione funkcje to jedne z najczęściej używanych funkcji w C - pierwsza służy do wypisywania danych na ekran, natomiast druga do wczytywania danych z klawiatury.
Wyżej wymienione funkcje to jedne z najczęściej używanych funkcji w C - pierwsza służy do wypisywania danych na ekran, natomiast druga do wczytywania danych z klawiatury<ref>W zasadzie standard C nie definiuje czegoś takiego jak ekran i klawiatura - mowa w nim o ''standardowym wyjściu'' i ''standardowym wejściu''. Zazwyczaj jest to właśnie ekran i klawiatura, ale nie zawsze. W szczególności użytkownicy Linuksa lub innych systemów uniksowych mogą być przyzwyczajeniu do przekierowania wejścia/wyjścia z/do pliku czy łączenie komend w potoki (ang. pipe). W takich sytuacjach dane nie są wyświetlane na ekranie, a odczytywane z klawiatury.</ref>.


== Podstawowe funkcje wejścia/wyjścia z biblioteki stdio ==
== Funkcje wyjścia ==


=== Funkcja printf ===
W C jest wiele różnych funkcji umożliwiających komunikację programu z użytkownikiem. Jedna z najprostszych, a zarazem najbardziej wszechstronnych funkcji wyjścia została użyta w [[C/Pierwszy_program|przykładzie "Hello World!"]]; na początku kodu w tym przykładzie użyliśmy standardowej biblioteki '''[[Programowanie:C:Biblioteka standardowa:Indeks tematyczny#stdio.h|stdio.h]]'''. Poniżej omówienie funkcji wejścia/wyjścia zawartych w tej bibliotece.


W [[C/Pierwszy_program|przykładzie "Hello World!"]] użyliśmy już jednej z dostępnych funkcji wyjścia, a mianowicie funkcji printf(). Z punktu widzenia swoich możliwości jest to jedna z bardziej skomplikowanych funkcji, a jednocześnie jest jedną z najczęściej używanych. Przyjrzyjmy się ponownie kodowi programu "Hello, World!".
=== Funkcja wyjścia printf() ===

Jest to najczęściej używana funkcja wyjścia. Przyjrzyjmy się ponownie kodowi programu "Hello, World!".


#include <stdio.h>
#include <stdio.h>
Linia 32: Linia 30:
Hello world!
Hello world!


Ten tekst na ekranie wyświetliła właśnie funkcja <tt>printf()</tt>. Funkcja wykonuje pewne zadanie, jednak używając funkcji nie musimy wiedzieć, jak ona działa, "co ma w środku" - nas interesuje tylko efekt jej działania, oraz jak tej funkcji używać (funkcje możemy również sami napisać, ale o tym w dalszej części podręcznika).


W naszym przykładowym programie, chcąc by funkcja <tt>printf()</tt> wypisała tekst na ekranie, umieściliśmy go w cudzysłowach wewnątrz nawiasów po funkcji printf(). Ogólnie, wywołanie funkcji <tt>printf()</tt> wygląda następująco:
W naszym przykładowym programie, chcąc by funkcja <tt>printf()</tt> wypisała tekst na ekranie, umieściliśmy go w cudzysłowach wewnątrz nawiasów. Ogólnie, wywołanie funkcji <tt>printf()</tt> wygląda następująco:
printf(format, argument1, argument2, ...);
printf(format, argument1, argument2, ...);
Przykładowo:
Przykładowo:
int i=500;
int i = 500;
printf("Liczbami calkowitymi sa na przykład %i oraz %i.\n", 1, i);
printf("Liczbami całkowitymi na przykład %i oraz %i.\n", 1, i);
wypisze
wypisze
Liczbami calkowitymi sa na przykład 1 oraz 500.
Liczbami całkowitymi na przykład 1 oraz 500.
''Format'' to napis ujęty w cudzysłowy, który określa ogólny kształt, schemat tego, co ma być wyświetlone. Format jest drukowany tak, jak go napiszemy, jednak niektóre znaki specjalne zostaną w nim podmienione na co innego. Przykładowo, znak specjalny <tt>\n</tt> jest zamieniany na znak nowej linii. Natomiast procent jest podmieniany na jeden z argumentów. Po procencie następuje specyfikacja, jak wyświetlić dany argument. W tym przykładzie <tt>%i</tt> (od '''i'''nt) oznacza, że argument ma być wyświetlony jak liczba całkowita. W związku z tym, że <tt>\</tt> i <tt>%</tt> mają specjalne znaczenie, aby wydrukować je, należy użyć ich podwójnie:
''Format'' to napis ujęty w cudzysłowy, który określa ogólny kształt, schemat tego, co ma być wyświetlone. Format jest drukowany tak, jak go napiszemy, jednak niektóre znaki specjalne zostaną w nim podmienione na co innego. Przykładowo, znak specjalny <tt>\n</tt> jest zamieniany na znak nowej linii <ref>Zmiana ta następuje w momencie kompilacji programu i dotyczy wszystkich literałów napisowych. Nie jest to jakaś szczególna własność funkcji printf(). Więcej o tego typu sekwencjach i ciągach znaków w szczególności opisane jest w rozdziale [[C/Napisy|Napisy]].</ref>. Natomiast procent jest podmieniany na jeden z argumentów. Po procencie następuje specyfikacja, jak wyświetlić dany argument. W tym przykładzie <tt>%i</tt> (od '''i'''nt) oznacza, że argument ma być wyświetlony jak liczba całkowita. W związku z tym, że <tt>\</tt> i <tt>%</tt> mają specjalne znaczenie, aby wydrukować je, należy użyć ich podwójnie:
printf("Procent: %% Backslash: \\");
printf("Procent: %% Backslash: \\");
drukuje:
drukuje:
Procent: % Backslash \
Procent: % Backslash \
(bez przejścia do nowej linii). Na liście argumentów możemy mieszać ze sobą zmienne różnych typów, liczby, napisy itp. w dowolnej ilości. Funkcja printf przyjmie ich tyle, ile tylko napiszemy. Należy uważać, by nie pomylić się w formatowaniu:
(bez przejścia do nowej linii). Na liście argumentów możemy mieszać ze sobą zmienne różnych typów, liczby, napisy itp. w dowolnej liczbie. Funkcja printf przyjmie ich tyle, ile tylko napiszemy. Należy uważać, by nie pomylić się w formatowaniu:
int i=5;
int i = 5;
printf("%i %s %i", 5, 4, "napis"); /* powinno być: "%i %i %s" */
printf("%i %s %i", 5, 4, "napis"); /* powinno być: "%i %i %s" */
Przy włączeniu ostrzeżeń (opcja <tt>-Wall</tt> w [[C/Używanie kompilatora#GCC|GCC]]) kompilator powinien nas ostrzec, gdy format nie odpowiada podanym elementom.
Przy włączeniu ostrzeżeń (opcja <tt>-Wall</tt> lub <tt>-Wformat</tt> w [[C/Używanie kompilatora#GCC|GCC]]) kompilator powinien nas ostrzec, gdy format nie odpowiada podanym elementom.


Najczęstrze użycie <tt>printf()</tt>:
Najczęstsze użycie printf():
* <tt>printf("%i", i);</tt> gdy <tt>i</tt> jest typu <tt>int</tt>; zamiast <tt>%i</tt> można użyć <tt>%d</tt>
* <tt>printf("%i", i);</tt> gdy <tt>i</tt> jest typu <tt>int</tt>; zamiast <tt>%i</tt> można użyć <tt>%d</tt>
* <tt>printf("%f", i);</tt> gdy <tt>i</tt> jest typu <tt>float</tt> lub <tt>double</tt>
* <tt>printf("%f", i);</tt> gdy <tt>i</tt> jest typu <tt>float</tt> lub <tt>double</tt>
* <tt>printf("%c", i);</tt> gdy <tt>i</tt> jest typu <tt>char</tt> (i chcemy wydrukować znak)
* <tt>printf("%c", i);</tt> gdy <tt>i</tt> jest typu <tt>char</tt> (i chcemy wydrukować znak)
* <tt>printf("%s", i);</tt> gdy <tt>i</tt> jest napisem (typu <tt>char*</tt>)
* <tt>printf("%s", i);</tt> gdy <tt>i</tt> jest napisem (typu <tt>char*</tt>)

Ponieważ funkcja printf() nie jest żadną specjalną konstrukcją języka i łańcuch formatujący może być podany jako zmienna. W związku z tym możliwa jest np. taka konstrukcja:

#include <stdio.h>
int main(void)
{
char buf[100];
scanf("%99s", buf); /* funkcja wczytuje tekst do tablicy buf */
printf(buf);
return 0;
}

Program wczytuje tekst, a następnie wypisuje go. Jednak ponieważ znak procentu jest traktowany w specjalny sposób, toteż jeżeli na wejściu pojawi się ciąg znaków zawierający ten znak mogą się stać różne dziwne rzeczy. Między innymi z tego powodu w takich sytuacjach lepiej używać funkcji puts() lub fputs() opisanych niżej lub wywołania: <code>printf("%s", zmienna);</code>.


[[C/printf|Więcej o funkcji printf()]]
[[C/printf|Więcej o funkcji printf()]]


=== Funkcja wejścia scanf() ===
== Funkcja puts ==

Funkcja puts() przyjmuje jako swój argument ciąg znaków, który następnie bezmyślnie wypisuje na ekran kończąc go znakiem przejścia do nowej linii. W ten sposób, nasz pierwszy program mogli byśmy napisać w ten sposób:

#include <stdio.h>
int main(void)
{
puts("Hello world!");
return 0;
}

W swoim działaniu funkcja ta jest w zasadzie identyczna do wywołania: <code>printf("%s\n", argument);</code> jednak prawdopodobnie będzie działać szybciej. Jedynym jej mankamentem może być fakt, że zawsze na końcu podawany jest znak przejścia do nowej linii. Jeżeli jest to efekt niepożądany (nie zawsze tak jest) należy skorzystać z funkcji fputs() opisanej niżej lub wywołania <tt>printf("%s", argument);</tt>.

[[C/puts|Więcej o funkcji puts()]]

== Funkcja fputs ==

Opisując funkcję fputs() wybiegamy już trochę w przyszłość (a konkretnie do opisu [[C/Czytanie_i_pisanie_do_plików|operacji na plikach]]), ale warto o niej wspomnieć już teraz, gdyż umożliwia ona wypisanie swojego argumentu bez wypisania na końcu znaku przejścia do nowej linii:

#include <stdio.h>
int main(void)
{
fputs("Hello world!\n", stdout);
return 0;
}

W chwili obecnej możesz się nie przejmować tym zagadkowym stdout wpisanym jako drugi argument funkcji. Jest to określenie strumienia wyjściowego (w naszym wypadku standardowe wyjście - '''st''an'''d'''ard '''out'''put).

[[C/fputs|Więcej o funkcji fputs()]]

=== Funkcja putchar ===

Funkcja putchar() służy do wypisywania pojedynczych znaków. Przykładowo jeżeli chcielibyśmy napisać program wypisujący w prostej tabelce wszystkie liczby od 0 do 99 moglibyśmy to zrobić tak:

#include <stdio.h>
int main(void) {
int i = 0;
for (; i<100; ++i) {
/* Nie jest to pierwsza liczba w wierszu */
if (i % 10) {
putchar(' ');
}
printf("%2d", i);
/* Jest to ostatnia liczba w wierszu */
if ((i % 10)==9) {
putchar('\n');
}
}
return 0;
}

[[C/putchar|Więcej o funkcji putchar()]]

== Funkcje wejścia ==

=== Funkcja scanf() ===
Teraz pomyślmy o sytuacji odwrotnej. Tym razem to użytkownik musi powiedzieć coś programowi. W poniższym przykładzie program podaje kwadrat liczby, podanej przez użytkownika:
Teraz pomyślmy o sytuacji odwrotnej. Tym razem to użytkownik musi powiedzieć coś programowi. W poniższym przykładzie program podaje kwadrat liczby, podanej przez użytkownika:


Linia 72: Linia 141:
}
}


Zauważyłeś, że w tej funkcji przy zmiennej pojawił się nowy operator - '''&'''. Jest on ważny, gdyż bez niego funkcja <tt>scanf()</tt> nie skopiuje odczytanej wartości liczby do odpowiedniej zmiennej! Właściwie oznacza przekazanie do funkcji adresu zmiennej, by funkcja mogła zmienić jej wartość. Nie musisz teraz rozumieć, jak to się odbywa, wszystko zostanie wyjaśnione w rozdziale [[C/Wskaźniki|Wskaźniki]].
Zauważyłeś, że w tej funkcji przy zmiennej pojawił się nowy operator - '''&''' (etka). Jest on ważny, gdyż bez niego funkcja scanf() nie skopiuje odczytanej wartości liczby do odpowiedniej zmiennej! Właściwie oznacza przekazanie do funkcji adresu zmiennej, by funkcja mogła zmienić jej wartość. Nie musisz teraz rozumieć, jak to się odbywa, wszystko zostanie wyjaśnione w rozdziale [[C/Wskaźniki|Wskaźniki]].


Oznaczenia są takie same jak przy <tt>printf()</tt>, czyli <tt>scanf("%i", &liczba);</tt> wczytuje liczbę typu <tt>int</tt> a <tt>scanf("%f", &liczba);</tt> &ndash; liczbę typu <tt>float</tt>.
Oznaczenia są podobne takie jak przy printf(), czyli <tt>scanf("%i", &liczba);</tt> wczytuje liczbę typu <tt>int</tt>, <tt>scanf("%f", &liczba);</tt> &ndash; liczbę typu <tt>float</tt>, a <tt>scanf("%s", tablica_znaków);</tt> ciąg znaków. Ale czemu w tym ostatnim przypadku nie ma etki? Otóż, gdy podajemy jako argument do funkcji wyrażenie typu tablicowego zamieniane jest ono automatycznie na adres pierwszego elementu tablicy. Będzie to dokładniej opisane w rozdziale poświęconym [[C/Wskaźniki|wskaźnikom]].


{{Uwaga|Brak etki jest częstym błędem szczególnie wśród początkujących programistów. Ponieważ funkcja scanf() akceptuje zmienną liczbę argumentów to nawet kompilator może mieć kłopoty z wychwyceniem takich błędów (konkretnie chodzi o to, że standard nie wymaga od kompilatora wykrywania takich pomyłek), choć kompilator GCC radzi sobie z tym jeżeli podamy mu argument <tt>-Wformat</tt>.}}
[[C/scanf|Więcej o funkcji scanf()]]


Należy jednak uważać na to ostatnie użycie. Rozważmy na przykład poniższy kod:
=== Funkcja puts() ===
Inną funkcją wyjścia jest funkcja <tt>puts()</tt>. W przeciwieństwie do <tt>printf()</tt> nie formatuje ona wyświetlanego tekstu, przez co jest szybsza. Jej użycie jest bardzo proste:


#include <stdio.h>
#include <stdio.h>
int main ()
int main(void)
{
{
char tablica[100]; /* 1 */
puts("Hello world!");
scanf("%s", tablica); /* 2 */
return 0;
return 0;
}
}


Robi on niewiele. W linijce 1 deklarujemy [[C/Tablice|tablicę]] 100 znaków czyli mogącą przechować [[C/Napisy|napis]] długości 99 znaków. Nie przejmuj się jeżeli nie do końca to wszystko rozumiesz - pojęcia takie jak tablica czy ciąg znaków staną się dla Ciebie jasne w miarę czytania kolejnych rozdziałów. W linijce 2 wywołujemy funkcję scanf(), która odczytuje tekst ze standardowego wejścia. Nie zna ona jednak rozmiaru tablicy i nie wie ile znaków może ona przechować przez co będzie czytać tyle znaków, aż napotka biały znak (format %s nakazuje czytanie pojedynczego słowa), co może doprowadzić do przepełnienia bufora. Niebezpieczne skutki czegoś takiego opisane są w rozdziale poświęconym [[C/Napisy|napisom]]. Na chwilę obecną musisz zapamiętać, żeby zaraz po znaku procentu podawać maksymalną liczbę znaków, które może przechować bufor, czyli liczbę o jeden mniejszą, niż rozmiar tablicy. Bezpieczna wersją powyższego kodu jest:
W przeciwieństwie do funkcji <tt>printf()</tt> funkcja <tt>puts()</tt> powoduje automatyczne przesunięcie kursora do następnego wiersza, dlatego też na końcu napisu nie musimy umieszczać znaku '\n'.
[[C/puts|Więcej o funkcji puts()]]


#include <stdio.h>
=== Funkcja wejścia getchar() ===
int main(void)
{
char tablica[100];
scanf("%99s", tablica);
return 0;
}


{{TODO|Opisać zwracaną wartość}}
Jest to bardzo prosta funkcja, wczytująca 1 znak z klawiatury. W systemie Linux z biblioteką GNU Libc po wpisaniu danego znaku należy nacisnąć klawisz enter. W przypadku wpisania kilku znaków do zmiennej przypisany zostanie pierwszy znak, wpisany przez użytkownika.

[[C/scanf|Więcej o funkcji scanf()]]

=== Funkcja gets ===

Funkcja gets służy do wczytania pojedynczej linii. Może Ci się to wydać dziwne, ale: funkcji tej '''nie należy używać pod żadnym pozorem'''. Przyjmuje ona jeden argument - adres pierwszego elementu tablicy, do którego należy zapisać odczytaną linię - i nic poza tym. Z tego powodu nie ma żadnej możliwości przekazania do tej funkcji rozmiaru bufora podanego jako argument. Podobnie jak w przypadku scanf() może to doprowadzić do przepełnienia bufora, co może mieć tragiczne skutki. Zamiast tej funkcji należy używać funkcji fgets().

[[C/gets|Więcej o funkcji gets()]]

=== Funkcja fgets ===

Funkcja fgets() jest bezpieczną wersją funkcji gets(), która dodatkowo może operować na dowolnych strumieniach wejściowych. Jej użycie jest następujące:

fgets(tablica_znaków, rozmiar_tablicy_znaków, stdin);

Na chwilę obecną nie musisz się przejmować ostatnim argumentem (jest to określenie strumienia, w naszym przypadku standardowe wejście - '''st'''an'''d'''ard '''in'''put). Funkcja czyta tekst aż do napotkania znaku przejścia do nowej linii, który także zapisuje w wynikowej tablicy (funkcja gets() tego nie robi). Jeżeli brakuje miejsca w tablicy to funkcja przerywa czytanie, w ten sposób, aby sprawdzić czy została wczytana cała linia czy tylko jej część należy sprawdzić czy ostatnim znakiem nie jest znak przejścia do nowej linii. Jeżeli nastąpił jakiś błąd lub na wejściu nie ma już danych funkcja zwraca wartość NULL.

#include <stdio.h>
int main(void) {
char buffer[128], whole_line = 1, *ch;
while (fgets(buffer, sizeof buffer, stdin)) { /* 1 */
if (whole_line) { /* 2 */
putchar('>');
if (buffer[0]!='>') {
putchar(' ');
}
}
fputs(buffer, stdout); /* 3 */
for (ch = buffer; *ch && *ch!='\n'; ++ch); /* 4 */
whole_line = *ch == '\n';
}
if (!whole_line) {
putchar('\n');
}
return 0;
}

Powyższy kod wczytuje dane ze standardowego wejścia - linia po linii - i dodaje na początku każdej linii znak większości, po którym dodaje spację jeżeli pierwszym znakiem na linii nie jest znak większości. W linijce 1 następuje odczytywanie linii. Jeżeli nie ma już więcej danych lub nastąpił błąd wejścia funkcja zwraca wartość NULL, która ma logiczną wartość 0 i wówczas pętla kończy działanie. W przeciwnym wypadku funkcja zwraca po prostu pierwszy argument, który ma wartość logiczną 1. W linijce 2 sprawdzamy, czy poprzednie wywołanie funkcji wczytało całą linię, czy tylko jej część - jeżeli całą to teraz jesteśmy na początku linii i należy dodać znak większości. W linii 3 najzwyczajniej w świecie wypisujemy linię. W linii 4 przeszukujemy tablicę znak po znaku, aż do momentu, gdy znajdziemy znak o kodzie 0 kończącym [[C/Napisy|ciąg znaków]] albo znak przejścia do nowej linii. Ten drugi przypadek oznacza, że funkcja fgets() wczytała całą linię.

[[C/fgets|Więcej o funkcji fgets()]]

=== Funkcja getchar() ===

Jest to bardzo prosta funkcja, wczytująca 1 znak z klawiatury. W wielu przypadkach dane mogą być buforowane przez co wysyłane są do programu dopiero, gdy bufor zostaje przepełniony lub na wejściu jest znak przejścia do nowej linii. Z tego powodu wpisaniu danego należy nacisnąć klawisz enter, aczkolwiek trzeba pamiętać, że w następnym wywołaniu zostanie zwrócony znak przejścia do nowej linii. Gdy nastąpił błąd lub nie ma już więcej danych funkcja zwraca wartość EOF (która ma jednak wartość logiczną 1 toteż zwykła pętla <code>while (getchar())</code> nie da oczekiwanego rezultatu):


#include <stdio.h>
#include <stdio.h>
Linia 100: Linia 220:
int main(void)
int main(void)
{
{
char c;
int c;
c = getchar();
while ((c = getchar())!=EOF) {
if (c==' ') {
printf("Wpisałeś znak: %c!\n", c);
c = '_';
}
putchar(c);
}
return 0;
return 0;
}
}

Ten prosty program wczytuje dane znak po znaku i zamienia wszystkie spacje na znaki podkreślenia. Może wydać się dziwne, że zmienną c zdefiniowaliśmy jako trzymającą typ int, a nie char. Właśnie taki typ (tj. int) zwraca funkcja getchar() i jest to konieczne ponieważ wartość EOF wykracza poza zakres wartości typu char (gdyby tak nie było to nie byłoby możliwości rozróżnienia wartości EOF od poprawnie wczytanego znaku).


[[C/getchar|Więcej o funkcji getchar()]]
[[C/getchar|Więcej o funkcji getchar()]]
{{przypisy}}
[[en:C Programming/Simple_Input_and_Output]]
[[en:C Programming/Simple_Input_and_Output]]
<noinclude>
<noinclude>

Wersja z 16:38, 14 sty 2007

Wejście/wyjście

Komputer byłby całkowicie bezużyteczny, gdyby użytkownik nie mógł się z nim porozumieć (tj. wprowadzić danych lub otrzymać wyników pracy programu). Programy komputerowe służą w największym uproszczeniu do obróbki danych - więc muszą te dane jakoś od nas otrzymać, przetworzyć i przekazać nam wynik.

Takie wczytywanie i "wyrzucanie" danych w terminologii komputerowej nazywamy wejściem (input) i wyjściem (output). Bardzo często mówi się o wejściu i wyjściu danych łącznie - input/output, albo po prostu I/O.

W C do komunikacji z użytkownikiem służą odpowiednie funkcje. Zresztą, do wielu zadań w C służą funkcje. Używając funkcji, nie musimy wiedzieć, w jaki sposób komputer wykonuje jakieś zadanie, interesuje nas tylko to, co ta funkcja robi. Funkcje niejako "wykonują za nas część pracy", ponieważ nie musimy pisać być może dziesiątek linijek kodu, żeby np. wypisać tekst na ekranie (wbrew pozorom - kod funkcji wyświetlającej tekst na ekranie jest dość skomplikowany). Jeszcze taka uwaga - gdy piszemy o jakiejś funkcji, zazwyczaj podając jej nazwę dopisujemy na końcu nawias:

printf()
scanf()

żeby było jasne, że chodzi o funkcję, a nie o coś innego.

Wyżej wymienione funkcje to jedne z najczęściej używanych funkcji w C - pierwsza służy do wypisywania danych na ekran, natomiast druga do wczytywania danych z klawiatury[1].

Funkcje wyjścia

Funkcja printf

W przykładzie "Hello World!" użyliśmy już jednej z dostępnych funkcji wyjścia, a mianowicie funkcji printf(). Z punktu widzenia swoich możliwości jest to jedna z bardziej skomplikowanych funkcji, a jednocześnie jest jedną z najczęściej używanych. Przyjrzyjmy się ponownie kodowi programu "Hello, World!".

#include <stdio.h>

int main(void)
{
  printf("Hello world!\n");
  return 0;
}

Po skompilowaniu i uruchomieniu, program wypisze na ekranie:

Hello world!


W naszym przykładowym programie, chcąc by funkcja printf() wypisała tekst na ekranie, umieściliśmy go w cudzysłowach wewnątrz nawiasów. Ogólnie, wywołanie funkcji printf() wygląda następująco:

printf(format, argument1, argument2, ...);

Przykładowo:

int i = 500;
printf("Liczbami całkowitymi są na przykład %i oraz %i.\n", 1, i);

wypisze

Liczbami całkowitymi są na przykład 1 oraz 500.

Format to napis ujęty w cudzysłowy, który określa ogólny kształt, schemat tego, co ma być wyświetlone. Format jest drukowany tak, jak go napiszemy, jednak niektóre znaki specjalne zostaną w nim podmienione na co innego. Przykładowo, znak specjalny \n jest zamieniany na znak nowej linii [2]. Natomiast procent jest podmieniany na jeden z argumentów. Po procencie następuje specyfikacja, jak wyświetlić dany argument. W tym przykładzie %i (od int) oznacza, że argument ma być wyświetlony jak liczba całkowita. W związku z tym, że \ i % mają specjalne znaczenie, aby wydrukować je, należy użyć ich podwójnie:

printf("Procent: %% Backslash: \\");

drukuje:

Procent: % Backslash \

(bez przejścia do nowej linii). Na liście argumentów możemy mieszać ze sobą zmienne różnych typów, liczby, napisy itp. w dowolnej liczbie. Funkcja printf przyjmie ich tyle, ile tylko napiszemy. Należy uważać, by nie pomylić się w formatowaniu:

int i = 5;
printf("%i %s %i", 5, 4, "napis"); /* powinno być: "%i %i %s" */

Przy włączeniu ostrzeżeń (opcja -Wall lub -Wformat w GCC) kompilator powinien nas ostrzec, gdy format nie odpowiada podanym elementom.

Najczęstsze użycie printf():

  • printf("%i", i); gdy i jest typu int; zamiast %i można użyć %d
  • printf("%f", i); gdy i jest typu float lub double
  • printf("%c", i); gdy i jest typu char (i chcemy wydrukować znak)
  • printf("%s", i); gdy i jest napisem (typu char*)

Ponieważ funkcja printf() nie jest żadną specjalną konstrukcją języka i łańcuch formatujący może być podany jako zmienna. W związku z tym możliwa jest np. taka konstrukcja:

#include <stdio.h>

int main(void)
{
  char buf[100];
  scanf("%99s", buf); /* funkcja wczytuje tekst do tablicy buf */
  printf(buf);
  return 0;
}

Program wczytuje tekst, a następnie wypisuje go. Jednak ponieważ znak procentu jest traktowany w specjalny sposób, toteż jeżeli na wejściu pojawi się ciąg znaków zawierający ten znak mogą się stać różne dziwne rzeczy. Między innymi z tego powodu w takich sytuacjach lepiej używać funkcji puts() lub fputs() opisanych niżej lub wywołania: printf("%s", zmienna);.

Więcej o funkcji printf()

Funkcja puts

Funkcja puts() przyjmuje jako swój argument ciąg znaków, który następnie bezmyślnie wypisuje na ekran kończąc go znakiem przejścia do nowej linii. W ten sposób, nasz pierwszy program mogli byśmy napisać w ten sposób:

#include <stdio.h>

int main(void)
{
  puts("Hello world!");
  return 0;
}

W swoim działaniu funkcja ta jest w zasadzie identyczna do wywołania: printf("%s\n", argument); jednak prawdopodobnie będzie działać szybciej. Jedynym jej mankamentem może być fakt, że zawsze na końcu podawany jest znak przejścia do nowej linii. Jeżeli jest to efekt niepożądany (nie zawsze tak jest) należy skorzystać z funkcji fputs() opisanej niżej lub wywołania printf("%s", argument);.

Więcej o funkcji puts()

Funkcja fputs

Opisując funkcję fputs() wybiegamy już trochę w przyszłość (a konkretnie do opisu operacji na plikach), ale warto o niej wspomnieć już teraz, gdyż umożliwia ona wypisanie swojego argumentu bez wypisania na końcu znaku przejścia do nowej linii:

#include <stdio.h>

int main(void)
{
  fputs("Hello world!\n", stdout);
  return 0;
}

W chwili obecnej możesz się nie przejmować tym zagadkowym stdout wpisanym jako drugi argument funkcji. Jest to określenie strumienia wyjściowego (w naszym wypadku standardowe wyjście - stan'dard output).

Więcej o funkcji fputs()

Funkcja putchar

Funkcja putchar() służy do wypisywania pojedynczych znaków. Przykładowo jeżeli chcielibyśmy napisać program wypisujący w prostej tabelce wszystkie liczby od 0 do 99 moglibyśmy to zrobić tak:

#include <stdio.h>

int main(void) {
  int i = 0;
  for (; i<100; ++i) {
    /* Nie jest to pierwsza liczba w wierszu */
    if (i % 10) {
      putchar(' ');
    }
    printf("%2d", i);
    /* Jest to ostatnia liczba w wierszu */
    if ((i % 10)==9) {
      putchar('\n');
    }
  }
  return 0;
}

Więcej o funkcji putchar()

Funkcje wejścia

Funkcja scanf()

Teraz pomyślmy o sytuacji odwrotnej. Tym razem to użytkownik musi powiedzieć coś programowi. W poniższym przykładzie program podaje kwadrat liczby, podanej przez użytkownika:

#include <stdio.h>

int main ()
{
  int liczba = 0;
  printf ("Podaj liczbę: ");
  scanf ("%d", &liczba);
  printf ("%d*%d=%d\n", liczba, liczba, liczba*liczba); 
  return 0;
}

Zauważyłeś, że w tej funkcji przy zmiennej pojawił się nowy operator - & (etka). Jest on ważny, gdyż bez niego funkcja scanf() nie skopiuje odczytanej wartości liczby do odpowiedniej zmiennej! Właściwie oznacza przekazanie do funkcji adresu zmiennej, by funkcja mogła zmienić jej wartość. Nie musisz teraz rozumieć, jak to się odbywa, wszystko zostanie wyjaśnione w rozdziale Wskaźniki.

Oznaczenia są podobne takie jak przy printf(), czyli scanf("%i", &liczba); wczytuje liczbę typu int, scanf("%f", &liczba); – liczbę typu float, a scanf("%s", tablica_znaków); ciąg znaków. Ale czemu w tym ostatnim przypadku nie ma etki? Otóż, gdy podajemy jako argument do funkcji wyrażenie typu tablicowego zamieniane jest ono automatycznie na adres pierwszego elementu tablicy. Będzie to dokładniej opisane w rozdziale poświęconym wskaźnikom.

Należy jednak uważać na to ostatnie użycie. Rozważmy na przykład poniższy kod:

#include <stdio.h>

int main(void)
{
  char tablica[100];     /* 1 */
  scanf("%s", tablica);  /* 2 */
  return 0;
}

Robi on niewiele. W linijce 1 deklarujemy tablicę 100 znaków czyli mogącą przechować napis długości 99 znaków. Nie przejmuj się jeżeli nie do końca to wszystko rozumiesz - pojęcia takie jak tablica czy ciąg znaków staną się dla Ciebie jasne w miarę czytania kolejnych rozdziałów. W linijce 2 wywołujemy funkcję scanf(), która odczytuje tekst ze standardowego wejścia. Nie zna ona jednak rozmiaru tablicy i nie wie ile znaków może ona przechować przez co będzie czytać tyle znaków, aż napotka biały znak (format %s nakazuje czytanie pojedynczego słowa), co może doprowadzić do przepełnienia bufora. Niebezpieczne skutki czegoś takiego opisane są w rozdziale poświęconym napisom. Na chwilę obecną musisz zapamiętać, żeby zaraz po znaku procentu podawać maksymalną liczbę znaków, które może przechować bufor, czyli liczbę o jeden mniejszą, niż rozmiar tablicy. Bezpieczna wersją powyższego kodu jest:

#include <stdio.h>

int main(void)
{
  char tablica[100];
  scanf("%99s", tablica);
  return 0;
}

Więcej o funkcji scanf()

Funkcja gets

Funkcja gets służy do wczytania pojedynczej linii. Może Ci się to wydać dziwne, ale: funkcji tej nie należy używać pod żadnym pozorem. Przyjmuje ona jeden argument - adres pierwszego elementu tablicy, do którego należy zapisać odczytaną linię - i nic poza tym. Z tego powodu nie ma żadnej możliwości przekazania do tej funkcji rozmiaru bufora podanego jako argument. Podobnie jak w przypadku scanf() może to doprowadzić do przepełnienia bufora, co może mieć tragiczne skutki. Zamiast tej funkcji należy używać funkcji fgets().

Więcej o funkcji gets()

Funkcja fgets

Funkcja fgets() jest bezpieczną wersją funkcji gets(), która dodatkowo może operować na dowolnych strumieniach wejściowych. Jej użycie jest następujące:

fgets(tablica_znaków, rozmiar_tablicy_znaków, stdin);

Na chwilę obecną nie musisz się przejmować ostatnim argumentem (jest to określenie strumienia, w naszym przypadku standardowe wejście - standard input). Funkcja czyta tekst aż do napotkania znaku przejścia do nowej linii, który także zapisuje w wynikowej tablicy (funkcja gets() tego nie robi). Jeżeli brakuje miejsca w tablicy to funkcja przerywa czytanie, w ten sposób, aby sprawdzić czy została wczytana cała linia czy tylko jej część należy sprawdzić czy ostatnim znakiem nie jest znak przejścia do nowej linii. Jeżeli nastąpił jakiś błąd lub na wejściu nie ma już danych funkcja zwraca wartość NULL.

#include <stdio.h>

int main(void) {
  char buffer[128], whole_line = 1, *ch;
  while (fgets(buffer, sizeof buffer, stdin)) { /* 1 */
    if (whole_line) {                           /* 2 */
      putchar('>');
      if (buffer[0]!='>') {
        putchar(' ');
      }
    }
    fputs(buffer, stdout);                      /* 3 */
    for (ch = buffer; *ch && *ch!='\n'; ++ch);  /* 4 */
    whole_line = *ch == '\n';
  }
  if (!whole_line) {
    putchar('\n');
  }
  return 0;
}

Powyższy kod wczytuje dane ze standardowego wejścia - linia po linii - i dodaje na początku każdej linii znak większości, po którym dodaje spację jeżeli pierwszym znakiem na linii nie jest znak większości. W linijce 1 następuje odczytywanie linii. Jeżeli nie ma już więcej danych lub nastąpił błąd wejścia funkcja zwraca wartość NULL, która ma logiczną wartość 0 i wówczas pętla kończy działanie. W przeciwnym wypadku funkcja zwraca po prostu pierwszy argument, który ma wartość logiczną 1. W linijce 2 sprawdzamy, czy poprzednie wywołanie funkcji wczytało całą linię, czy tylko jej część - jeżeli całą to teraz jesteśmy na początku linii i należy dodać znak większości. W linii 3 najzwyczajniej w świecie wypisujemy linię. W linii 4 przeszukujemy tablicę znak po znaku, aż do momentu, gdy znajdziemy znak o kodzie 0 kończącym ciąg znaków albo znak przejścia do nowej linii. Ten drugi przypadek oznacza, że funkcja fgets() wczytała całą linię.

Więcej o funkcji fgets()

Funkcja getchar()

Jest to bardzo prosta funkcja, wczytująca 1 znak z klawiatury. W wielu przypadkach dane mogą być buforowane przez co wysyłane są do programu dopiero, gdy bufor zostaje przepełniony lub na wejściu jest znak przejścia do nowej linii. Z tego powodu wpisaniu danego należy nacisnąć klawisz enter, aczkolwiek trzeba pamiętać, że w następnym wywołaniu zostanie zwrócony znak przejścia do nowej linii. Gdy nastąpił błąd lub nie ma już więcej danych funkcja zwraca wartość EOF (która ma jednak wartość logiczną 1 toteż zwykła pętla while (getchar()) nie da oczekiwanego rezultatu):

#include <stdio.h>

int main(void)
{
  int c;
  while ((c = getchar())!=EOF) {
    if (c==' ') {
      c = '_';
    }
    putchar(c);
  }
  return 0;
}

Ten prosty program wczytuje dane znak po znaku i zamienia wszystkie spacje na znaki podkreślenia. Może wydać się dziwne, że zmienną c zdefiniowaliśmy jako trzymającą typ int, a nie char. Właśnie taki typ (tj. int) zwraca funkcja getchar() i jest to konieczne ponieważ wartość EOF wykracza poza zakres wartości typu char (gdyby tak nie było to nie byłoby możliwości rozróżnienia wartości EOF od poprawnie wczytanego znaku).

Więcej o funkcji getchar()

Przypisy

  1. W zasadzie standard C nie definiuje czegoś takiego jak ekran i klawiatura - mowa w nim o standardowym wyjściu i standardowym wejściu. Zazwyczaj jest to właśnie ekran i klawiatura, ale nie zawsze. W szczególności użytkownicy Linuksa lub innych systemów uniksowych mogą być przyzwyczajeniu do przekierowania wejścia/wyjścia z/do pliku czy łączenie komend w potoki (ang. pipe). W takich sytuacjach dane nie są wyświetlane na ekranie, a odczytywane z klawiatury.
  2. Zmiana ta następuje w momencie kompilacji programu i dotyczy wszystkich literałów napisowych. Nie jest to jakaś szczególna własność funkcji printf(). Więcej o tego typu sekwencjach i ciągach znaków w szczególności opisane jest w rozdziale Napisy.

Szablon:ProstaNawigacja