Inne darmowe! kompilatory języka C działające m.in. po systemem WINDOWS:

Podobne dokumenty
Biblioteka standardowa - operacje wejścia/wyjścia

Funkcje zawarte w bibliotece < io.h >

Funkcje zawarte w bibliotece < io.h >

Tablice, funkcje - wprowadzenie

Wskaźniki w C. Anna Gogolińska

Wykład VI. Programowanie. dr inż. Janusz Słupik. Gliwice, Wydział Matematyki Stosowanej Politechniki Śląskiej. c Copyright 2014 Janusz Słupik

Tablice (jedno i wielowymiarowe), łańcuchy znaków

Pliki. Informacje ogólne. Obsługa plików w języku C

Wskaźniki. Przemysław Gawroński D-10, p marca Wykład 2. (Wykład 2) Wskaźniki 8 marca / 17

Temat: Dynamiczne przydzielanie i zwalnianie pamięci. Struktura listy operacje wstawiania, wyszukiwania oraz usuwania danych.

Argumenty wywołania programu, operacje na plikach

Wskaźniki do funkcji. Wykład 11. Podstawy programowania ( język C ) Wskaźniki do funkcji (1) Wskaźniki do funkcji (2)

Podstawy programowania. Wykład 6 Wskaźniki. Krzysztof Banaś Podstawy programowania 1

METODY I JĘZYKI PROGRAMOWANIA PROGRAMOWANIE STRUKTURALNE. Wykład 02

Lab 9 Podstawy Programowania

۰ Elementem jednostkowym takiego pliku jest bajt. ۰ Format pliku binarnego: [bajty pliku][eof]

Pliki. Informacje ogólne. Obsługa plików w języku C

DYNAMICZNE PRZYDZIELANIE PAMIECI

Stałe, tablice dynamiczne i wielowymiarowe

wykład III uzupełnienie notatek: dr Jerzy Białkowski Programowanie C/C++ Język C - zarządzanie pamięcią, struktury,

Obsługa plików. Laboratorium Podstaw Informatyki. Kierunek Elektrotechnika. Laboratorium Podstaw Informatyki Strona 1. Kraków 2013

Wskaźniki i dynamiczna alokacja pamięci. Spotkanie 4. Wskaźniki. Dynamiczna alokacja pamięci. Przykłady

Podstawy programowania w języku C++

Ghost in the machine

Co to jest sterta? Sterta (ang. heap) to obszar pamięci udostępniany przez system operacyjny wszystkim działającym programom (procesom).

Program wykonujący operację na plikach powinien zachować schemat działania zapewniający poprawną pracę:

Ćwiczenie 4. Obsługa plików. Laboratorium Podstaw Informatyki. Kierunek Elektrotechnika. Laboratorium Podstaw Informatyki Strona 1.

Podstawy programowania komputerów

Operacje na plikach. Informatyka. Standardowe strumienie wejścia i wyjścia

TABLICE W JĘZYKU C/C++ typ_elementu nazwa_tablicy [wymiar_1][wymiar_2]... [wymiar_n] ;

Podstawy programowania. Wykład Funkcje. Krzysztof Banaś Podstawy programowania 1

Wskaźniki. nie są konieczne, ale dają językowi siłę i elastyczność są języki w których nie używa się wskaźników typ wskaźnikowy typ pochodny:

Programowanie proceduralne INP001210WL rok akademicki 2018/19 semestr letni. Wykład 6. Karol Tarnowski A-1 p.

Wskaźniki. Informatyka

Podstawy programowania skrót z wykładów:

Wstęp do wskaźników w języku ANSI C

Zmienne, stałe i operatory

4. Tablica dwuwymiarowa to jednowymiarowa tablica wskaźników do jednowymiarowych tablic danego typu.

wykład II uzupełnienie notatek: dr Jerzy Białkowski Programowanie C/C++ Język C - funkcje, tablice i wskaźniki wykład II dr Jarosław Mederski Spis

Języki programowania. Przetwarzanie plików amorficznych Konwencja języka C. Część siódma. Autorzy Tomasz Xięski Roman Simiński

JĘZYKI PROGRAMOWANIA Z PROGRAMOWANIEM OBIEKTOWYM. Wykład 6

Poprzedni wykład [ ] :

Programowanie proceduralne INP001210WL rok akademicki 2015/16 semestr letni. Wykład 6. Karol Tarnowski A-1 p.

ISO/ANSI C dostęp do plików ISO/ANSI C. ISO/ANSI C dostęp do plików. ISO/ANSI C dostęp do plików. ISO/ANSI C dostęp do plików

Struktury czyli rekordy w C/C++

Tablice i struktury. czyli złożone typy danych. Programowanie Proceduralne 1

Podstawy programowania. Wykład 7 Tablice wielowymiarowe, SOA, AOS, itp. Krzysztof Banaś Podstawy programowania 1

Struktury. Przykład W8_1

Programowanie w języku C++

Wskaźniki. Programowanie Proceduralne 1

Wstęp do programowania INP001213Wcl rok akademicki 2017/18 semestr zimowy. Wykład 12. Karol Tarnowski A-1 p.

Programowanie w językach wysokiego poziomu

Wstęp do Programowania, laboratorium 02

ISO/ANSI C dostęp do plików ISO/ANSI C. ISO/ANSI C dostęp do plików. ISO/ANSI C dostęp do plików. ISO/ANSI C dostęp do plików

Formatowane (tekstowe) wejście/wyjście. Binarne wejście/wyjście.

Funkcja (podprogram) void

PRZYKŁADY OPERACJI PLIKOWYCH z wykorzystaniem biblioteki <stdio.h>

Języki programowania. Karolina Mikulska-Rumińska Pokój 573, tel Konsultacje wtorek 9-10.

Język ANSI C tablice wielowymiarowe

Podstawy programowania 1

Tablice, funkcje, wskaźniki - wprowadzenie

ISO/ANSI C - funkcje. Funkcje. ISO/ANSI C - funkcje. ISO/ANSI C - funkcje. ISO/ANSI C - funkcje. ISO/ANSI C - funkcje

Programowanie Procedurale. Pliki w języku C++

Stałe i zmienne znakowe. Stała znakowa: znak

1. Wartość, jaką odczytuje się z obszaru przydzielonego obiektowi to: a) I - wartość b) definicja obiektu c) typ oboektu d) p - wartość

main( ) main( void ) main( int argc, char argv[ ] ) int MAX ( int liczba_1, liczba_2, liczba_3 ) źle!

Tablice deklaracja, reprezentacja wewnętrzna

Programowanie w C++ Wykład 5. Katarzyna Grzelak. 16 kwietnia K.Grzelak (Wykład 1) Programowanie w C++ 1 / 27

Języki i metodyka programowania. Wprowadzenie do języka C

W języku C każdy plik fizyczny jest ciągiem bajtów, z których każdy może być niezależnie odczytany. Borland 01234

2 Przygotował: mgr inż. Maciej Lasota

tablica: dane_liczbowe

Poprzedni wykład: - Elementarz formatowanego wejścia/wyjścia (podstawowe. - Sterowanie: instrukcje i bloki; instrukcja warunkowa (decyzje

Język C zajęcia nr 11. Funkcje

Łącza nienazwane(potoki) Łącza nienazwane mogą być używane tylko pomiędzy procesami ze sobą powiązanymi.

ZASADY PROGRAMOWANIA KOMPUTERÓW

Konwersje napis <-> liczba Struktury, unie Scanf / printf Wskaźniki

Podstawy programowania w języku C++

Wykład VII. Programowanie. dr inż. Janusz Słupik. Gliwice, Wydział Matematyki Stosowanej Politechniki Śląskiej. c Copyright 2014 Janusz Słupik

Podstawy programowania w języku C++

Dla każdej operacji łącznie tworzenia danych i zapisu ich do pliku przeprowadzić pomiar czasu wykonania polecenia. Wyniki przedstawić w tabelce.

> C++ wskaźniki. Dane: Iwona Polak. Uniwersytet Śląski Instytut Informatyki 26 kwietnia 2017

Wykład 4: Klasy i Metody

1 Podstawy c++ w pigułce.

7 Przygotował: mgr inż. Maciej Lasota

Programowanie w C++ Wykład 5. Katarzyna Grzelak. 26 marca kwietnia K.Grzelak (Wykład 1) Programowanie w C++ 1 / 40

Języki i metodyka programowania. Typy, operatory, wyrażenia. Wejście i wyjście.

Podstawy Informatyki. Inżynieria Ciepła, I rok. Wykład 10 Kurs C++

PROE wykład 3 klasa string, przeciążanie funkcji, operatory. dr inż. Jacek Naruniec

Wymiar musi być wyrażeniem stałym typu całkowitego, tzn. takim, które może obliczyć kompilator. Przykłady:

Wstęp do programowania 1

Laboratorium 3: Tablice, tablice znaków i funkcje operujące na ciągach znaków. dr inż. Arkadiusz Chrobot dr inż. Grzegorz Łukawski

Podstawy programowania. Wykład Pętle. Tablice. Krzysztof Banaś Podstawy programowania 1

KURS C/C++ WYKŁAD 6. Wskaźniki

Podstawy Programowania. Specyfikacja funkcji, operacje wejścia i wyjścia na plikach, rekurencja, tablice i wskaźniki

Stałe, znaki, łańcuchy znaków, wejście i wyjście sformatowane

1 Podstawy c++ w pigułce.

1. Nagłówek funkcji: int funkcja(void); wskazuje na to, że ta funkcja. 2. Schemat blokowy przedstawia algorytm obliczania

Wstęp do programowania INP001213Wcl rok akademicki 2018/19 semestr zimowy. Wykład 4. Karol Tarnowski A-1 p.

Transkrypt:

Inne darmowe! kompilatory języka C działające m.in. po systemem WINDOWS: IDE (ang. integrated development environment) działające online [ nie trzeba niczego instalować! ]: https://www.onlinegdb.com Wielosystemowe IDE dla C/C++: CLion ( dostępne za darmo dla posiadaczy konta email w domenie uj.edu.pl ): https://www.jetbrains.com/student/ Adam Rycerz [ wyklad08.pdf ] Strona 1 z 36

Poprzedni wykład [ 4.12.2018 ] : - Wskaźniki i tablice; wskaźniki jako argumenty funkcji - Arytmetyka adresów - Funkcje operujące na wskaźnikach znakowych - Wskaźniki plikowe ( FILE * ), funkcje fopen i fclose Adam Rycerz [ wyklad08.pdf ] Strona 2 z 36

Wskaźniki i tablice (esencja ) Po deklaracjach: double x[100], *px; int i=7; i przypisaniu: px = &x[0]; [ lub równoważnie: px = x; ] poprawne ( i równoważne! ) będą wyrażenia: x[i] *(px+i) *(x+i) px[i] oraz instrukcje: px++; px += i; itp. [ arytmetyka wskaźników ] Niepoprawne będą: x++; x+=i; itp. [ nie jest dla tablic! ] [ Po przekazaniu jako argument funkcji, np.: my_fun(char *s); parametr działa (wewnątrz funkcji) już jak zwyczajny wskaźnik, również po wywołaniu dla stałej: my_fun( Stały Napis ); ] Adam Rycerz [ wyklad08.pdf ] Strona 3 z 36

Operacje plikowe (przypomnienie podstaw) Czytamy liczbę całkowitą z pliku tekstowego: FILE *dane; dane = fopen(../mojplik.txt, r ); fscanf(dane, %d,&n); fclose(dane); fopen(nazwa, tryb) wiąże wskaźnik plikowy z plikiem dyskowym; tryby otwarcia: r odczyt, w zapis, a dopisywanie; ponadto: r+, w+, a+ jednoczesne czytanie i pisanie po tym samym pliku. Dla plików binarnych dodajemy literę b w trybie otwarcia: rb, wb, r+b, a+b, itp. Adam Rycerz [ wyklad08.pdf ] Strona 4 z 36

Obok funkcji formatowanego wejścia/wyjścia (fprintf, fscanf), w przypadku plików tekstowych mamy do dyspozycji także funkcje tzw. wierszowego wejścia/wyjścia: fgets(napis, rozmiar, plik) fputs(napis, plik) W obu funkcjach, pierwszym argumentem jest tablica znakowa (lub wskaźnik char * ) zaś ostatnim wskaźnik plikowy. Drugi argument fgets (typu int) to maksymalna l.znaków, które mogą zostać wczytane powiększona o 1 [ na ogół podajemy tutaj rozmiar tablicy napis, aby uniknąć błędu naruszenia ochrony pamięci ]. Adam Rycerz [ wyklad08.pdf ] Strona 5 z 36

Dla plików binarnych działa wejście/wyjście znakowe (funkcje: getc, putc), istnieje także możliwość hurtowego transferu danych np. z pliku do tablicy: FILE *plik=fopen( myfile.dat, rb ); double X[100]; int N; N=fread(X, sizeof(double), 100, plik); /* <== */ fclose(plik); Pierwszy z parametrów fread to wskaźnik ( void * ), drugim jest rozmiar komórki pamięci, trzecim l.elementów do wczytania, a ostatnim wskaźnik plikowy. [ Wartość zwracana to liczba faktycznie wczytanych elementów. ] Adam Rycerz [ wyklad08.pdf ] Strona 6 z 36

Identyczny zestaw parametrów ma komplementarna (do fread) funkcja fwrite. Obliczając rozmiar komórki pamięci użyliśmy operatora sizeof, który (w standardzie ANSI C) może pojawić się w dwóch formach: sizeof obiekt lub sizeof( nazwa-typu ) gdzie obiekt to zmienna skalarna, tablica lub struktura ( => rozmiar obiektu musi być ustalony w momencie kompilacji! ). Przykładowo, stała 100 podana jako trzeci argument fwrite (l.elementów do wczytania) może być zastąpiona przez wyrażenie: sizeof X / sizeof(double) [ Operator sizeof zwraca liczbę całkowitą typu size_t, który jest zdefiniowany w nagłówku <stddef.h>. ] Adam Rycerz [ wyklad08.pdf ] Strona 7 z 36

Jeśli używamy niskopoziomowych funkcji fread, fwrite (np. czytając/pisząc do pliku tyczasowego o trybie dostępu wb+ otwartego funkcją tmpfile() z biblioteki standardowej) zwykle będziemy również używać funkcji rewind( plik ); która przewija plik do początku, lub bardziej uniwersalnej: fseek( plik, przesunięcie, punkt odniesiena ); gdzie przesunięcie podajemy w bajtach, zaś punkt odniesienia można ustalić wybierając jedną z trzech wartości: SEEK_SET początek pliku SEEK_CUR bieżąca pozycja w pliku SEEK_END koniec pliku Adam Rycerz [ wyklad08.pdf ] Strona 8 z 36

Przykładowo: fseek(f, 0, SEEK_SET); będzie równoważne wywołaniu rewind(f); z kolei: fseek(f, 0, SEEK_END); oznacza przesunięcie na koniec pliku. [ Więcej: http://man7.org/linux/man-pages/man3/fseek.3.html ] Z operacjami wejścia/wyjścia często wiąże się potrzeba wywoływania poleceń systemowych. W systemach UNIX/LINUX służy do tego celu funkcja: system( polecenie ) przykładowo, instrukcja: system( date ); wyświetli datę i czas na standardowe wyjście. Adam Rycerz [ wyklad08.pdf ] Strona 9 z 36

Dynamiczny przydział pamięci W języku ANSI C rozmiary tablic muszą być zadane jako stałe lub wyrażenia stałe, np.: int tab[100]; double X[3*3+7]; nieco innym (ale tylko pozornie ) sposobem jest inicjacja tablicy z użyciem obiektu o rozmiarze znanym w chwili kompilacji: char s[] = Napis ; int numbers[] = {1,2,3,4,5}; W przypadku, kiedy rozmiar tablicy jest wynikiem obliczeń niemożliwych (lub niewygodnych) do wykonania przed uruchomieniem programu (a nie chcemy marnować pamięci tworząc na zapas bardzo duże tablice ) ==> stosujemy tzw. dynamiczny przydział pamięci. Adam Rycerz [ wyklad08.pdf ] Strona 10 z 36

Przykładowa sekwencja poleceń: int N; double *tab; scanf( %d,&n); tab = (double*)malloc(n * sizeof(double)); if (NULL == tab) { } printf( Nie udalo sie \n ); return 1; tworzy tablicę N liczb typu double (gdzie N jest czytane ze standardowego wejścia) i wiąże ją ze wskaźnikiem tab. Zamiast funkcji malloc można też użyć: tab = (double*)calloc(n, sizeof(double)); funkcja calloc dodatkowo zeruje tworzoną tablicę. Adam Rycerz [ wyklad08.pdf ] Strona 11 z 36

Kiedy zachodzi potrzeba zwolnienia pamięci używamy: free(tab); Zmiana wielkości już przydzielonej tablicy ( bez zerowania! ): tab = realloc( tab, nowy-rozmiar ); Należy pamiętać, że po dynamicznym przydziale pamięci tab pozostaje wskaźnikiem (<=> nie staje się tablicą! ), a zatem np. działać będzie operator zwiększania: tab++ [ ze wszystkimi tego konsekwencjami ] Z kolei operator: sizeof tab obliczy po prostu rozmiar wskaźnika (na ogół: 4 bajty ). Adam Rycerz [ wyklad08.pdf ] Strona 12 z 36

Przykład tablica trójkątna do przechowywania współczynników dwumianu Newtona: const int N_MAX = 1000; int j, n, *newt[n_max+1]; /* tablica wskaźników */ printf( Podaj n: \n ); scanf( %d,&n); for (j=0; j<=n; j++) { newt[j] = (int*)calloc(j+1, sizeof(int)); if ( NULL == newt[j] ) return 1; } /* Obliczenia ==> ĆWICZENIE! */ Adam Rycerz [ wyklad08.pdf ] Strona 13 z 36

Tablice wskaźników Wskaźniki są także zmiennymi mogą być przechowywane w tablicach. W ostatnim przykładzie tablica wskaźników pojawiła się jako realizacja tablicy dwuwymiarowej. [ ==> c.d.n. ] Znacznie częściej, tablice wskaźników przydają się w sytuacji, gdy chcemy wprowadzić porządek w zbiorze pewnych obiektów ( = posortować ten zbiór ), bez faktycznego przemieszczania ich w pamięci komputera: => wystarczy wówczas jedynie uporządkować (według przyjętej reguły) odpowiednie wskaźniki w tablicy (!) [ Kilka tablic: pozwoli wprowadzić kilka różnych uporządkowań na raz ] Rozważymy teraz przykład porządkowania wierszy tekstu. Adam Rycerz [ wyklad08.pdf ] Strona 14 z 36

Możliwość przemieszczania samych wskaźników eliminuje problemy: (i) zarządzania pamięcią oraz (ii) kosztów przesuwania wierszy tekstu. Proces porządkowania można podzielić na 3 etapy: przeczytaj wiersze z wejścia uporządkuj je (np. alfabetycznie) wypisz uporządkowane wiersze Adam Rycerz [ wyklad08.pdf ] Strona 15 z 36

Program do porządkowania wierszy dzielimy na funkcje, spośród których 3 dostępne dla funkcji main będą realizować: 1. Wczytywanie (oraz liczenie!) kolejnych wierszy; z jednoczesnym budowaniem tablicy wskaźników 2. Porządkowanie alfabetyczne napisów (za pośrednictwem utworzonej tablicy wskaźników) 3. Wypisywanie ustalonej wcześniej liczby wierszy wskazywanych przez kolejne wskaźniki w tablicy. Funkcja wejściowa (por. pkt.1) w praktyce będzie pracować z ograniczoną liczbą wierszy; może zatem zwrócić np. -1 aby zasygnalizować zbyt dużą ilość danych. Adam Rycerz [ wyklad08.pdf ] Strona 16 z 36

/* Porządkowanie wierszy: Wersja 1 bez malloc */ /* wg Kernighan-Ritchie, 1994 */ #include <stdio.h> #include <string.h> #define ALLOCSIZE 500000L /* potrzebna pamiec */ #define MAXLINES 10000 /* maks. liczba wierszy */ char *lineptr[maxlines]; int readlines(char *lineptr[], int maxlines); void writelines(char *lineptr[], int nlines); void qsortlines(char *lineptr[], int left, int right); Adam Rycerz [ wyklad08.pdf ] Strona 17 z 36

int main() { int nlines; if ((nlines = readlines(lineptr, MAXLINES)) >= 0) { qsortlines(lineptr, 0, nlines-1); fprintf(stderr, "==> PO SORTOWANIU: <==\n"); writelines(lineptr, nlines); return 0; } else { /* stderr: pisze ZAWSZE na ekran! */ fprintf(stderr, "ERROR: INPUT TOO BIG TO SORT\n"); return 1; } } Adam Rycerz [ wyklad08.pdf ] Strona 18 z 36

Funkcja wypisująca wiersze adresowane przez kolejne wskaźniki w tablicy ( lineptr[0],, lineptr[nlines-1] ): void writelines(char *lineptr[], int nlines) { while (nlines-- > 0) printf("%s\n", *lineptr++); /* ==> Po każdej linii dopisujemy '\n'! */ } [ W 2. argumencie printf mamy z synonim: nazwa tablicy wskaźników to jednocześnie wskaźnik do wskaźnika do 1. znaku 1.wiersza! ] Adam Rycerz [ wyklad08.pdf ] Strona 19 z 36

Dla porównania: elementarna wersja tej samej funkcji: void writelines(char *lineptr[], int nlines) { int j; } for (j=0; j<nlines; j++) printf("%s\n", lineptr[j]); /* ==> Po każdej linii dopisujemy '\n'! */ Dalej funkcja czytająca wiersze: Adam Rycerz [ wyklad08.pdf ] Strona 20 z 36

int readlines(char *lineptr[], int maxlines) { static char text[allocsize]; /* tablica statyczna! */ int c, nlines=0; long j=0; lineptr[0] = &text[0]; while (EOF!= (c=getchar())) { if (j >= ALLOCSIZE nlines >= maxlines) return -1; else if (c!= '\n') text[j++] = c; else { text[j++] = \0'; nlines++; lineptr[nlines] = &text[j]; /* 1. ZA tablica-ok!*/ } } return nlines; } Adam Rycerz [ wyklad08.pdf ] Strona 21 z 36

Funkcja porządkująca alfabetycznie używa omawianego już kilkakrotnie algorytmu quicksort. Drobne różnice w porównaniu z podanymi wcześniej implementacjami dla różnych tablic liczbowych sprowadzają się do: (1) zastąpienia wyrażenia porównania: (v[i] < v[j]) wywołaniem funkcji strcmp(v[i],v[j]) (zwraca wartość ujemną, jeśli v[i] jest leksykalnie mniejsze od v[j]); oraz (2) oczywistych zmian typów zmiennych. Efektywność porządkowania wynika w dużej mierze z faktu, że niezależnie od tego, jak odległe w pamięci maszyny są porządkowane napisy wskaźniki zebrane w tablicy zawsze zajmować będą ciągły (i stosunkowo mały) blok pamięci; zatem ich przemieszczanie będzie odbywać się szybko. Adam Rycerz [ wyklad08.pdf ] Strona 22 z 36

void qsortlines(char *v[], int left, int right) { int i, last; void swap2lines(char *v[], int i, int j); if (left >= right) return; /* koniec! */ swap2lines(v, left, (left + right)/2); /* wybr. el.*/ last = left; /* ostatni el. < wybrany el. */ for (i=left+1; i <= right; i++) if (strcmp(v[i], v[left]) < 0) swap2lines(v, ++last, i); swap2lines(v, left, last); /* wybrany elem. wraca */ qsortlines(v, left, last-1); qsortlines(v, last+1, right); } Adam Rycerz [ wyklad08.pdf ] Strona 23 z 36

Funkcja zamieniające 2 elementy wymaga drobnej modyfikacji w porównaniu z wersjami z poprzednich wykładów: void swap2lines(char *v[], int i, int j) { char *temp; } temp = v[i]; v[i] = v[j]; v[j] = temp; => Każdy element tablicy v[] (czyli: lineptr[]) to wskaźnik znakowy, zmienna temp również musi mieć taki typ. Adam Rycerz [ wyklad08.pdf ] Strona 24 z 36

Funkcja czytająca wiersze może także zostać napisana nieco inaczej, z wykorzystaniem dynamicznego przydziału pamięci. (a dokładniej funkcji malloc). W tym celu, musimy włączyć w naszym programie dodatkowo nagłówek standardowy: #include <stdlib.h> Definiujemy także maksymalną długość pojedynczego wiersza: #define MAXLEN 1000 ( Definicja ALLOCSIZE przestaje wówczas być potrzebna! ) Adam Rycerz [ wyklad08.pdf ] Strona 25 z 36

int readlines(char *lineptr[], int maxlines) { char s[maxlen+1]; int len, nlines=0; while (fgets(s, MAXLEN+1, stdin)) { len = strlen(s); s[len-1] = '\0'; /* kasujemy '\n' */ lineptr[nlines] = (char*)malloc(len*sizeof(char)); if (nlines>=maxlines NULL==lineptr[nlines]) return -1; strcpy(lineptr[nlines++], s); } return nlines; } Adam Rycerz [ wyklad08.pdf ] Strona 26 z 36

Do czytania pojedynczego wiersza użyliśmy tym razem funkcji: fgets( ) której środkowym argumentem jest zawsze: maksymalna liczba znaków do przeczytania powiększona o 1. Taka konwencja ma związek z potrzebą przechowywania znaku końca napisu ( \0 ); podobnie lokalna (teraz: automatyczna) tablica znakowa, służąca do przechowania ostatnio wczytanego wiersza, jest zadeklarowana jako: char s[maxlen+1]; Po obliczeniu długości wczytanego wiersza len (funkcja strlen), znak końca linii ( \n ) jest zastępowany przez \0 ; w ten sposób skracamy każdy wiersz o jeden znak (dlatego wystarczy przydzielić pamięć na len znaków, a nie len+1!). Adam Rycerz [ wyklad08.pdf ] Strona 27 z 36

Przykładowy wynik uruchomienia programu: $./napisy1.out do re mi fa sol do so la sol. ==> PO SORTOWANIU: <==. do do so la sol fa mi re sol Adam Rycerz [ wyklad08.pdf ] Strona 28 z 36

Pomiary czasów wykonania 2 wesji programu dla dużego pliku: $ time./napisy1.out < AR_dokto.tex >output1.txt ==> PO SORTOWANIU: <==. real 0m0.020s. user 0m0.015s. sys 0m0.003s. $ time./napisy2.out < AR_dokto.tex >output2.txt ==> PO SORTOWANIU: <==. real 0m0.009s. user 0m0.005s. sys 0m0.003s. Identyczność plików po sortowaniu sprawdzamy poleceniem: diff output1.txt output2.txt Adam Rycerz [ wyklad08.pdf ] Strona 29 z 36

Rozmiary plików (źródłowego i wynikowych) użytych w przykładzie: $ wc AR_dokto.tex output* 5025 30153 234448 AR_dokto.tex 5025 30153 234448 output1.txt 5025 30153 234448 output2.txt 15075 90459 703344 total => Czas wykonania wersji 2. (z dynamicznym przydziałem pamięci), na komputerze z procesorem Intel Core i7 2.3GHz, wyniósł 0.005s i okazuje się trzykrotnie krótszy od czasu wykonania wersji 1. (który wyniósł 0.015s). Przyczyną są narzuty czasowe związane z obsługą dużej statycznej tablicy znakowej oraz (częściowo) czytanie danych znak-po-znaku. [ Dalsze przyspieszenie możemy uzyskać np. korzystając z funkcji bibliotecznej qsort zamiast jej własnej wersji => ĆWICZENIE! ] Adam Rycerz [ wyklad08.pdf ] Strona 30 z 36

Wskaźniki na wskaźniki Jak pamiętamy, nazwa tablicy w języku C jest synonimem wskaźnika do pierwszego (=zerowego) elementu. Zasada ta przenosi się w prosty sposób na tablice wskaźników: nazwa takiej tablicy także będzie synonimem wskaźnika do jej pierwszego elementu, a zatem wskaźnika do wskaźnika do komórki pamięci zawierającej daną ustalonego typu. Przykładowo, jeśli po deklaracjach: char *tabptr[n]; char **ptr; wczytamy wiersze do tabptr, a następnie dokonamy przypisania ptr = tabptr; 8. znak 4. wiersza to: tabprt[3][7] oraz *(*(prt+3)+7) Adam Rycerz [ wyklad08.pdf ] Strona 31 z 36

Dla wskaźników na wskaźniki dopuszczalne są wszystkie operacje w ramach arytmetyki wskaźników, np. **ptr++ lub inna: *(*ptr)++ W pierwszym przypadku: wyłuskujemy 1. znak z 1. wiersza i przesuwamy ptr do 1. znaku 2. wiersza; w drugim przypadku: przesuwamy ptr[0] do 2. znaku 1. wiersza, bez modyfikowania ptr[1] ptr[n-1] Dalej, w analogii do zwykłych tablic operacje zwiększania/ zmniejszania będą niedozwolone dla tabptr, ale dla jej elementów składowych ( tabptr[0], tabptr[1], ) już tak. [ Naturalnie, po przekazaniu do funkcji (takiej jak readlines) tablica wskaźników zachowuje się jak wskaźnik na wskaźnik. ] Adam Rycerz [ wyklad08.pdf ] Strona 32 z 36

Tablice wielowymiarowe W języku C dostępne są także tablice dwu- (i więcej) wymiarowe, chociaż używane są zdecydowanie rzadziej niż tablice wskaźników. Przykładowo, w programach kalendarzowych przydać się mogą dwie listy długości miesięcy (dla lat zwykłych i przestępnych): int daytab[2][13] = { {0,31,28,31,30,31,30,31,31,30,31,30,31}, {0,31,29,31,30,31,30,31,31,30,31,30,31} }; Wówczas, po obliczeniu: leap = year%4==0 && year%100!=0 year%400==0; odwołanie: daytab[leap][j] odczyta liczbę dni w j-tym miesiącu danego roku ( year ). [ Miesiące numerujemy od 1! ] Adam Rycerz [ wyklad08.pdf ] Strona 33 z 36

Kiedy tablica dwuwymiarowa jest przekazywana do funkcji, konieczne jest podanie długości wiersza ( = liczby kolumn) w deklaracji parametrów; w naszym przykładzie funkcja f operująca na tablicy daytab może wyglądać tak: lub też tak: albo tak: f(int daytab[2][13]) { }; f(int daytab[][13]) { }; f(int (*daytab)[13]) { }; W ostatnim przypadku deklaracja mówi, że parametr funkcji to wskaźnik do tablicy 13 liczb całkowitych. [ Ogólnie - przekazując tablicę wielowymiarową możemy pominąć tylko pierwszy wymiar, pozostałe muszą być podane jawnie. ] Adam Rycerz [ wyklad08.pdf ] Strona 34 z 36

Inicjowanie tablic wskaźników Poniższa funkcja zwraca napis - nazwę miesiąca o numerze n: char *month_name(int n) { static char *name[] = { Illegal month, January, February, March, April, May, June, July, August, October, November, December }; return (n<1 n>12)? name[0] : name[n]; } [ Podobnie jak przy inicjowaniu tablic stałymi podawanie rozmiarów jest niepotrzebne, kompilator sam je oblicza. ] Adam Rycerz [ wyklad08.pdf ] Strona 35 z 36

Tablice wskaźników a tablice wielowymiarowe Obie poniższe deklaracje: double a[10][20]; double *b[10]; umożliwiają działania na dwuwymiarowej tablicy liczbowej (macierzy!) zawierającej 10 wierszy. Zmienna a jest jednak prawdziwą tablicą: 10 x 20 = 200 liczb typu double zajmujących ciągły obszar pamięci (pozycja elementu [wiersz][kolumna] to po prostu 20 x wiersz + kolumna ). [ => Konieczność ustalania dł. wiersza przy przekazywaniu t. do funkcji! ] W drugim przypadku: zmienna b to tablica 10 wskaźników, które muszą zostać powiązane z tablicami jednowymiarowymi gdzieś dalej w programie ( => wiersze mogą być różnej długości! ) Adam Rycerz [ wyklad08.pdf ] Strona 36 z 36