Progr
Programowanie
nie
w jęz
w ję yku C
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ą.
ADRES
RAM
0
1
2
int z = 99;
int *w = &z;
1000
1004
99
1008
1012
1016 1004
Operatory związane ze wskaźnikami w = &z
Jednoargumentowy operator tworzący wskaźnik na zmienną. Nie można go stosować do stałych, wyrażeń i zmiennych typu register.
z = *w
Operator wyłuskania – symbolizuje „adresowanie pośrednie”. Zastosowany do wskaźnika daje zawartość obiektu wskazywanego przez ten wskaźnik.
Definicja „int *wsk” mówi że „*wsk” jest zmienną typu „int” .
int *wsk;
// wsk = ??
int x=1, y=2, tabela[10];
wsk = &x;
y = *wsk;
// y = 1
*wsk = 0;
// x = 0
wsk = &tabela[3];
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 Operacje na zmiennych wskazywanych przez wskaźniki:
*wsk += 100;
printf(”%d”, *wsk);
y = *wsk * 10;
x = tabela[*wsk];
*wsk = *wsk + 88;
(*wsk)++;
Operacje na zmiennych wskaźnikowych Operacje przypisania na wskaźnikach:
int *w1, *w2;
int x = 30;
w1 = &x;
// *w1 = 30
w2 = w1;
// *w2 = *w1 = 30
Wskaźnikowe argumenty funkcji void dodaj_czas(int *godz, int *min, int czas)
{
*min += czas;
if(*min > 59) {
*godz += 1;
*min -= 60;
}
}
int tab[5];
int *wsk, x;
wsk = &tab[0];
x = *wsk;
// x = tab[0]
x = *(wsk+1);
// x = tab[1]
Wartością zmiennej tablicowej jest wskaźnik na zerowy element tej tablicy, zapis równoważny:
wsk = &tab[0];
<=>
wsk = tab;
Przy dodawaniu liczby do adresu, kolejny element wyznaczany jest automatycznie (kompilator bierze pod uwagę typ danej na którą wskazuje dany wskaźnik).
Tablicę można traktować jak wskaźnik i vice versa:
*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++;
Tablice i wskaźniki jako parametry funkcji 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)
{…}
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ątkowe wskaźników 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!”);
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??
int i;
char *imiona[] = {”Ania”, ”Ala”, ”Basia”};
for(i=0;i < 3;i++)
puts(imiona[i]);
Tablica wskaźników VS tablica 2-wymiarowa char tab[][] = {…};
A B C 0 ? ? ? X Y Z 1 2 3 0 D 0 ? ? ? ? ?
char *tab[] = {…};
A B C 0
X Y Z 1 2 3 0
D 0
#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);
Odczyt i zapis pojedynczych znakó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 wierszy tekstu 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 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);
Odczyt i zapis bloków danych 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 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.
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);
}