Programowanie strukturalne w
C++, pętle, instrukcje if, switch,
Podstawowa budowa programu;
strumienie
Najprostszy program w C++, jaki jest, każdy
widzi:
#include <iostream>
using namespace
std;
int main()
{ // pierwszy program
cout
<<
"Hello, I'm Jan B.\n"
;
return
0;
}
// pierwszy program – pierwszy program w C++
to komentarz, czyli dowolny opis słowny. Jest on całkowicie
ignorowany przez kompilator, natomiast może być pomocny dla
piszącego i czytającego kod.
Komentarze
piszemy w celu wyjaśnienia pewnych fragmentów kodu
programu, oddzielenia jednej jego części od drugiej, oznaczania funkcji
i modułów itp. Odpowiednia ilość komentarzy ułatwia zrozumienie
kodu, więc stosuj je często
W C++ komentarze zaczynamy od
//
(dwóch slashy), lub umieszczamy
je między /*
i */, na przykład:
/* Ten komentarz może być bardzo długi i składać się z kilku
linijek. */
Rys.1. Translacja programu C++
Funkcja
main()
Kiedy uruchamiamy nasz program, zaczyna on wykonywać kod
zawarty w funkcji main(). Od niej więc rozpoczyna się działanie aplikacji
– a nawet więcej: na niej też to działanie się kończy. Zatem program
(konsolowy) to przede wszystkim kod zawarty w funkcji main() –
determinuje on bezpośrednio jego zachowanie.
Rys.2. Struktura funkcji main()
W przypadku rozważanej aplikacji funkcja ta nie jest zbyt
obszerna, niemniej zawiera wszystkie niezbędne elementy.
Najważniejszym z nich jest nagłówek, który u nas prezentuje
się następująco:
W C++ Builder ta sama funkcja może mieć postać:
#include <iostream>
#include <conio.h>
//using namespace
std;
//int
main() // standard
void main()
{ // pierwszy program
std::
cout
<<
"Hello, I'm Jan B.\n"
;
getch();
// return 0; //standard
}
std::cout oznacza tak zwany strumień wyjścia. Jego
zadaniem jest wyświetlanie na ekranie konsoli wszystkiego, co doń
wyślemy – a wysyłać możemy oczywiście tekst.
Korzystanie z tego strumienia umożliwia zatem pokazywanie nam w oknie
konsoli wszelkiego rodzaju komunikatów i innych informacji. Będziemy go
używać bardzo często, dlatego musisz koniecznie zaznajomić się ze
sposobem wysyłania doń tekstu.
Dołączanie plików nagłówkowych
#include
<iostream>
#include
<conio.h>
Linijki które wcale nie są tak straszne, jak wyglądają na pierwszy rzut
oka. Przede wszystkim zauważmy, że zaczynają się one od znaku
#
,
czym niewątpliwie różnią się od innych instrukcji języka C++. Są to
bowiem specjalne polecenia wykonywane jeszcze przed kompilacją - tak
zwane dyrektywy. Przekazują one różne informacje i komendy,
pozwalają więc sterować przebiegiem kompilacji programu.
Pliki nagłówkowe
umożliwiają korzystanie z pewnych funkcji, technik,
bibliotek itp. wszystkim programom, które dołączają je do swojego kodu
źródłowego.
Preprocesor
"Chiałbym się kiedyś dowiedzieć, że preprocesor został usunięty.
Jednak jedyny realny i odpowiedzialny sposób, który może do tego
doprowadzić, polega na tym, żeby najpierw sprawić, że stanie się zbędny,
po czym zachęcić ludzi do używania jego lepszych odpowiedników."
Bjarne Stroustrup
Preprocesor
to specjalny mechanizm języka, który przetwarza tekst
programu jeszcze przed jego kompilacją
.
Rys. Linkowanie (łączenie) tworzy wolny od luki kod
wykonywalny
Zwyczajowy przebieg budowania programu
W
języku
programowania
nieposiadającym
preprocesora
generowanie docelowego pliku z programem przebiega, jak wiemy, w
dwóch etapach.
Pierwszym jest kompilacja, w trakcie której kompilator przetwarza
kod źródłowy aplikacji i produkuje skompilowany kod maszynowy,
zapisany w osobnych plikach. Każdy taki plik - wynik pracy
kompilatora - odpowiada jednemu modułowi kodu źródłowego.
W drugim etapie następuje linkowanie (łączenie) skompilowanych
wcześniej modułów oraz ewentualnych innych kodów, niezbędnych do
działania programu. W wyniku tego procesu powstaje gotowy
program.
Rys. Najprostszy proces budowania programu z kodu
źródłowego
Przy takim modelu kompilacji zawartość każdego modułu musi
wystarczać do jego samodzielnej kompilacji, niezależnej od innych
modułów. W przypadku języków z rodziny C oznacza to, że każdy moduł
musi zawierać deklaracje używanych funkcji oraz definicje klas, których
obiekty tworzy i z których korzysta.
Gdyby zadanie dołączania tych wszystkich deklaracji spoczywało na
programiście, to byłoby to dla niego niezmiernie uciążliwe. Pliki z kodem
zostały ponadto rozdęte do nieprzyzwoitych rozmiarów, a i tak
większość zawartych weń informacji przydawałyby się tylko przez
chwilę. Przez tą chwilę, którą zajmuje kompilacja modułu.
Nic więc dziwnego, że aby zapobiec podobnym irracjonalnym
wymaganiom wprowadzono mechanizm preprocesora.
Dodajemy preprocesor
Ujawnił się nam pierwszy cel istnienia preprocesora: w języku C(+
+) służy on do łączenia w jedną całość modułów kodu wraz z
deklaracjami, które są niezbędne do działania tegoż kodu. A skąd
brane są te deklaracje?… Oczywiście - z plików nagłówkowych.
Zawierają one przecież prototypy funkcji i definicje klas, z jakich
można korzystać, jeżeli dołączy się dany nagłówek do swojego
modułu.
Jednak kompilator
nic nie wie
o plikach nagłówkowych. On tylko
oczekuje, że zostaną mu podane pliki z kodem źródłowym, do którego
będą się zaliczały także deklaracje pewnych zewnętrznych elementów
- nieobecnych w danym module.
Kompilator potrzebuje tylko ich
określenia „z wierzchu”, bez wnikania w implementację, gdyż ta może
znajdować się w innych modułach lub nawet innych bibliotekach i staje
się ważna dopiero przy linkowaniu
. Nie jest już ona sprawą kompilatora
- on żąda tylko tych informacji, które są mu potrzebne do kompilacji.
Niezbędne deklaracje powinny się znaleźć na początku każdego
modułu
. Trudno jednak oczekiwać, żebyśmy wpisywali je ręcznie w
każdym module, który ich wymaga. Byłoby to niezmiernie uciążliwe,
więc wymyślono w tym celu pliki nagłówkowe… i preprocesor. Jego
zadaniem jest tutaj połączenie napisanych przez nas modułów oraz
plików nagłówkowych w pliki z kodem, które mogą być bez przeszkód
przetworzone przez kompilator.
Rys. Budowanie programu C++ z udziałem preprocesora
Skąd preprocesor wie, jak ma to zrobić?… Otóż, mówimy o tym
wyraźnie, stosując dyrektywę
#include
. W miejscu jej pojawienia się
zostaje po prostu wstawiona treść odpowiedniego pliku nagłówkowego.
Włączanie nagłówków nie jest jednak jedynym działaniem
podejmowanym przez preprocesor. Gdyby tak było, to przecież nie
poświęcalibyśmy mu całego rozdziału. Jest wręcz przeciwnie:
dołączanie plików to tylko jedna z czynności, jaką możemy zlecić temu
mechanizmowi - jedna z wielu czynności…
Wszystkie zadania preprocesora są różnorodne, ale mają też kilka
cech wspólnych. Przyjrzyjmy się im w tym momencie.
Dyrektywy
Polecenie dla preprocesora nazywamy jego dyrektywą (ang.
directive). Jest to specjalna linijka kodu źródłowego, rozpoczynająca się
od znaku
#
(hash), zwanego płotkiem:
#.
Na nim też może się zakończyć
- wtedy mamy do czynienia z dyrektywą pustą. Jest ona ignorowana
przez preprocesor i nie wykonuje żadnych czynności.
Przed hashem mogą znajdować się wyłącznie tzw. białe znaki, czyli
spacje lub tabulatory. Zwykle nie znajduje się nic.
Bardziej praktyczne są inne dyrektywy, których nazwy piszemy zaraz
za znakiem
#
. Nie oddzielamy ich zwykle żadnymi spacjami (choć można
to robić), więc w praktyce płotek staje się częścią ich nazw. Mówi się więc
o instrukcjach
#include
,
#define
,
#pragma
i innych, gdyż w takiej
formie zapisujemy je w kodzie.
Dalsza część dyrektywy zależy już od jej rodzaju. Różne „parametry”
dyrektyw poznamy, gdy zajmiemy się szczegółowo każdą z nich.
Dyrektywy preprocesora kończą się zawsze przejściem do następnego
wiersza.
Zapamiętaj!
Nie kończ dyrektyw preprocesora średnikiem. Nie są to przecież
instrukcje
języka
programowania,
lecz
polecenia
dla
modułu
wspomagającego kompilator
.
Oto pliki należące do standardowej biblioteki C (włącznie z ISO C 9X).
assert.h - makra do asercji
ctype.h -klasyfikacje znaków typu char (isspace, isalpha itd.)
errno.h - deklaracja errno
fenv.h - środowisko dla liczb zmiennoprzecinkowych (ISO C 9X)
float.h - definicje specjalne dla liczb zmiennoprzecinkowych
limits.h - makra określające granice dla typów ścisłych
locale.h - definicje lokali
math.h - funkcje matematyczne
setjmp.h - funkcje setjmp i longjmp
signal.h - sygnały
stdarg.h - narzędzia dla funkcji o zmiennej liście parametrów
stddef.h - standardowe definicje (ptrdiff_t i size_t głównie)
stdio.h - operacje wejścia/wyjścia
stdlib.h - zespół funkcji użytkowych
string.h - funkcje operujące na tablicach znaków
time.h - narzędzia do odczytywania, interpretacji i prezentacji czasu
wchar.h - obsługa "szerokiego" (wide-char) zestawu znaków
wctype.h - wersja `ctype.h' dla szerokich znaków
Dyrektywa #define
Dyrektywa ta pozwala tworzyć makrodefinicje. Pozwala ona zastąpić
dowolny ciąg znaków (również pusty) identyfikatorem. Może służyć
np. do definiowania stałych:
#define przyciaganie_ziemskie 9.81
Taka makrodefinicja może również posiadać argumenty, np.
#define ctg( x ) 1/tan( x )
Znajomość tej dyrektywy raczej nie będzie Ci potrzebna; najwyżej
do tego, żeby wiedzieć, co to jest. Jest to uniwersalne dosyć
narzędzie, ale bardzo niebezpieczne i dające mnóstwo okazji do
popełniania błędów.
Kompilator nie stwierdzi błędu w definicji makra (jeśli np. zdefiniujesz
jako desygnat makra jakąś konstrukcję, która jest niepoprawna
składniowo w C++), a ewentualnie dopiero w miejscu, gdzie zostało ono
użyte (kompilator nie widzi makr ani ich używania; preprocesor jest
właśnie od tego, żeby je usuwać).
W przypadku zastępowania nimi
wyrażeń arytmetyczno-logicznych należy używać dla pewności jak
najwięcej nawiasów, oraz - o czym też wielu zapomina - NIE WOLNO
używać
żadnych
operatorów
modyfikujących
na
zmiennych
przekazywanych jako parametry makra
(tzn. jako argumenty makra
należy podawać wartości lub zmienne raczej, niż wyrażenia).
Jednym też
z typowych błędów jest zakończenie tej dyrektywy średnikiem
.
Kompilacja warunkowa
Podstawową dyrektywą warunkową jest
#if
. Jako argument przyjmuje
ona warunek, który ma być spełniony (pamiętaj jednak, że interpretuje
go preprocesor, a nie kompilator!). Najczęściej jednak do kompilacji
warunkowej stosuje się makrowartowniki. Są to puste makra,
definiowane w plikach nagłówkowych, dla np. zabezpieczenia przed
kilkakrotnym wstawianiem tego samego pliku. Sprawdzenia tego
dokonujemy dyrektywami #ifdef i #ifndef, czasem używa się też #if
i funkcji preprocesora defined():
#ifndef __STDLIB_H
lub
#if !defined( __STDLIB_H )
i dalej:
#define __STDLIB_H ... (tutaj deklaracje) #endif
Inne dyrektywy
Znacznie rzadziej są w programach używane dyrektywy takie, jak:
1. #error
- powoduje wyrzucenie błędu kompilacji z podanym jako
argument komunikatem (używane tylko wespół z dyrektywami
warunkowymi),
2. #pragma
- zmienia ustawienia kompilatora (użycie tej dyrektywy
zależy wyłącznie od implementacji)
3. #line
- udaje, że następna linia jest inną linią z innego pliku
Dyrektywa pusta (składająca się tylko ze znaku `#') jest dopuszczalna i
nie daje żadnego efektu
Instrukcje i bloki
Instrukcja prosta jest to wyrażenie występujące w przeznaczonym dla
niego miejscu i zakończone średnikiem. Instrukcja złożona zaś, zwana też
blokiem, jest to jedna lub więcej instrukcji prostych, ujętych w
{ }.
Blok
taki -
proszę pamiętać
-
posiada już osobny, lokalny zasięg
;
toteż
identyfikatory w nim definiowane mają zasięg tylko wewnątrz tego bloku i
mogą "przysłaniać
" (ang. hide)
identyfikatory znajdujące się w wyższym
zasięgu.
No to może tak mały przykład:
Zauważ, że zadeklarowano zmienną w wyrażeniu będącym
argumentem instrukcji
if.
W C++ jest to dopuszczalne, ale proszę się
jednak starać tego nie nadużywać. Zmienna ‘a' jak widać jednak, ma
zasięg tylko dla instrukcji podporządkowanej
if
(jest podana instrukcja
prosta, ale można też podać złożoną, tak jak w każdym przypadku).
Podobnie jest ze wszystkimi tego typu instrukcjami.
Jak widać, mamy trzy różne zmienne x o różnych zasięgach.
Ponieważ zdarza się deklarować zmienne lokalne o takich samych
nazwach, jak globalne, dlatego istnieje operator ::, zwany operatorem
zasięgu. Jego jednoargumentowa (przedrostkowa) postać nakazuje
uzyskać identyfikator z najwyższego zasięgu.
#include <iostream>
using namespace std;
int x = 5;int main()
{ // blok funkcji main
int x = 0; // zmienna lokalna dla bloku funkcji main
{ // lokalny blok
int x = 2; cout << "Wewnętrzna: " << x << endl;
}
cout << "Lokalna: " << x << endl;
cout << "Globalna: " << ::x << endl;
if ( int a = x + 2 > 0 ) cout << a << endl; // instrukcja
podporządkowana if ( ) return 0;}
Operator ::
ten istniał już w
C
, ale tylko jako operator
jednoargumentowy, czyli o takim znaczeniu, jakie tu zostało podane.
Później poznamy ten operator w wersji dwuargumentowej. W
przeciwieństwie do swojego jednoargumentowego przodka, ten
dwuargumentowy jest operatorem jednym z częściej używanych w
C+
+.
Nadal zresztą nazywa się operatorem zasięgu
Instrukcje sterujące
Słowa
kluczowe C++
Program byłby „głupi”, gdyby przebiegał krokowo od początku do
końca. Dlatego też w programie praktycznie zawsze używa się instrukcji
sterujących, na które składają się:
instrukcje odgałęzienia warunkowego: if, else ;
pętle: while, do-while, for
instrukcje przełączające: switch/case;
instrukcje skoku: goto, break, continue;
instrukcja powrotu z funkcji, return.
if
( <warunek> )
{nstr}
else
{instr}
Oczywiście
else
z całą resztą jest opcjonalne.
if
Przejdźmy zatem do pętli. Najprostszą pętlą z wyrażeniem
warunkowym jest pętla
`while
':
while ( <warunek> )
<instr>
Instrukcja ta nakazuje powtarzać <instr> dopóki spełniony jest
<warunek>. Zwraca się uwagę, że <warunek> sprawdzany jest na
początku, a więc przed pierwszym wejściem i każdym następnym
powtórzeniem. Nieco inne możliwości prezentuje nam pętla do-while:
Pętla
while
może nie wykonać się ani razu, jeżeli jej warunek będzie od
początku nieprawdziwy.
do
{
<instr>
}while ( warunek );
Tu <warunek> sprawdzany jest na końcu pętli, toteż pętla wykona się
bezwzględnie co najmniej raz.
Rysunek Działanie przykładowej pętli
do
for
( int i = 0; i < 5; i++ )
{
//instrukcje
….
…..
}
for (;;)
{ // tu coś robimy...
// tu trzeba sprawdzić warunek
// i przerwać... tylko jak? // (dalej)
}
Pętla nieskończona
Do przerywania wykonywania pętli służy instrukcja
break
. Może
być ona używana w dowolnej pętli i powoduje wykonanie skoku do
pierwszej instrukcji za pętlą. Jeśli zależy nam z kolei na skoku na
początek bieżącej pętli, służy do tego celu słowo
continue
. Proszę
pamiętać jednak że słowo
continue
ma zupełnie inne znaczenie dla
pętli
while
i
do-while
, niż dla pętli for! W przypadku tych pierwszych
powoduje normalny skok na początek pętli, podczas gdy w ‘for'
powoduje przejście do następnej iteracji. W praktyce więc skacze do
wyrażenia <next> i dopiero potem jest sprawdzany <warunek>.
switch
( <wartość> )
{
case <jedna_możliwość>: <instr> <instr> ...
case <druga_możliwość>: <instr> <instr> ...
default: <instr> <instr> ...
}
W większości przypadków należy zatem kończyć fragment kodu
rozpoczęty przez
case
instrukcją
break
- gwarantuje to, iż tylko jedna z
możliwości ustalonych w
switch
zostanie wykonana.
Pytanie?, co się stanie, kiedy umieszczę
continue
wewnątrz
switch
(pytanie sugeruje, że switch rozpocznie sprawdzanie od
początku). Najlepsza odpowiedź: po prostu, continue może się
pojawić tylko wewnątrz pętli ;*).
Instrukcja continue w ogóle nie ma związku ze switch
.
Instrukcja przełączająca
switch
porównuje po kolei <wartość> z
kolejnymi wariantami. Etykieta default - jak się zapewne można
domyślać - jest miejscem skoku w przypadku gdy <wartość> nie pasuje
do żadnego wariantu.
Dlaczego radzę się jej wystrzegać? Dlatego, że jest bardzo prosta i
zachęcająca w stosowaniu do tego stopnia, że
większość programujących
zapomina o tym, że bywa powolna
.
Postawowe operacje wejścia/wyjścia; biblioteka
stdio
Ta biblioteka zawiera operacje na plikach, w tym również na konsoli.
Typem danej, na którym się tutaj operuje jest
FILE
. Jest to struktura,
do której nie warto zaglądać (może być na każdym systemie
zdefiniowana inaczej, aczkolwiek nie ma problemu; w C NIE DA się
niczego ukryć). Wiemy tylko tyle, że na niej operuje większość
podanych tu funkcji. Oczywiście należy pamiętać, że ową strukturę
otrzymujemy od funkcji f
open
przy otwieraniu pliku i przekazać
musimy do
fclose
chcąc go zamknąć (oczywiście przez wskaźnik).
Język C/C++ nie ma wbudowanych żadnych instrukcji
umożliwiających wykonywanie operacji wejścia-wyjścia ! Służą
do tego funkcje biblioteczne
.
Funkcje zawarte w bibliotece < io.h >
Dostęp do pliku za pomocą uchwytu (ang. Handle) - operacje niskiego
poziomu
1
. Funkcje otwierania (zwraca uchwyt pliku) oraz zamknięcia pliku
int open
( const char ∗nazwa_pliku, int tryb_dostepu )
int close
( int handle )
2
. Funkcje zapisu i odczytu z pliku
int write
( int handle, void ∗adres_bufora, unsigned ilosc_bajtow )
int read
( int handle, void ∗adres_bufora, unsigned ilosc_bajtow );
3.
Funkcje pomocnicze
int eof
( int handle ) // zwraca 1 gdy „END OF FiILE”
wskaźnika pliku
long tell
( int handle ) // zwraca pozycję
long filelength
( int handle ) // zwraca długosć pliku w bajtach
long lseek
( int handle, long przesuniecie, int względem_czego )
// przesuwa wskaźnik względem zadanego miejsca: pliku o
zadaną ilość
//bajtów
SEEK_SET
- względem początku pliku
SEEK_CUR
- względem aktualnej pozycji
SEEK_END
- względem końca pliku
Przykład
int plik;
char tekst[ ] = ”To jest tekst zapisywany i odczytywany z pliku”;
char znak;
plik = open( ”test.dat”, O_CREAT | O_RDWR );
write( plik, tekst, strlen( tekst ) ); // zapis zawartosci tekstu do
pliku
lseek( plik, 0L, SEEK_SET ); // przesuniecie wskaźnika na
poczatek
do
{ // odczyt po jednym znaku aż do napotkania eof
read( plik, &znak, 1);
printf( ”%c”, znak ); // wydruk odczytanego znaku na ekranie
} while ( !eof( plik ) );
close( plik );
Przede wszystkim, na razie skorzystamy z trzech
podstawowych strumieni plikowych, które są predefiniowane
(jako stałe typu FILE*):
stdin
- "standardowe wejście; //konsola klawiatura
stdout
- "standardowe wyjście„;//konsola -monitor
stderr
- "standardowe wyjście diagnostyczne
"
stdprn
− strumień drukarki
Przy uruchamianiu programów na konsoli, stdin to
wejście z klawiatury, a stdout i stderr są kierowane na
ekran. Oczywiście przy uruchamianiu programu można
dokonać przekierowania, jeśli wywołujemy program przez
polecenie w linii komend ( < dla stdin i > dla stdout;
unixowe powłoki pozwalają też na przekierowanie stderr, np.
w sh i pochodnych jest to 2>).
Funkcje otwarcia (zwraca wskaźnik na FILE) oraz zamknięcia pliku
FILE ∗ fopen
( char ∗nazwa_pliku, char ∗rodzaj_operacji )
rodzaj operacji:--
− r
- tylko do odczytu
−
w
- tylko do zapisu (utworzenie nowego)
−
a
- dopisywanie na końcu
−
+
- z możliwością aktualizacji
−
b
− otwarcie jako plik binarny
−
t
−otwarcie jako plik tekstowy
Przykład
FILE ∗plik
; // utworzenie pliku binarnego z możliwoscia
aktualizacji
plik = fopen
( ”a:\\wyniki.dat”, ”w+b” );
if( plik == NULL ) // kontrola błędów we/wy
{
printf( ”Blad otwarcia pliku wyników” );
return -1
}
int fclose
( FILE ∗strumien ) // zamknięcie wskazanego
strumienia
int fcloseall
(void ) // zamknięcie wszystkich strumieni
Zapis danych do strumienia
int fputc
( int znak, FILE∗ strumien ) // wysłanie
pojedynczego znaku
fputs
( char tekst, FILE strumien ) // int wysłanie
łańcucha znaków
int fprintf
( FILE ∗ strumien, char format, . . . )
// funkcja sformatowanego wyjscia analogiczna do
printf( )
i
nt fwrite
( void adres_w_pamieci,
size_t
rozmiar_bloku,
size_t
ilosc_blokow,
FILE ∗ strumien)
// funkcja kopiujca (ilosc_blokow*rozmiar_bloku)
bajtów
spod wskazanego obszaru pamięci do strumienia
(pliku)
Przykład
#include <stdio.h>
struct T_student
{
char nazwisko[31];
char imie[16];
int wiek;
};
//--------------------------------------------------------
void main( void )
{
FILE *strumien;
T_student baza_danych[10];
int ilosc;
if ( (strumien = fopen( ”test.bin” , ”wb” ) ) != NULL )
{ // zapis zawartości zawartosci bazy ( tablicy struktur) do pliku
binarnego
fwrite( baza_danych, sizeof(T_student), 10 , strumien);
ilosc++;
fclose( strumien );
}
if ( (strumien = fopen( ”test.txt” , ”wt” ) ) != NULL )
{ // zapis zawartości calej bazy ( tablicy struktur) do pliku
tekstowego
for( int i = 0; i < 10; i++ )
fprintf ( strumien, ”%s %s %d \n”, baza_danych[ i ].nazwisko,
baza_danych[ i ].imie, baza_danych [ i ].wiek );
fclose( strumien );
}
}
Ciąg dalszy programu
Funkcje pomocnicze
int feof
( FILE ∗strumien ) // testowanie osiągnięcia końca
pliku
int fseek
( FILE ∗strumien, long przesuniecie, int wzgledem) //
przesuwa
//wskaźnik względem zadanego miejsca:
SEEK_SET
- względem początku pliku
SEEK_CUR
- względem aktualnej pozycji
SEEK_END
- względem końca pliku
long ftell
( FILE ∗strumien ) // zwraca aktualną pozycję
wskaźnika pliku
int fflush
( FILE ∗strumien ) // „wymiata” bufor wskazanego
strumienia
int flushall
( void ) // j.w.dla wszystkich buforowanych
strumieni
Przykład
// funkcja wyznaczająca pozycję maksymalnej liczby double w pliku
binarnym
#include <stdio.h>
long Maksimum( char *nazwa_pliku )
{
FILE *plik_danych;
long pozycja=0, poz_max = -1;
double liczba, maksimum;
if ( (plik_danych = fopen( nazwa_pliku , ”rb” ) ) != NULL )
{
while( fread( &liczba, sizeof(double), 1, plik_danych) == 1)
{
if( pozycja == 0 )
{
maksimum = liczba; poz_max = 0; }
else
if( liczba > maksimum )
{
maksimum = liczba; poz_max = pozycja;
}
pozycja++;
}
fclose( strumien );
}
return poz_max ;
}