Programowanie
w języku C
Programowanie
w języku C
© 2012
Grzegorz Łukawski & Maciej Lasota
Politechnika Świętokrzyska w Kielcach
Wskaźniki i obsługa plików
w języku C
Wskaźnik – to zmienna przechowująca adres,
wskazująca inną zmienną.
Wskaźnik – definicja
1004
99
Zwykłe zmienne i wskaźniki
0
1
2
1000
1004
1008
1012
1016
ADRES
RAM
int z = 99;
int
*
w =
&
z;
Jednoargumentowy operator tworzący wskaźnik na zmienną. Nie można go
stosować do stałych, wyrażeń i zmiennych typu register.
Operatory związane ze wskaźnikami
w =
&
z
z =
*
w
Operator wyłuskania – symbolizuje „adresowanie pośrednie”. Zastosowany
do wskaźnika daje zawartość obiektu wskazywanego przez ten wskaźnik.
Zmienne wskaźnikowe
int
*
wsk
;
// wsk = ??
int
x
=1,
y
=2,
tabela
[10];
wsk
= &
x
;
y
= *
wsk
;
// y = 1
*
wsk
= 0;
// x = 0
wsk
= &
tabela
[3];
Definicja „int *wsk” mówi że „*wsk” jest zmienną typu „int”.
UWAGA!
Wskaźnik zawsze wskazuje na obiekty określonego rodzaju – można to
zmienić przez rzutowanie. Wskaźnik zawsze na coś wskazuje!
Operacje na zmiennych wskaźnikowych
*
wsk
+= 100;
printf
(”%d”, *
wsk
);
y
= *
wsk
* 10;
x
=
tabela
[*
wsk
];
*
wsk
= *
wsk
+ 88;
(*
wsk
)++;
Operacje na zmiennych wskazywanych przez wskaźniki:
Operacje przypisania na wskaźnikach:
int
*
w1
, *
w2
;
int
x
= 30;
w1
= &
x
;
// *w1 = 30
w2
=
w1
;
// *w2 = *w1 = 30
Operacje na zmiennych wskaźnikowych
void
dodaj_czas(
int
*
godz
,
int
*
min
,
int
czas
)
{
*
min
+=
czas
;
if(*
min
> 59) {
*
godz
+= 1;
*
min
-= 60;
}
}
Wskaźnikowe argumenty funkcji
Wartością zmiennej tablicowej jest wskaźnik na zerowy element tej
tablicy, zapis równoważny:
Tablice i wskaźniki
int
tab
[5];
int
*
wsk
,
x
;
wsk
= &
tab
[0];
x
= *
wsk
;
// x = tab[0]
x
= *(
wsk
+1);
// x = tab[1]
Przy dodawaniu liczby do adresu, kolejny element wyznaczany jest
automatycznie (kompilator bierze pod uwagę typ danej na którą
wskazuje dany wskaźnik).
wsk
= &
tab
[0];
<=>
wsk
=
tab
;
Tablicę można traktować jak wskaźnik i vice versa:
Tablice i wskaźniki
*
tab
<=>
tab
[0]
*(
tab
+
i
)
<=>
tab
[i]
wsk
[3]
<=>
*(
wsk
+3)
Nazwa tablicy nie jest zmienną, więc nie można zmieniać jej wartości:
tab
=
wsk
+1;
tab
++;
Wskaźnik jest zmienną, więc można go modyfikować:
wsk
=
tab
+1;
wsk
++;
Tablicowy parametr funkcji wskazuje na zerowy element tablicy. Wiedząc
o tym, do funkcji można przekazać część tablicy, np.:
char
tekst
[] =
”Ala ma kota”
;
puts
(
tekst
+ 4);
puts
(&
tekst
[4]);
Deklaracja parametru funkcji może mieć postać:
int
funkcja
(
int
tab
[]) {…}
int
funkcja
(
int
*
tab
)
{…}
Tablice i wskaźniki jako parametry funkcji
Ujemne indeksy tablic?
int
tablica
[7];
int
*
polowa
= &
tablica
[3];
(…)
polowa
[-3] = 0;
polowa
[-2] = 1;
polowa
[-1] = 2;
polowa
[0] = 3;
polowa
[1] = 4;
(…)
Wartością początkową wskaźnika może być zero (NULL), lub wskaźnik na
już zadeklarowaną zmienną, np.:
int
elementy
[100];
int
*
koniec
= &
elementy
[99];
int
*
pusty
=
NULL
;
Wyjątek – wskaźniki znakowe mogą być inicjowane stałymi napisowymi:
char
*
tekst
=
”Tekst to jest”
;
Język C gwarantuje, że zero nigdy nie będzie prawidłowym adresem. Zero
(NULL) służy do oznaczania „pustych” wskaźników, np.:
if(
pusty
)
puts
(
”Wskaznik OK”
);
else
puts
(
”Wskaznik pusty!”
);
Wartości początkowe wskaźników
Wskaźniki znakowe wzkazują na stałe znakowe zawsze dłuższe o 1 bajt
(zero kończące):
char
t1
[] =
”Abc”
;
// Tablica
char
*
t2
=
”Abc”
;
// Wskaźnik
W przypadku wskaźnika nie wolno zmieniać tekstu (stałej napisowej),
skutek takiego działania jest nieokreślony. Można za to zmienić wskaźnik
i przypisać mu inną stałą napisową, np.:
t2
[2] =
'W'
;
// ZABRONIONE!
t2
=
”Wxyz”
;
// OK
W przypadku tablicy znaków można zmieniać tekst. Nie można przypisać
innej stałej napisowej (tylko z pomocą funkcji strcpy()), np.:
t1
[2] =
'W'
;
// OK
t1
=
”Wxyz”
;
// ZABRONIONE!
strcpy
(
t1
,
”Wxyz”
);
// OK??
Wskaźniki znakowe
Tablica wskaźników
int
i;
char
*
imiona
[] = {
”Ania”
,
”Ala”
,
”Basia”
};
for(
i
=0;
i
< 3;
i
++)
puts
(
imiona
[
i
]);
Tablica wskaźników VS tablica 2-wymiarowa
A B C
0
? ? ?
X Y Z 1 2 3
0
D
0
? ? ? ? ?
A B C
0
X Y Z 1 2 3
0
D
0
char
tab
[][] = {…};
char
*
tab
[] = {…};
#include <stdio.h>
Uchwyt (deskryptor) pliku:
FILE
*
plik
;
Otwarcie pliku:
plik
=
fopen
(
nazwa_pliku
,
tryb_otwarcia
);
Wybrane tryby otwarcia pliku:
”r” - odczyt
”w” - zapis (wyczyszczenie/utworzenie pliku)
”r+” - odczyt z zapisem
”w+” - zapis z odczytem
”rb” - odczyt w trybie binarnym
”wb” - zapis w trybie binarnym
Gdy plik nie jest już potrzebny, należy go zamknąć:
fclose
(
plik
);
Obsługa plików
Odczyt jednego (kolejnego) znaku z pliku:
int
znak
=
fgetc
(
plik
);
Jeżeli osiągnięto koniec pliku, funkcja zwraca stałą EOF:
if(
znak
==
EOF
)
puts
(
”Koniec pliku”
);
Zapis jednego znaku do pliku:
fputc
(
znak
,
plik
);
Odczyt i zapis pojedynczych znaków
Wczytanie jednego wiersza z pliku tekstowego razem ze znakiem
końca linii. Funkcja automatycznie dodaje znak 0 na koniec
wczytanego ciągu. Drugi parametr to długość tablicy na znaki
(maksymalna długość wczytanego ciągu + 1):
char
tekst
[32];
fgets
(
tekst
, 32,
plik
);
Zapis jednego wiersza do pliku, z automatycznym dopisaniem znaku
końca linii:
char
tekst
[] =
”Ala ma kota I psa”
;
fputs
(
tekst
,
plik
);
Odczyt i zapis wierszy tekstu
Odczyt i zapis tekstu z formatowaniem
Funkcje fprintf (zapis) i fscanf (odczyt) formatują tekst zgodnie
z podanym wzorcem, analogicznie do funkcji printf i scanf:
fprintf
(
plik
,
”Wartości x=%d y=%d\n”
,
x
,
y
);
fscanf
(
plik
,
”%d”
, &
n
);
Do odczytu i zapisu bloków danych z/do plików binarnych zaleca się
zastosować jedną z poniższych funkcji:
x
=
fread
(
wsk
,
size
,
nobj
,
plik
);
x
=
fwrite
(
wsk
,
size
,
nobj
,
plik
);
gdzie:
●
wsk
– wskaźnik na miejsce w pamięci (bufor danych);
●
size
– rozmiar jednego bloku danych;
●
nobj
– liczba bloków danych do odczytania/zapisania;
●
plik
– uchwyt pliku;
●
x
– liczba faktycznie odczytanych/zapisanych bloków
danych (do kontroli błędów).
Faktycznie odczytana/zapisania ilość danych to zawsze
size
*
nobj
bajtów!
Odczyt i zapis bloków danych
Odczyt bieżącego położenia wskaźnika odczytu/zapisu w pliku:
off_t
poz
=
ftell
(
plik
);
Zmiana położenia wskaźnika odczytu/zapisu pliku:
fseek
(
plik
,
offset
,
origin
);
gdzie:
offset
– przesunięcie (argument typu off_t);
origin
– względem czego liczone jest przesunięcie:
●
SEEK_SET
– od początku pliku (pierwszy bajt ma indeks 0);
●
SEEK_CUR
– względem aktualnej pozycji pliku;
●
SEEK_END
– względem końca pliku.
Zmiana pozycji w pliku
Obsługa błędów związanych z plikami
Funkcja sygnalizująca wystąpienie błędu – po wystąpieniu jakiegoś
błędu związanego z obsługą plików zwraca wartość różną od zera:
ferror
();
Funkcja sygnalizująca koniec pliku – wtedy zwraca wartość różną
od zera:
feof
(
plik
);
Przykład:
int
z
;
while(!
feof
(
plik
)) {
z
=
fgetc
(
plik
);
putchar
(
z
);
}