Funkcja "scanf" w języku C również jest częścią standardowej obsługi wejścia-wyjścia 🔧. Tym razem, w odróżnieniu od "printf", obsługuje ona komunikację pomiędzy człowiekiem, a komputerem w drugą stronę - oczekując wprowadzenia danych i zatwierdzenia ✅. Sprawdź na co musisz koniecznie zwrócić uwagę podczas korzystania z funkcji "scanf" oraz dlaczego niewłaściwe jej użycie poskutkuje wyłożeniem się programu 💀!
"SCANF" W JĘZYKU C POBIERZE CI DANE ZE STRUMIENIA WEJŚCIOWEGO!
Wyjaśnię na szybko zastosowanie funkcji i od razu pokażę przykład użycia w kodzie 😄!
DO CZEGO SŁUŻY FUNKCJA "SCANF"?
Tytułowa funkcja "scanf" (będąca skrótem od "scan formatted"), umożliwia przekazywanie wartości do zmiennych w postaci wprowadzanych znaków z klawiatury, lecz to znowu jest uproszczenie - tak naprawdę to dotyczy strumienia wejściowego "stdin" (ang. standard input), którym wcale nie musi być klawiatura ⚠️!
Przy użyciu tej funkcji, jesteś w stanie pobrać wpisane dane, a następnie przekazać je do wskazanego miejsca w pamięci ✅. Natomiast trzeba liczyć się z pewną pułapką, na którą wejdziesz na 99%, jeśli korzystasz z funkcji po raz pierwszy w życiu 😏! Pokażę o co chodzi podczas prezentacji przykładu kodu.
PRZYKŁAD KODU ŹRÓDŁOWEGO DZIAŁANIA FUNKCJI "SCANF"
Teraz prosty przykład użycia. Dajmy na to, że interesuje nas pobranie od użytkownika jego wieku. Po wprowadzeniu danej, zostanie wypisany komunikat z podaną liczbą. No to proszę bardzo 👇:
#include <stdio.h>
int main(void)
{
int typedAge;
printf("Ile masz lat?\n");
scanf("%d", &typedAge);
printf("Podany wiek to: %d\n", typedAge);
return 0;
}Na pierwszy rzut oka, nie ma niczego wzbudzającego niepokój. Jednak przyjrzyj się wywołaniu "scanf" w języku C 👀. Czy widzisz ten subtelny szczegół 🫵? Może wytnę ten fragment:
scanf("%d", &typedAge);Już 🙂? Jeszcze raz przycięcie, aż do zostawienia kluczowego fragmentu 🔍:
&typedAgeAmpersand przed nazwą zmiennej 💥! To jest kolejna rzecz, której nie zobaczysz w takich językach, jak Java, C# czy JavaScript. Tu robimy przystanek na wyjaśnienie znaczenia tego symbolu ✋.
JAKIE ZNACZENIE MA AMPERSAND W JĘZYKU C?
Znak ampersandu w języku C ma kilka znaczeń. Jednym z nich jest pobranie adresu zmiennej. Adres to miejsce w pamięci zapisane w systemie szesnastkowym (heksadecymalnym) ℹ️. Więcej informacji w odrębnym artykule.
Na potrzeby funkcji "scanf" w języku C wystarczy żebyś wiedział(a), że chcąc przypisać liczbę całkowitą, znak czy cokolwiek innego, co zostaje wprowadzone "z palca" 👆, za parametr wstawiasz adres do zmiennej, a nie samą zmienną! Powodem jest sama operacja utrwalania wprowadzonej danej.
Typy proste nie mają zdolności do utrwalania zmodyfikowanych wartości po zakończeniu działania funkcji, w której ta zmiana zaszła ⚠️. Wtedy musimy skorzystać ze wskaźnika, aby "przesiąść się" na przekazywanie przez referencję (więcej szczegółów o wspomnianych terminach znajdziesz w odrębnych artykułach) ℹ️.
Co się stanie, gdy zapomnimy wstawić ampersand 🤔?
scanf("%d", typedAge);Pisząc krótko: wyłoży się program 💣. Pisząc fachowo: dojdzie do niezdefiniowanego zachowania, które w języku C jest bardzo niebezpieczne 💥! Także pamiętaj - używamy adresu zmiennej, aby dokonać trwałej modyfikacji!
KIEDY NIE TRZEBA WSTAWIAĆ AMPERSANDU W FUNKCJI "SCANF"?
2 wyjątki, które nie wymagają ampersandu podczas korzystania ze "scanf", to 👇:
- wskaźnik (wraz z tablicą),
- łańcuch znaków.
Powód znów logiczny: oba należą do typów referencyjnych (same w sobie są wskaźnikami) ℹ️. Czyli automatycznie "nabywają" zdolności do utrwalenia danych 😄.
Zeszliśmy z omawiania samej postaci funkcji "scanf", także już opisuję z czego się ona składa 😁.
JAKIE PARAMETRY PRZYJMUJE FUNKCJA "SCANF"?
Pierwszym parametrem jest specyfikator. Tak, ten sam poznany przy tłumaczeniu funkcji "printf". Trzeba uważać przy wpisywaniu, ponieważ on zależy od typu danych ⚠️. Pełna lista wspieranych specyfikatorów znajduje się w dalszej części tego artykułu ℹ️.
Drugim parametrem jest wspomniany adres do zmiennej, która ma posiadać wartość jaką wprowadzimy. Tutaj tak samo trzymamy się zgodności pomiędzy specyfikatorem, a typem danych zmiennej (bo inaczej narażasz się na niezdefiniowane zachowanie 💣) ⚠️!
Najprostsza postać wywołania "scanf" w języku C kończy się na tych dwóch parametrach 👍. Jednak, jak się domyślasz, to nie koniec niespodzianek 😉!
W JAKI SPOSÓB KORZYSTAĆ Z FUNKCJI "SCANF" DLA WIELU DANYCH JEDNOCZEŚNIE?
Są sytuacje, w których będziesz potrzebować pobrać więcej wartości, niż tylko jedna. Oczywiście, można skorzystać z dwóch osobnych wywołań 👇:
scanf("%d", &typedAge);
scanf("%d", &monthlyIncome);albo zmodyfikować łańcuch znaków, tak aby posiadał DWA specyfikatory, zamiast jednego:
scanf("%d %d", &typedAge, &monthlyIncome);Wtedy program będzie oczekiwać wprowadzenia dwóch wartości w jednym czasie 💥! W trakcie oczekiwania podania przez nas danych, mamy 2 możliwości ich wpisywania:
- wprowadzamy tyle ile potrzeba, jednym ciągiem oddzielając wartości spacją,
- wprowadzamy jedna po drugiej zatwierdzając Enterem, aż do wyczerpania.
JAK MOŻNA ZIGNOROWAĆ WPROWADZONĄ WARTOŚĆ W FUNKCJI "SCANF"?
"scanf" w języku C umożliwia dosłowne "przeskakiwanie" pewnych wartości ✈️. Na przykład możesz chcieć oczekiwać wprowadzenia 3 liczb, a tylko ostatnia zostanie uwzględniona. To da się zrobić używając asterysku, czyli znaku "gwiazdki" 👇:
scanf("%*d %*d %d", &typedAge);Uwaga ⚠️! Spójrz na liczbę adresów do zmiennych. Nie wprowadzasz wszystkich jak leci, tylko tyle, ile odpowiada liczba specyfikatorów bez asterysku (to są te, które bierzemy pod uwagę 😉). Natomiast przy uruchomieniu programu, podajemy grzecznie trzy liczby całkowite 🔥!
Ignorowanie wskazanych wartości przydaje się bardziej podczas przetwarzania danych z plików, gdy występują jakieś kolumny z danymi, które nas nie interesują, a nie powinniśmy ich bezlitośnie usuwać ❌. Pamiętaj, że strumieniem wejściowym nie musi być wyłącznie klawiatura ⚠️!
Ostatnia ważna cecha funkcji, o jakiej chciałem napisać 📝.
JAKĄ WARTOŚĆ ZWRACA FUNKCJA "SCANF"?
"scanf" w języku C zwraca wartość wynikową w postaci liczby całkowitej 🔢! Przypisując wywołanie funkcji do zmiennej typu "int" 👇:
int numberOfCorrectlyTypedValues = scanf("%d", &typedAge);otrzymasz wartość określającą ile oczekiwanych wartości zostało uznanych za prawidłowe ✅.
Tylko uwaga! Nie bierze pod uwagę tych specyfikatorów, które mają zostać zignorowane (zobacz poprzedni nagłówek) 🚨!
NA CO TRZEBA UWAŻAĆ PODCZAS KORZYSTANIA Z FUNKCJI "SCANF"?
Muszę Ci uświadomić przykrą rzecz. "scanf" w języku C nie jest odporne na wpisywanie niewłaściwych wartości 🚫. Robiąc "na złość", narażamy się na nieprzewidywalne konsekwencje 😱!
Już wiesz co się stanie, jak zapomnisz wstawić znak ampersandu przed nazwą zmiennej 👇:
scanf("%d", typedAge);Działanie programu zostanie brutalnie przerwane ❌! W przypadku, gdy "scanf" oczekuje liczby:
scanf("%d", &typedAge);a my wprowadzimy coś innego np. łańcuch znaków:
"Tekst zamiast liczby xD"to program sobie "poleci" z następnymi wywołaniami "scanf", już bez jakichkolwiek pytań Ciebie o dane 😁! Ponadto, kolejne wartości będą kompletnie "z czapy" 🧢.
W języku C nie ma miejsca na pomyłki ✋! Tylko Ty odpowiadasz za różne "fikołki" programu, jakie powstają w wyniku zostawionych błędów! Dlatego przy programowaniu (już obojętnie w jakim języku) należy zawsze weryfikować wprowadzony ciąg znaków, tak aby nie doszło do żadnych bzdur i dziwnych "odlotów" 😅.
W JAKI SPOSÓB MOŻNA UCHRONIĆ SIĘ PRZED PODANIEM NIEPRAWIDŁOWYCH DANYCH PRZEZ FUNKCJĘ "SCANF"?
Mogę ze swej strony pokazać jedną sztuczkę z użyciem pętli "do-while" 🔔. Korzystając z wiedzy, że "scanf" zwraca wartość w postaci liczby poprawnie odczytanych danych, możesz wstawić wynik wywołania bezpośrednio do pętli i sprawdzać co iterację czy równa się oczekiwanej wartości 👇:
do
{
printf("Podaj 3 liczby\n");
}
while (scanf("%d %d %d", &a, &b, &c) != 3 && streamWasCleaned());Tylko oprócz tego, trzeba skorzystać z niekonwencjonalnego podejścia stosując drugą pętlę "while" i funkcję "getchar":
int streamWasCleaned(void)
{
while (getchar() != '\n');
return 1;
}Ujmując najprościej jak to możliwe: "obleć" wszystkie wprowadzone znaki w jednym ciągu, dopóki nie natrafisz na Enter, które zatwierdziło wprowadzone dane. Chodzi o "przeczyszczenie" strumienia danych ze wszystkich znaków jakie zostały wprowadzone, gdyż bez tego, mielibyśmy nieskończoną pętlę 🔁!
Taki zapis sprawdza się świetnie w kwestii walidacji danych ✅.
(DRUGI) PRZYKŁAD KODU ŹRÓDŁOWEGO DZIAŁANIA FUNKCJI "SCANF"
Kod źródłowy zbierający wszystkie możliwe kombinacje - tym razem wstawiony pod sam koniec punktu 👇😄:
#include <stdio.h>
int main(void)
{
int a;
char word[10];
int b;
int c;
int d;
scanf("%d", &a);
scanf("%s", word);
scanf("%d %d", &b, &c);
scanf("%*d %*d %d", &d);
printf("%d\n", a);
printf("%s\n", word);
printf("%d\n", b);
printf("%d\n", c);
printf("%d\n", d);
return 0;
}Pominąłem sprawdzanie poprawności, aby jeszcze nie bardziej nie rozciągać i tak już zbyt długiego artykułu 😅.
Dodatkowy komentarz do wywołania z łańcuchem znaków:
scanf("%s", word);"scanf" akceptuje łańcuchy, natomiast przyjmuje tylko jeden ciąg, do natrafienia na znak spacji (reszta łańcucha zostanie "ucięta" ✂️) 😲. Z tego powodu, to nie jest najlepsza metoda na wprowadzanie łańcuchów ❌. Lepiej już użyć chociażby "fgets", które już uwzględnia spacje 👍.
JAKIE SĄ SPECYFIKATORY FORMATU FUNKCJI "SCANF"?
Tak jak w poprzednim wpisie tego typu, łap "ściągawkę" wszystkich istniejących specyfikatorów, które dotyczą opisywanej funkcji "scanf" w języku C 👇:
| SPECYFIKATOR | ZNACZENIE |
| %hd | liczba całkowita w systemie dziesiętnym ze znakiem (signed short int) |
| %hu | liczba całkowita w systemie dziesiętnym bez znaku (unsigned short int) |
| %d / %i | liczba całkowita w systemie dziesiętnym ze znakiem (signed int) |
| %u | liczba całkowita w systemie dziesiętnym bez znaku (unsigned int) |
| %ld | liczba całkowita w systemie dziesiętnym ze znakiem (signed long int) |
| %lu | liczba całkowita w systemie dziesiętnym bez znaku (unsigned long int) |
| %lld | liczba całkowita w systemie dziesiętnym ze znakiem (signed long long int) |
| %llu | liczba całkowita w systemie dziesiętnym bez znaku (unsigned long long int) |
| %o | liczba całkowita w systemie oktalnym (ósemkowym) |
| %x / %X | liczba całkowita w systemie heksadecymalnym (szesnastkowym) |
| %f | liczba zmiennoprzecinkowa w systemie dziesiętnym (float) |
| %lf | liczba zmiennoprzecinkowa w systemie dziesiętnym (double) |
| %Lf | liczba zmiennoprzecinkowa w systemie dziesiętnym (long double) |
| %c | pojedynczy znak |
| %s | łańcuch znaków bez spacji |
| %p | wskaźnik jako adres (*) |
Ta sama porada - nie kuj tego wszystkiego na siłę ✋. Jak będziesz sobie to ćwiczyć i jak zacznie być potrzebne, to wtedy tu przyjdź, popatrz, użyj. Tak dużo lepiej się uczyć ✔️.
![]() |
Funkcja "scanf" w języku C pobiera od strumienia wejściowego dane o konkretnym typie zwracając uwagę na prawidłowość.
Wreszcie koniec o funkcji "scanf" w języku C 🙂. Spory materiał wyszedł, to trzeba przyznać 😅!
