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

Usunięta treść Dodana treść
Nie podano opisu zmian
Linia 109:
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:
 
<source lang="C">
#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');
}
Linia 126 ⟶ 131:
return 0;
}
</source>
 
[[C/putchar|Więcej o funkcji putchar()]]
Linia 134 ⟶ 140:
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:
 
<source lang="C">
#include <stdio.h>
Linia 144 ⟶ 151:
return 0;
}
</source>
 
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]].
Linia 152 ⟶ 160:
 
Należy jednak uważać na to ostatnie użycie. Rozważmy na przykład poniższy kod:
<source lang="C">
 
#include <stdio.h>
Linia 161 ⟶ 169:
return 0;
}
</source>
 
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:
<source lang="C">
 
#include <stdio.h>
Linia 172 ⟶ 181:
return 0;
}
</source>
 
Funkcja scanf() zwraca liczbę poprawnie wczytanych zmiennych lub EOF jeżeli nie ma już danych w strumieniu lub nastąpił błąd. Załóżmy dla przykładu, że chcemy stworzyć program, który odczytuje po kolei liczby i wypisuje ich 3 potęgi. W pewnym momencie dane się kończą lub jest wprowadzana niepoprawna dana i wówczas nasz program powinien zakończyć działanie. Aby to zrobić, należy sprawdzać wartość zwracaną przez funkcję scanf() w warunku pętli:
 
<source lang="C">
#include <stdio.h>
Linia 180 ⟶ 190:
{
int n;
while (scanf("%d", &n)==1) {
{
printf("%d\n", n*n*n);
}
return 0;
}
</source>
 
Podobnie możemy napisać program, który wczytuje po dwie liczby i je sumuje:
<source lang="C">
 
#include <stdio.h>
Linia 193 ⟶ 205:
{
int a, b;
while (scanf("%d %d", &a, &b)==2) {
{
printf("%d\n", a+b);
}
return 0;
}
</source>
 
Rozpatrzmy teraz trochę bardziej skomplikowany przykład. Otóż, ponownie jak poprzednio nasz program będzie wypisywał 3 potęgę podanej liczby, ale tym razem musi ignorować błędne dane (tzn. pomijać ciągi znaków, które nie są liczbami) i kończyć działanie tylko w momencie, gdy nastąpi błąd odczytu lub koniec pliku<ref>Jak rozróżniać te dwa zdarzenia dowiesz się w rozdziale [[C/Czytanie_i_pisanie_do_plików|Czytanie i pisanie do plików]].</ref>.
<source lang="C">
 
#include <stdio.h>
Linia 206 ⟶ 219:
{
int result, n;
do {
{
result = scanf("%d", &n);
if (result==1) {
{
printf("%d\n", n*n*n);
}
else if (!result) { /* !result to to samo co result==0 */
result = scanf("%*s");
}
}
while (result!=EOF);
return 0;
}
</source>
 
Zastanówmy się przez chwilę co się dzieje w programie. Najpierw wywoływana jest funkcja scanf() i następuje próba odczytu liczby typu int. Jeżeli funkcja zwróciła 1 to liczba została poprawnie odczytana i następuje wypisanie jej trzeciej potęgi. Jeżeli funkcja zwróciła 0 to na wejściu były jakieś dane, które nie wyglądały jak liczba. W tej sytuacji wywołujemy funkcję scanf() z formatem odczytującym dowolny ciąg znaków nie będący białymi znakami z jednoczesnym określeniem, żeby nie zapisywała nigdzie wyniku. W ten sposób niepoprawnie wpisana dana jest omijana. Pętla główna wykonuje się tak długo jak długo funkcja scanf() nie zwróci wartości EOF.
 
Linia 234 ⟶ 251:
 
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.
<source lang="C">
 
#include <stdio.h>
Linia 255 ⟶ 272:
return 0;
}
</source>
 
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ę.
 
Linia 263 ⟶ 280:
 
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 po wpisaniu danego znaku 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):
<source lang="C">
 
#include <stdio.h>
Linia 277 ⟶ 294:
return 0;
}
</source>
 
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()]]