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

m
Update syntaxhighlight tags - remove use of deprecated <source> tags
m (Wycofano edycje użytkownika 77.65.16.150 (dyskusja). Autor przywróconej wersji to Adam majewski.)
Znacznik: Wycofanie zmian
m (Update syntaxhighlight tags - remove use of deprecated <source> tags)
 
 
W [[C/Pierwszy_program|przykładzie "Witaj świecie!"]] 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 "Witaj świecie!".
<sourcesyntaxhighlight lang="C">
#include <stdio.h>
return 0;
}
</syntaxhighlight>
</source>
Po skompilowaniu i uruchomieniu, program wypisze na ekranie:
 
printf(format, argument1, argument2, ...);
Przykładowo:
<sourcesyntaxhighlight lang="C">
int i = 500;
printf("Liczbami całkowitymi są na przykład %i oraz %i.\n", 1, i);
</syntaxhighlight>
</source>
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 <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:
<sourcesyntaxhighlight lang="C">
printf("Procent: %% Backslash: \\");
</syntaxhighlight>
</source>
drukuje:
Procent: % Backslash: \
 
Funkcja printf() nie jest żadną specjalną konstrukcją języka i łańcuch formatujący może być podany jako zmienna.<ref>[https://www.unix.com/programming/244285-passing-printf-formatting-parameters-variables.html unix.com : passing-printf-formatting-parameters-variables]</ref> W związku z tym możliwa jest np. taka konstrukcja:
<sourcesyntaxhighlight lang="C">
#include <stdio.h>
return 0;
}
</syntaxhighlight>
</source>
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>.
 
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 moglibyśmy napisać w ten sposób:
 
<sourcesyntaxhighlight lang="C">
#include <stdio.h>
return 0;
}
</syntaxhighlight>
</source>
 
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>.
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:
 
<sourcesyntaxhighlight lang="C">
#include <stdio.h>
return 0;
}
</syntaxhighlight>
</source>
 
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).
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:
 
<sourcesyntaxhighlight lang="C">
#include <stdio.h>
return 0;
}
</syntaxhighlight>
</source>
 
[[C/putchar|Więcej o funkcji putchar()]]
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:
 
<sourcesyntaxhighlight lang="C">
#include <stdio.h>
return 0;
}
</syntaxhighlight>
</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]].
 
Należy jednak uważać na to ostatnie użycie. Rozważmy na przykład poniższy kod:
<sourcesyntaxhighlight lang="C">
#include <stdio.h>
return 0;
}
</syntaxhighlight>
</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:
<sourcesyntaxhighlight lang="C">
#include <stdio.h>
return 0;
}
</syntaxhighlight>
</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 trzecie 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:
 
<sourcesyntaxhighlight lang="C">
#include <stdio.h>
return 0;
}
</syntaxhighlight>
</source>
 
Podobnie możemy napisać program, który wczytuje po dwie liczby i je sumuje:
<sourcesyntaxhighlight lang="C">
#include <stdio.h>
return 0;
}
</syntaxhighlight>
</source>
Rozpatrzmy teraz trochę bardziej skomplikowany przykład. Otóż, ponownie jak poprzednio nasz program będzie wypisywał trzecią 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>.
<sourcesyntaxhighlight lang="C">
#include <stdio.h>
return 0;
}
</syntaxhighlight>
</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.
 
 
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.
<sourcesyntaxhighlight lang="C">
#include <stdio.h>
return 0;
}
</syntaxhighlight>
</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ę.
 
 
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):
<sourcesyntaxhighlight lang="C">
#include <stdio.h>
return 0;
}
</syntaxhighlight>
</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()]]
90

edycji