Cyfroteka.pl

klikaj i czytaj online

Cyfro
Czytomierz
00560 006860 13265793 na godz. na dobę w sumie
Wskaźniki w języku C. Przewodnik - książka
Wskaźniki w języku C. Przewodnik - książka
Autor: Liczba stron: 256
Wydawca: Helion Język publikacji: polski
ISBN: 978-83-246-8289-8 Data wydania:
Lektor:
Kategoria: ebooki >> komputery i informatyka >> programowanie >> c - programowanie
Porównaj ceny (książka, ebook, audiobook).

Jeśli chcesz błyskawicznie opanować programowanie w języku C, sięgnij po tę książkę! Gdy już poznasz podstawy, nauczysz się także korzystać ze wskaźników. To prawdziwa zmora wszystkich programistów, bowiem błędne wykorzystanie wskaźnika może w okamgnieniu zrujnować Twój program. Zobacz, jak tego uniknąć i zaprzyjaźnić się ze wskaźnikami.

Inne książki opisują wskaźniki w jednym lub dwu rozdziałach, natomiast my poświęciliśmy im całą książkę. Dzięki temu dogłębnie poznasz ten mechanizm, zrozumiesz go i przekonasz się, że przy odrobinie uwagi nie jest on wcale taki straszny! W trakcie lektury wykorzystasz wskaźniki na funkcję, przygotujesz tablicę wskaźników oraz zobaczysz, jak współdziałają one z łańcuchami znaków. Twoją uwagę z pewnością zwrócą fragmenty omawiające zabezpieczenia oraz niewłaściwe wykorzystanie wskaźników. Książka ta jest jedyną pozycją na rynku w całości poświęconą wskaźnikom w języku C. To lektura obowiązkowa każdego programisty!

Poznaj:

Odkryj tajniki wskaźników w języku C i wykorzystaj ich potencjał!

Znajdź podobne książki Ostatnio czytane w tej kategorii

Darmowy fragment publikacji:

Tytuł oryginału: Understanding and Using C Pointers Tłumaczenie: Konrad Matuk ISBN: 978-83-246-8289-8 © 2014 Helion S.A. Authorized Polish translation of the English edition of Understanding and Using C Pointers ISBN 9781449344184 © 2013 Richard Reese, Ph.D. This translation is published and sold by permission of O’Reilly Media, Inc., which owns or controls all rights to sell the same. All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from the Publisher. Wszelkie prawa zastrzeżone. Nieautoryzowane rozpowszechnianie całości lub fragmentu niniejszej publikacji w jakiejkolwiek postaci jest zabronione. Wykonywanie kopii metodą kserograficzną, fotograficzną, a także kopiowanie książki na nośniku filmowym, magnetycznym lub innym powoduje naruszenie praw autorskich niniejszej publikacji. Wszystkie znaki występujące w tekście są zastrzeżonymi znakami firmowymi bądź towarowymi ich właścicieli. Autor oraz Wydawnictwo HELION dołożyli wszelkich starań, by zawarte w tej książce informacje były kompletne i rzetelne. Nie biorą jednak żadnej odpowiedzialności ani za ich wykorzystanie, ani za związane z tym ewentualne naruszenie praw patentowych lub autorskich. Autor oraz Wydawnictwo HELION nie ponoszą również żadnej odpowiedzialności za ewentualne szkody wynikłe z wykorzystania informacji zawartych w książce. Wydawnictwo HELION ul. Kościuszki 1c, 44-100 GLIWICE tel. 32 231 22 19, 32 230 98 63 e-mail: helion@helion.pl WWW: http://helion.pl (księgarnia internetowa, katalog książek) Drogi Czytelniku! Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres http://helion.pl/user/opinie/wskazc Możesz tam wpisać swoje uwagi, spostrzeżenia, recenzję. Printed in Poland. • Kup książkę • Poleć książkę • Oceń książkę • Księgarnia internetowa • Lubię to! » Nasza społeczność Spis tre(cid:316)ci Przedmowa .................................................................................................9 Wska(cid:274)niki i pami(cid:246)(cid:232) 1. Wst(cid:253)p ............................................................................................. 15 16 17 20 22 23 24 Dlaczego warto opanowa(cid:232) wska(cid:274)niki Deklarowanie wska(cid:274)ników Interpretowanie deklaracji Operator adresu Wy(cid:264)wietlanie warto(cid:264)ci wska(cid:274)ników Wy(cid:228)uskiwanie wska(cid:274)nika za pomoc(cid:241) operatora adresowania po(cid:264)redniego Wska(cid:274)niki na funkcje Poj(cid:246)cie warto(cid:264)ci null Rodzaje wska(cid:274)ników i ich rozmiary Modele pami(cid:246)ci Predefiniowane typy zwi(cid:241)zane ze wska(cid:274)nikami Operatory wska(cid:274)ników Arytmetyka wska(cid:274)nikowa Porównywanie wska(cid:274)ników Zastosowania wska(cid:274)ników Wielopoziomowe adresowanie po(cid:264)rednie Sta(cid:228)e i wska(cid:274)niki Podsumowanie 26 27 27 32 32 33 37 37 42 42 43 44 50 Dynamiczna alokacja pami(cid:246)ci Wycieki pami(cid:246)ci 2. C i dynamiczne zarz(cid:233)dzanie pami(cid:253)ci(cid:233) .......................................... 51 52 55 57 58 62 Stosowanie funkcji malloc Stosowanie funkcji calloc Funkcje dynamicznego alokowania pami(cid:246)ci 3 Kup książkęPoleć książkę Wisz(cid:241)ce wska(cid:274)niki Dealokacja pami(cid:246)ci przy u(cid:276)yciu funkcji free Stosowanie funkcji realloc Funkcja alloca i tablice o zmiennej d(cid:228)ugo(cid:264)ci Przypisywanie warto(cid:264)ci NULL do zwalnianego wska(cid:274)nika Podwójne uwalnianie Sterta i pami(cid:246)(cid:232) systemowa Zwalnianie pami(cid:246)ci po zako(cid:254)czeniu dzia(cid:228)ania programu 63 66 66 68 68 70 70 71 71 Przyk(cid:228)ady wisz(cid:241)cych wska(cid:274)ników Rozwi(cid:241)zywanie problemu wisz(cid:241)cych wska(cid:274)ników 74 Stosowanie wersji testowej do wykrywania wycieków pami(cid:246)ci 74 75 76 76 77 78 Sprz(cid:241)tanie pami(cid:246)ci w j(cid:246)zyku C Inicjowanie przy pozyskaniu zasobu (RAII) Korzystanie z procedury obs(cid:228)ugi wyj(cid:241)tków Techniki dynamicznej alokacji pami(cid:246)ci Podsumowanie Sterta i stos programu Przekazywanie i zwracanie za pomoc(cid:241) wska(cid:274)nika Stos programu Organizacja ramki stosu Stosowanie wska(cid:274)ników do przekazywania danych Przekazywanie danych poprzez warto(cid:264)(cid:232) Przekazywanie wska(cid:274)nika do sta(cid:228)ej Zwracanie wska(cid:274)nika Wska(cid:274)niki do danych lokalnych Przekazywanie pustych wska(cid:274)ników Przekazywanie wska(cid:274)nika do wska(cid:274)nika 3. Wska(cid:346)niki i funkcje ........................................................................79 80 80 81 84 84 85 86 87 89 91 91 95 96 97 99 99 100 101 102 103 Deklarowanie wska(cid:274)ników na funkcj(cid:246) Stosowanie wska(cid:274)ników na funkcj(cid:246) Przekazywanie wska(cid:274)ników na funkcj(cid:246) Zwracanie wska(cid:274)ników na funkcj(cid:246) Stosowanie tablic wska(cid:274)ników na funkcj(cid:246) Porównywanie wska(cid:274)ników na funkcj(cid:246) Rzutowanie wska(cid:274)ników na funkcj(cid:246) Wska(cid:274)niki na funkcj(cid:246) Podsumowanie 4 (cid:95) Spis tre(cid:316)ci Kup książkęPoleć książkę Tablice Notacja wska(cid:274)nikowa i tablice Ró(cid:276)nice pomi(cid:246)dzy tablicami a wska(cid:274)nikami Tablice jednowymiarowe Tablice dwuwymiarowe Tablice wielowymiarowe 4. Wska(cid:346)niki i tablice ....................................................................... 105 106 107 108 109 109 112 Stosowanie funkcji malloc do tworzenia tablic jednowymiarowych 113 114 Stosowanie funkcji realloc do zmiany rozmiaru tablicy 118 Przekazywanie tablicy jednowymiarowej 118 119 120 122 125 128 129 129 131 135 Stosowanie jednowymiarowych tablic wska(cid:274)ników Wska(cid:274)niki i tablice wielowymiarowe Przekazywanie tablicy wielowymiarowej Dynamiczna alokacja tablicy dwuwymiarowej Alokowanie pami(cid:246)ci o potencjalnie nieci(cid:241)g(cid:228)ym obszarze Alokacja pami(cid:246)ci o ci(cid:241)g(cid:228)ym obszarze Tablice postrz(cid:246)pione i wska(cid:274)niki Podsumowanie Stosowanie notacji tablicowej Stosowanie notacji wska(cid:274)nikowej Podstawowe wiadomo(cid:264)ci na temat wska(cid:274)ników Standardowe operacje wykonywane na (cid:228)a(cid:254)cuchach Deklaracja (cid:228)a(cid:254)cucha Pula litera(cid:228)ów (cid:228)a(cid:254)cuchowych Inicjalizacja (cid:228)a(cid:254)cucha 5. Wska(cid:346)niki i (cid:293)a(cid:295)cuchy ................................................................... 137 138 139 139 141 145 145 147 149 153 153 155 155 157 Przekazywanie prostego (cid:228)a(cid:254)cucha Przekazywanie wska(cid:274)nika na sta(cid:228)(cid:241) typu char Przekazywanie wska(cid:274)nika wymagaj(cid:241)cego inicjalizacji Przekazywanie argumentów do aplikacji Porównywanie (cid:228)a(cid:254)cuchów Kopiowanie (cid:228)a(cid:254)cuchów (cid:227)(cid:241)czenie (cid:228)a(cid:254)cuchów Przekazywanie (cid:228)a(cid:254)cuchów Spis tre(cid:316)ci (cid:95) 5 Kup książkęPoleć książkę Zwracanie (cid:228)a(cid:254)cuchów Zwracanie adresu litera(cid:228)u Zwracanie adresu pami(cid:246)ci adresowanej dynamicznie Wska(cid:274)niki na funkcje i (cid:228)a(cid:254)cuchy Podsumowanie 158 158 160 162 165 Wst(cid:246)p Alokacja struktury w pami(cid:246)ci 6. Wska(cid:346)niki i struktury ................................................................... 167 168 169 Zagadnienia zwi(cid:241)zane z dealokacj(cid:241) struktury 170 Unikanie narzutu wynikaj(cid:241)cego ze stosowania funkcji malloc i free 174 176 Stosowanie wska(cid:274)ników do obs(cid:228)ugi struktur danych 177 185 188 190 194 Jednostronna lista powi(cid:241)zana Stosowanie wska(cid:274)ników do obs(cid:228)ugi kolejek Stosowanie wska(cid:274)ników do obs(cid:228)ugi stosu Stosowanie wska(cid:274)ników do obs(cid:228)ugi drzewa Podsumowanie 7. Problemy z zabezpieczeniami i niew(cid:293)a(cid:316)ciwe stosowanie wska(cid:346)ników ..................................... 195 197 Deklaracja i inicjalizacja wska(cid:274)ników 197 Niew(cid:228)a(cid:264)ciwa deklaracja wska(cid:274)nika Niepowodzenie inicjalizacji wska(cid:274)nika przed u(cid:276)yciem 198 Rozwi(cid:241)zywanie problemów z niezainicjalizowanymi wska(cid:274)nikami Problemy wynikaj(cid:241)ce ze stosowania wska(cid:274)ników Wykrywanie warto(cid:264)ci zerowej Niew(cid:228)a(cid:264)ciwe stosowanie operatora wy(cid:228)uskiwania Wisz(cid:241)ce wska(cid:274)niki Uzyskiwanie dost(cid:246)pu do pami(cid:246)ci znajduj(cid:241)cej si(cid:246) poza granicami tablicy B(cid:228)(cid:246)dne obliczenie rozmiaru tablicy Niew(cid:228)a(cid:264)ciwe stosowanie operatora sizeof Zawsze dopasowuj do siebie typy wska(cid:274)ników Wska(cid:274)niki ograniczone Problemy z zabezpieczeniami zwi(cid:241)zane z (cid:228)a(cid:254)cuchami Arytmetyka wska(cid:274)nikowa i struktury Problemy zwi(cid:241)zane ze wska(cid:274)nikami na funkcj(cid:246) 6 (cid:95) Spis tre(cid:316)ci 198 199 200 201 201 202 203 203 204 205 206 207 209 Kup książkęPoleć książkę Problemy zwi(cid:241)zane z dealokacj(cid:241) pami(cid:246)ci Dublowanie funkcji free Czyszczenie danych wra(cid:276)liwych Stosowanie narz(cid:246)dzi analizy statycznej Podsumowanie 211 211 211 212 213 Rzutowanie wska(cid:274)ników 8. Pozosta(cid:293)e techniki ....................................................................... 215 216 Uzyskiwanie dost(cid:246)pu do adresu specjalnego przeznaczenia 217 219 Uzyskiwanie dost(cid:246)pu do portu Uzyskiwanie dost(cid:246)pu do pami(cid:246)ci przy u(cid:276)yciu DMA 220 220 Okre(cid:264)lanie porz(cid:241)dku bajtów danej maszyny 221 Aliasing wska(cid:274)ników i s(cid:228)owo kluczowe restrict 223 225 226 227 228 Stosowanie unii do reprezentacji warto(cid:264)ci na ró(cid:276)ne sposoby Strict aliasing Stosowanie s(cid:228)owa kluczowego restrict W(cid:241)tki i wska(cid:274)niki Wspó(cid:228)dzielenie wska(cid:274)ników przez w(cid:241)tki Stosowanie wska(cid:274)ników na funkcj(cid:246) do obs(cid:228)ugi wywo(cid:228)a(cid:254) zwrotnych Techniki obiektowe Tworzenie i stosowanie wska(cid:274)ników nieprze(cid:274)roczystych Polimorfizm w j(cid:246)zyku C Podsumowanie 231 233 233 237 242 Skorowidz ...............................................................................................243 Spis tre(cid:316)ci (cid:95) 7 Kup książkęPoleć książkę 8 (cid:95) Spis tre(cid:316)ci Kup książkęPoleć książkę ROZDZIA(cid:292) 1. Wst(cid:253)p Dobre opanowanie wska(cid:274)ników i umiej(cid:246)tno(cid:264)(cid:232) ich efektywnego stosowania odró(cid:276)niaj(cid:241) zaawansowanego programist(cid:246) od nowicjusza. Wska(cid:274)niki s(cid:241) wa(cid:276)- nym elementem j(cid:246)zyka C, zapewniaj(cid:241)cym mu elastyczno(cid:264)(cid:232). Umo(cid:276)liwiaj(cid:241) obs(cid:228)ug(cid:246) dynamicznej alokacji pami(cid:246)ci. Ich zapis jest zwi(cid:241)zany z zapisem tablic. Wska(cid:274)niki na funkcj(cid:246) daj(cid:241) ogromne mo(cid:276)liwo(cid:264)ci kontroli wykony- wania programu. Wska(cid:274)niki od dawna s(cid:241) g(cid:228)ówn(cid:241) przeszkod(cid:241) w nauce j(cid:246)zyka C. W za(cid:228)o(cid:276)e- niu wska(cid:274)nik jest zmienn(cid:241) przechowuj(cid:241)c(cid:241) adres pami(cid:246)ci. Ten prosty ele- ment zaczyna wydawa(cid:232) si(cid:246) skomplikowany, gdy zaczynamy stosowa(cid:232) operatory wska(cid:274)ników lub gdy próbujemy zrozumie(cid:232) ich skomplikowan(cid:241) notacj(cid:246). Takie problemy nie musz(cid:241) grozi(cid:232) ka(cid:276)demu. Je(cid:276)eli dobrze opanujesz proste podstawy wska(cid:274)ników, wtedy ich zaawansowane zastosowania wcale nie s(cid:241) trudne do opanowania. Kluczem do opanowania wska(cid:274)ników jest zrozumienie tego, jak program napisany w j(cid:246)zyku C zarz(cid:241)dza pami(cid:246)ci(cid:241) — wska(cid:274)niki zawieraj(cid:241) adresy ko- mórek pami(cid:246)ci. Je(cid:276)eli nie zrozumiesz tego, jak zorganizowana jest pami(cid:246)(cid:232), trudno b(cid:246)dzie Ci poj(cid:241)(cid:232), jak dzia(cid:228)aj(cid:241) wska(cid:274)niki. W celu u(cid:228)atwienia nauki ksi(cid:241)(cid:276)ka zawiera rysunki ilustruj(cid:241)ce organizacj(cid:246) pami(cid:246)ci. Umieszczono je wsz(cid:246)dzie tam, gdzie mog(cid:241) okaza(cid:232) si(cid:246) pomocne w wyja(cid:264)nianiu dzia(cid:228)ania wska(cid:274)ników. Gdy ju(cid:276) zrozumiesz organizacj(cid:246) pami(cid:246)ci, zg(cid:228)(cid:246)bianie zagad- nie(cid:254) zwi(cid:241)zanych ze wska(cid:274)nikami stanie si(cid:246) o wiele (cid:228)atwiejsze. Poni(cid:276)szy rozdzia(cid:228) zawiera wst(cid:246)pne informacje na temat wska(cid:274)ników, ich operatorów oraz tego, jak wska(cid:274)niki oddzia(cid:228)uj(cid:241) na pami(cid:246)(cid:232). Pierwsza cz(cid:246)(cid:264)(cid:232) rozdzia(cid:228)u opisuje deklaracje wska(cid:274)ników, podstawowe operatory wska(cid:274)- ników, a tak(cid:276)e poj(cid:246)cie warto(cid:264)ci typu null. Istnieje wiele rodzajów warto(cid:264)ci typu null obs(cid:228)ugiwanych przez j(cid:246)zyk C. Dok(cid:228)adne przeanalizowanie tego zagadnienia mo(cid:276)e okaza(cid:232) si(cid:246) przydatne. 15 Kup książkęPoleć książkę Drugi podrozdzia(cid:228) opisuje ró(cid:276)norakie modele pami(cid:246)ci, które z pewno(cid:264)ci(cid:241) napotkasz, pracuj(cid:241)c w j(cid:246)zyku C. Model stosowany przez dany kompilator lub system operacyjny wp(cid:228)ywa na dzia(cid:228)anie wska(cid:274)ników. Ponadto w tym podrozdziale spotkasz si(cid:246) z analiz(cid:241) ró(cid:276)nych predefiniowanych typów zwi(cid:241)- zanych ze wska(cid:274)nikami i modelami pami(cid:246)ci. W kolejnym podrozdziale opisano bardziej szczegó(cid:228)owo operatory wska(cid:274)- ników. Dowiesz si(cid:246) wielu przydatnych rzeczy na temat arytmetyki wska(cid:274)- ników oraz ich porównywania. Ostatni podrozdzia(cid:228) opisuje problematyk(cid:246) zwi(cid:241)zan(cid:241) ze sta(cid:228)ymi i wska(cid:274)nikami. Liczne kombinacje deklaracji oferuj(cid:241) wiele interesuj(cid:241)cych, a zarazem przydatnych mo(cid:276)liwo(cid:264)ci. Niezale(cid:276)nie od tego, czy jeste(cid:264) pocz(cid:241)tkuj(cid:241)cym, czy zaawansowanym pro- gramist(cid:241) j(cid:246)zyka C, niniejsza ksi(cid:241)(cid:276)ka pomo(cid:276)e Ci zrozumie(cid:232) wska(cid:274)niki i wy- pe(cid:228)ni(cid:232) braki w wiedzy. Zaawansowany programista mo(cid:276)e czyta(cid:232) t(cid:246) publikacj(cid:246) selektywnie, wybieraj(cid:241)c tylko te tematy, które go interesuj(cid:241). Pocz(cid:241)tkuj(cid:241)cy programista powinien ostro(cid:276)nie zapozna(cid:232) si(cid:246) z ca(cid:228)o(cid:264)ci(cid:241) ksi(cid:241)(cid:276)ki. Wska(cid:346)niki i pami(cid:253)(cid:235) Program napisany w j(cid:246)zyku C po skompilowaniu wykorzystuje trzy rodzaje pami(cid:246)ci: Statyczna/globalna W tym rodzaju pami(cid:246)ci alokowane s(cid:241) zmienne deklarowane statycznie. Zmienne globalne tak(cid:276)e korzystaj(cid:241) z tej pami(cid:246)ci. S(cid:241) one alokowane w pa- mi(cid:246)ci od chwili rozpocz(cid:246)cia dzia(cid:228)ania programu a(cid:276) do jego zamkni(cid:246)cia. Wszystkie funkcje maj(cid:241) dost(cid:246)p do zmiennych globalnych. Zasi(cid:246)g zmien- nych statycznych jest ograniczony do funkcji, w której zosta(cid:228)y one zde- finiowane. Automatyczna Zmienne te s(cid:241) deklarowane wewn(cid:241)trz funkcji, a wi(cid:246)c s(cid:241) tworzone w chwili wywo(cid:228)ania funkcji. Ich zasi(cid:246)g jest ograniczony do funkcji. Okres istnienia tych zmiennych jest ograniczony do czasu wykonywania funkcji. Dynamiczna Pami(cid:246)(cid:232) jest alokowana na stercie i mo(cid:276)e zosta(cid:232) zwolniona, gdy b(cid:246)dzie to konieczne. Wska(cid:274)nik odnosi si(cid:246) do alokowanej pami(cid:246)ci. Zasi(cid:246)g zmien- nych jest ograniczony przez wska(cid:274)niki odnosz(cid:241)ce si(cid:246) do tej pami(cid:246)ci. Pami(cid:246)(cid:232) ta istnieje do momentu jej zwolnienia. Problematyka ta zosta(cid:228)a szerzej opisana w rozdziale 2. 16 (cid:95) Rozdzia(cid:293) 1. Wst(cid:253)p Kup książkęPoleć książkę Tabela 1.1 podsumowuje wiadomo(cid:264)ci dotycz(cid:241)ce zasi(cid:246)gu i okresu istnienia zmiennych stosowanych w poszczególnych obszarach pami(cid:246)ci. Tabela 1.1. Zasi(cid:246)g i okres istnienia Globalna Statyczna Automatyczna (lokalna) Dynamiczna Zasi(cid:253)g ca(cid:293)y plik funkcja, w której zosta(cid:293)a zadeklarowana funkcja, w której zosta(cid:293)a zadeklarowana okre(cid:316)lony przez wska(cid:346)niki odnosz(cid:233)ce si(cid:253) do tej pami(cid:253)ci Okres istnienia ca(cid:293)y okres dzia(cid:293)ania aplikacji ca(cid:293)y okres dzia(cid:293)ania aplikacji czas wykonywania funkcji do momentu zwolnienia pami(cid:253)ci Bli(cid:276)sze zapoznanie si(cid:246) z tymi rodzajami pami(cid:246)ci pozwoli Ci na lepsze zro- zumienie funkcjonowania wska(cid:274)ników. W wi(cid:246)kszo(cid:264)ci przypadków wska(cid:274)- niki s(cid:241) stosowane do wykonywania operacji na danych przechowywanych w pami(cid:246)ci. Zinterpretowanie tego, jak pami(cid:246)(cid:232) jest partycjonowana i organi- zowana, pomo(cid:276)e w wyja(cid:264)nieniu dzia(cid:228)a(cid:254) wykonywanych na pami(cid:246)ci przez wska(cid:274)niki. Zmienna b(cid:246)d(cid:241)ca wska(cid:274)nikiem zawiera adres pami(cid:246)ci, pod którym znaj- duje si(cid:246) inna zmienna, obiekt lub funkcja. Obiekt jest alokowany w pami(cid:246)ci za pomoc(cid:241) funkcji alokuj(cid:241)cej, takiej jak np. funkcja malloc. Zwykle dekla- ruje si(cid:246) typ wska(cid:274)nika, który zale(cid:276)y od tego, na co dany wska(cid:274)nik wska- zuje. Np. mo(cid:276)emy zadeklarowa(cid:232) wska(cid:274)nik na obiekt typu char. Obiektem mo(cid:276)e by(cid:232) liczba ca(cid:228)kowita, znak, (cid:228)a(cid:254)cuch, struktura lub dowolny inny typ danych spotykany w j(cid:246)zyku C. Wska(cid:274)nik nie zawiera niczego, co by in- formowa(cid:228)o o tym, na jaki typ danych wskazuje. Wska(cid:274)nik zawiera tylko adres danych. Dlaczego warto opanowa(cid:235) wska(cid:346)niki Wska(cid:274)niki mo(cid:276)na stosowa(cid:232) do: (cid:120) tworzenia szybkiego i wydajnego kodu, (cid:120) rozwi(cid:241)zywania w prosty sposób ró(cid:276)nego typu problemów, (cid:120) obs(cid:228)ugi dynamicznej alokacji pami(cid:246)ci, (cid:120) tworzenia zwi(cid:246)z(cid:228)ych wyra(cid:276)e(cid:254), (cid:120) przekazywania struktur danych bez ponoszenia kosztów w postaci narzutu, (cid:120) ochrony danych przekazywanych do funkcji jako parametry. Wska(cid:346)niki i pami(cid:253)(cid:235) (cid:95) 17 Kup książkęPoleć książkę Logika dzia(cid:228)ania wska(cid:274)ników jest bliska zasadzie funkcjonowania kom- putera, a wi(cid:246)c mo(cid:276)liwe jest dzi(cid:246)ki nim tworzenie szybszego i bardziej wy- dajnego kodu. To znaczy, (cid:276)e kompilator jest w stanie sprawniej prze(cid:228)o(cid:276)y(cid:232) operacje na kod maszynowy. Korzystaj(cid:241)c ze wska(cid:274)ników, tworzymy mniej narzutu ni(cid:276) w przypadku korzystania z innych operatorów. Przy u(cid:276)yciu wska(cid:274)ników mo(cid:276)liwa jest o wiele (cid:228)atwiejsza implementacja wielu struktur danych. Np. lista powi(cid:241)zana mo(cid:276)e by(cid:232) obs(cid:228)ugiwana za pomoc(cid:241) za- równo tablic, jak i wska(cid:274)ników, ale stosuj(cid:241)c wska(cid:274)niki, mo(cid:276)na (cid:228)atwiej od- wo(cid:228)ywa(cid:232) si(cid:246) bezpo(cid:264)rednio do nast(cid:246)pnego lub wcze(cid:264)niejszego powi(cid:241)zania. Wykonanie tej samej operacji przy u(cid:276)yciu tablic wymaga korzystania z indek- sów tablic, co nie jest tak intuicyjne i wygodne jak stosowanie wska(cid:274)ników. Rysunek 1.1 obrazuje korzystanie z listy powi(cid:241)zanych elementów (listy pracowników) przy u(cid:276)yciu wska(cid:274)ników i tablic. Z lewej strony rysunku pokazano operacje przeprowadzane za pomoc(cid:241) tablicy. Zmienna head (z ang. g(cid:228)owa) informuje o tym, (cid:276)e pierwszy element listy znajduje si(cid:246) pod indek- sem tablicy o numerze 10. Ka(cid:276)dy element tablicy zawiera struktur(cid:246) repre- zentuj(cid:241)c(cid:241) danego pracownika. Pole next (z ang. nast(cid:246)pny), b(cid:246)d(cid:241)ce elementem struktury, przechowuje indeks, pod którym znajduje si(cid:246) tablica zawieraj(cid:241)ca dane nast(cid:246)pnego pracownika. Elementy zacieniowane symbolizuj(cid:241) niewy- korzystane elementy tablicy. Rysunek 1.1. Porównanie reprezentacji listy powi(cid:241)zanej za pomoc(cid:241) tablicy i wska(cid:274)ników 18 (cid:95) Rozdzia(cid:293) 1. Wst(cid:253)p Kup książkęPoleć książkę Prawa strona rysunku przedstawia t(cid:246) sam(cid:241) operacj(cid:246) przeprowadzan(cid:241) przy u(cid:276)yciu wska(cid:274)ników. Zmienna head przechowuje wska(cid:274)nik do w(cid:246)z(cid:228)a za- wieraj(cid:241)cego dane pierwszego pracownika. Ka(cid:276)dy w(cid:246)ze(cid:228) przechowuje dane pracownika, a tak(cid:276)e wska(cid:274)nik do nast(cid:246)pnego w(cid:246)z(cid:228)a powi(cid:241)zanego z list(cid:241). Reprezentacja wykonana za pomoc(cid:241) wska(cid:274)ników jest nie tylko bardziej czytelna, ale tak(cid:276)e bardziej elastyczna. Zwykle przed stworzeniem tablicy musimy okre(cid:264)li(cid:232) jej rozmiar, co narzuca nam ograniczenie liczby elemen- tów przechowywanych przez tablic(cid:246). Reprezentacja wykonana przy u(cid:276)y- ciu wska(cid:274)ników nie narzuca takiego ograniczenia. W razie potrzeby nowy w(cid:246)ze(cid:228) mo(cid:276)na dynamicznie alokowa(cid:232). Wska(cid:274)niki w j(cid:246)zyku C s(cid:241) stosowane do obs(cid:228)ugi dynamicznej alokacji pa- mi(cid:246)ci. Funkcja malloc jest stosowana do dynamicznego alokowania pami(cid:246)ci, a funkcja free jest u(cid:276)ywana do jej zwalniania. Dynamiczna alokacja pami(cid:246)ci pozwala na tworzenie struktur danych i tablic o zmiennym rozmiarze. Takie struktury to np. listy powi(cid:241)zane i kolejki. Jedynie nowszy standard j(cid:246)zyka C — C11 — obs(cid:228)uguje tablice o zmiennym rozmiarze. Zwarte wyra(cid:276)enia mog(cid:241) zawiera(cid:232) wiele informacji, ale jednocze(cid:264)nie mog(cid:241) by(cid:232) trudne do odczytania. Z tego powodu zapis wska(cid:274)ników nie jest zro- zumia(cid:228)y dla wielu programistów. Zwarty zapis powinien odpowiada(cid:232) na konkretne potrzeby. Nie powinien by(cid:232) niepotrzebnie zagmatwany. W po- ni(cid:276)szej przyk(cid:228)adowej sekwencji kodu trzeci znak drugiego elementu names (litera „w”) jest wy(cid:264)wietlany za pomoc(cid:241) dwóch ró(cid:276)nych funkcji printf. Na razie takie zastosowanie wska(cid:274)ników mo(cid:276)e wydawa(cid:232) si(cid:246) niejasne, jednak(cid:276)e zostanie ono wyt(cid:228)umaczone w podrozdziale „Wy(cid:228)uskiwanie wska(cid:274)nika za pomoc(cid:241) operatora adresowania po(cid:264)redniego”. Obie funkcje printf daj(cid:241) ten sam rezultat — wy(cid:264)wietlaj(cid:241) liter(cid:246) w. Prostszym dzia(cid:228)aniem wydaje si(cid:246) jednak stosowanie notacji tablicowej. char *names[] = { Kowalski , Nowak , Kowalczyk }; printf( c\n ,*(*(names+1)+2)); printf( c\n ,names[1][2]); Wska(cid:274)niki s(cid:241) pot(cid:246)(cid:276)nym narz(cid:246)dziem s(cid:228)u(cid:276)(cid:241)cym do tworzenia aplikacji oraz usprawniania ich dzia(cid:228)ania. Jednak(cid:276)e korzystaj(cid:241)c ze wska(cid:274)ników, mo(cid:276)emy natrafi(cid:232) na liczne problemy, takie jak: (cid:120) próby uzyskania dost(cid:246)pu do danych znajduj(cid:241)cych si(cid:246) poza granicami struktury (mo(cid:276)e mie(cid:232) to miejsce w przypadku odczytu danych z tablicy); (cid:120) odwo(cid:228)ywanie si(cid:246) do zmiennych automatycznych, gdy te zmienne ju(cid:276) nie istniej(cid:241); Wska(cid:346)niki i pami(cid:253)(cid:235) (cid:95) 19 Kup książkęPoleć książkę (cid:120) odwo(cid:228)ywanie si(cid:246) do pami(cid:246)ci alokowanej na stercie, gdy ta ju(cid:276) zosta(cid:228)a wcze(cid:264)niej zwolniona; (cid:120) wy(cid:228)uskiwanie wska(cid:274)nika przed alokowaniem go w pami(cid:246)ci. Szczegó(cid:228)owe omówienie tych problemów znajdziesz w rozdziale 7. Sk(cid:228)adnia i semantyka wska(cid:274)ników s(cid:241) jasno okre(cid:264)lone w specyfikacji j(cid:246)zyka C (http://bit.ly/173cDxJ). Pomimo tego mo(cid:276)na napotka(cid:232) sytuacje, w których specyfikacja dok(cid:228)adnie nie okre(cid:264)la zachowania wska(cid:274)nika. W takich przy- padkach zachowanie wska(cid:274)nika jest: Zdefiniowane przez implementacj(cid:246) Niektóre przypadki s(cid:241) zdefiniowane w dokumentacji. Przyk(cid:228)adem za- chowania zdefiniowanego przez implementacj(cid:246) jest propagacja najbar- dziej znacz(cid:241)cego bitu podczas operacji prawostronnej zamiany elemen- tów typu integer. Nieokre(cid:264)lone Niektóre implementacje s(cid:241) ustalone, ale nieudokumentowane. Przyk(cid:228)a- dem tego mo(cid:276)e by(cid:232) ilo(cid:264)(cid:232) pami(cid:246)ci alokowanej przez funkcj(cid:246) malloc z ar- gumentem zerowym. List(cid:246) tego typu zachowa(cid:254) mo(cid:276)na znale(cid:274)(cid:232) w CERT Secure Coding, w za(cid:228)(cid:241)czniku DD (http://bit.ly/YOFY8s). Niezdefiniowane W takich przypadkach nie istniej(cid:241) (cid:276)adne na(cid:228)o(cid:276)one wymagania, a wi(cid:246)c mo(cid:276)e wynikn(cid:241)(cid:232) dos(cid:228)ownie wszystko. Takim przyk(cid:228)adem jest dealo- kacja warto(cid:264)ci wska(cid:274)nika za pomoc(cid:241) funkcji free. List(cid:246) tego typu przy- padków mo(cid:276)na znale(cid:274)(cid:232) w CERT Secure Coding w za(cid:228)(cid:241)czniku CC (http://bit.ly/16msOVK). Niektóre zachowania s(cid:241) czasami okre(cid:264)lone miejscowo. Mo(cid:276)na je zwykle znale(cid:274)(cid:232) w dokumentacji kompilatora. Tolerancja wynikaj(cid:241)ca z istnienia za- chowa(cid:254) okre(cid:264)lonych miejscowo pozwala na generowanie bardziej wydaj- nego kodu. Deklarowanie wska(cid:346)ników Deklaracja zmiennej b(cid:246)d(cid:241)cej wska(cid:274)nikiem sk(cid:228)ada si(cid:246) z nast(cid:246)puj(cid:241)cych po sobie elementów: typu danych, gwiazdki, nazwy wska(cid:274)nika. W poni(cid:276)szym przyk(cid:228)adzie zadeklarowano obiekt typu integer, a tak(cid:276)e wska(cid:274)nik na ele- ment typu integer: int num; int *pi; 20 (cid:95) Rozdzia(cid:293) 1. Wst(cid:253)p Kup książkęPoleć książkę Stosowanie w zapisie znaku spacji nie ma tutaj znaczenia. Poni(cid:276)sze przy- k(cid:228)ady s(cid:241) równoznaczne z zapisem umieszczonym powy(cid:276)ej: int* pi; int * pi; int *pi; int*pi; Stosowanie znaku spacji jest indywidualn(cid:241) spraw(cid:241) u(cid:276)ytkownika. Gwiazdka informuje o tym, (cid:276)e dana zmienna jest wska(cid:274)nikiem. Symbol ten jest bardzo cz(cid:246)sto u(cid:276)ywany. Korzysta si(cid:246) z niego równie(cid:276) podczas ma- nipulowania wska(cid:274)nikiem i wy(cid:228)uskiwania go. Rysunek 1.2 wizualizuje sposób alokowania pami(cid:246)ci dla powy(cid:276)szych de- klaracji. Komórki pami(cid:246)ci s(cid:241) przedstawione za pomoc(cid:241) trzech prostok(cid:241)tów. Numery znajduj(cid:241)ce si(cid:246) po lewej stronie odpowiadaj(cid:241) adresom zmiennych. Adres numer 100 zosta(cid:228) tu zastosowany w celu uczynienia rysunku wy- ra(cid:274)niejszym. Zwykle nie znamy dok(cid:228)adnych adresów wska(cid:274)ników ani ja- kichkolwiek innych zmiennych. W wi(cid:246)kszo(cid:264)ci sytuacji taki dok(cid:228)adny adres nie interesuje nas jako programistów. Wielokropki symbolizuj(cid:241) pami(cid:246)(cid:232) niezainicjowan(cid:241). Rysunek 1.2. Schemat pami(cid:246)ci Wska(cid:274)niki na niezainicjowan(cid:241) pami(cid:246)(cid:232) mog(cid:241) by(cid:232) problematyczne. Gdy poddamy taki wska(cid:274)nik dereferencji, prawdopodobnie jego zawarto(cid:264)(cid:232) nie b(cid:246)dzie okre(cid:264)la(cid:228)a poprawnego adresu. W przypadku, gdy b(cid:246)dzie wskazy- wa(cid:232) na poprawny adres, mo(cid:276)e on nie zawiera(cid:232) poprawnych danych. Nie- poprawnym adresem nazywamy adres, do którego dany program nie ma praw dost(cid:246)pu. Zaistnienie takiego adresu spowoduje na wi(cid:246)kszo(cid:264)ci plat- form zako(cid:254)czenie dzia(cid:228)ania programu. Jak opisano w rozdziale 7., mo(cid:276)e to prowadzi(cid:232) do licznych, powa(cid:276)nych problemów. Zmienne num oraz pi znajduj(cid:241) si(cid:246) odpowiednio pod adresami 100 i 104. Zak(cid:228)adamy, (cid:276)e obie zmienne zajmuj(cid:241) po 4 bajty ka(cid:276)da. Rozmiary te mog(cid:241) by(cid:232) ró(cid:276)ne w zale(cid:276)no(cid:264)ci od konfiguracji systemu. Zagadnienie to opisano Wska(cid:346)niki i pami(cid:253)(cid:235) (cid:95) 21 Kup książkęPoleć książkę szerzej w podrozdziale „Rodzaje wska(cid:274)ników i ich rozmiary”. Je(cid:276)eli nie zaznaczono inaczej, przyjmujemy w przedstawianych przyk(cid:228)adach, (cid:276)e wszystkie obiekty typu integer zajmuj(cid:241) po cztery bajty. W celu wyja(cid:264)nienia zasad dzia(cid:228)ania wska(cid:274)ników b(cid:246)dziemy ko- rzysta(cid:232) z adresów pami(cid:246)ci, takich jak np. 100. W du(cid:276)ym stopniu upro(cid:264)ci to przyk(cid:228)ady. Kiedy samodzielnie wykonasz zaprezento- wane przyk(cid:228)ady, otrzymasz zupe(cid:228)nie inne adresy. Adresy te mog(cid:241) by(cid:232) ró(cid:276)ne w kolejnych uruchomieniach tego samego programu. Warto pami(cid:246)ta(cid:232) o tym, (cid:276)e: (cid:120) Wska(cid:274)nik pi powinien w ko(cid:254)cu by(cid:232) przypisany do adresu zmiennej typu ca(cid:228)kowitoliczbowego (integer). (cid:120) Przedstawione zmienne nie zosta(cid:228)y zainicjowane, a wi(cid:246)c zawieraj(cid:241) bezu(cid:276)yteczne dane. (cid:120) Implementacja wska(cid:274)nika nie zawiera w swojej istocie niczego, co by mog(cid:228)o sugerowa(cid:232) typ danych, na jakie wskazuje wska(cid:274)nik, oraz infor- mowa(cid:232) o poprawno(cid:264)ci wskazywanych danych. Jednak(cid:276)e, je(cid:276)eli okre(cid:264)li- li(cid:264)my typ wska(cid:274)nika, kompilator b(cid:246)dzie sygnalizowa(cid:232) sytuacje, w których wska(cid:274)nik nie b(cid:246)dzie stosowany prawid(cid:228)owo. Poprzez dane bezu(cid:276)yteczne nale(cid:276)y rozumie(cid:232) takie elementy, które po alokowaniu pami(cid:246)ci mog(cid:241) zawiera(cid:232) dowolne warto(cid:264)ci. Pami(cid:246)(cid:232) nie jest czyszczona po alokacji. Wcze(cid:264)niej mog(cid:228)y by(cid:232) w niej ju(cid:276) zapisane jakie(cid:264) dane. Je(cid:276)eli dany obszar pami(cid:246)ci wcze(cid:264)niej zawiera(cid:228) liczb(cid:246) zmiennoprzecinkow(cid:241), to interpretacja jej jako liczby ca(cid:228)kowitej nie ma sensu. Nawet je(cid:276)eli by(cid:228)a tam zapisana liczba ca(cid:228)kowita, to prawdopodobnie nie b(cid:246)dzie nam ona do niczego potrzebna. Dlatego dane zawarte w takiej pami(cid:246)ci s(cid:241) bezu(cid:276)yteczne. Wska(cid:274)nik mo(cid:276)e by(cid:232) stosowany bez uprzedniego zainicjowania, jednak(cid:276)e mo(cid:276)e on nie dzia(cid:228)a(cid:232) prawid(cid:228)owo do momentu inicjalizacji. Interpretowanie deklaracji Aby zrozumie(cid:232) dzia(cid:228)anie wska(cid:274)ników, warto przyjrze(cid:232) si(cid:246) ich deklara- cjom. Nale(cid:276)y je odczytywa(cid:232) od ko(cid:254)ca. Co prawda nie omówili(cid:264)my jeszcze wska(cid:274)ników na sta(cid:228)e, jednak(cid:276)e przyjrzyjmy si(cid:246) poni(cid:276)szej deklaracji. const int *pci; 22 (cid:95) Rozdzia(cid:293) 1. Wst(cid:253)p Kup książkęPoleć książkę Odczytanie deklaracji od ko(cid:254)ca pozwoli Ci na jej stopniowe odszyfrowanie (patrz rysunek 1.3). Rysunek 1.3. Odczytywanie deklaracji Wed(cid:228)ug wielu programistów interpretowanie deklaracji „od ko(cid:254)ca” jest (cid:228)atwiejsze. Pracuj(cid:241)c ze z(cid:228)o(cid:276)onymi wska(cid:274)nikami, rysuj ich schematy. Takie schematy zostan(cid:241) przedstawione przy wielu omawianych przy- k(cid:228)adach. Operator adresu Operator adresu zwróci adres argumentu wyra(cid:276)enia. Stosuj(cid:241)c adres zmiennej num, mo(cid:276)esz zainicjowa(cid:232) wska(cid:274)nik pi: int num; pi = num; Zmiennej num przypisano warto(cid:264)(cid:232) zero, a zmienna pi ma wskazywa(cid:232) na adres zmiennej num. Ilustruje to poni(cid:276)szy rysunek. Rysunek 1.4. Przyporz(cid:241)dkowanie pami(cid:246)ci Ju(cid:276) podczas deklaracji zmiennych mo(cid:276)esz zainicjowa(cid:232) pi, aby wskazywa(cid:228)a na adres num: int num; int *pi = num; Jednak(cid:276)e zastosowanie poni(cid:276)szych deklaracji w wi(cid:246)kszo(cid:264)ci kompilatorów spowoduje wy(cid:264)wietlenie informacji o b(cid:228)(cid:246)dzie sk(cid:228)adni: num = 0; pi = num; Wska(cid:346)niki i pami(cid:253)(cid:235) (cid:95) 23 Kup książkęPoleć książkę Wy(cid:264)wietli si(cid:246) komunikat b(cid:228)(cid:246)du o nast(cid:246)puj(cid:241)cej tre(cid:264)ci: error: invalid conversion from int to int* Zmienna pi jest wska(cid:274)nikiem na obiekt typu integer, a num jest zmienn(cid:241) typu integer. Komunikat o b(cid:228)(cid:246)dzie informuje nas, (cid:276)e nie mo(cid:276)emy dokona(cid:232) konwersji. Przypisanie elementu typu integer do wska(cid:274)nika zwykle po- woduje wy(cid:264)wietlenie przez kompilator ostrze(cid:276)enia lub komu- nikatu o b(cid:228)(cid:246)dzie. Wska(cid:274)niki ró(cid:276)ni(cid:241) si(cid:246) od zmiennych typu integer. Co prawda obydwa te elementy mog(cid:241) by(cid:232) przechowywane w pami(cid:246)ci przy u(cid:276)yciu takiej samej liczby bajtów, jednak(cid:276)e s(cid:241) pomi(cid:246)dzy nimi znacz(cid:241)ce ró(cid:276)nice. Istnieje mo(cid:276)- liwo(cid:264)(cid:232) rzutowania zmiennej typu integer na wska(cid:274)nik na zmienn(cid:241) typu integer: pi = (int *)num; Zastosowanie powy(cid:276)szej instrukcji nie spowoduje wy(cid:264)wietlenia komuni- katu o b(cid:228)(cid:246)dzie sk(cid:228)adni, jednak(cid:276)e wykonywany program mo(cid:276)e ulec anor- malnemu zako(cid:254)czeniu podczas próby dereferencji warto(cid:264)ci o adresie zero. W wi(cid:246)kszo(cid:264)ci systemów operacyjnych nie zawsze mo(cid:276)na wykorzystywa(cid:232) adres zerowy. Problematyk(cid:246) t(cid:246) opisano szerzej w podrozdziale „Poj(cid:246)cie braku warto(cid:264)ci”. Dobr(cid:241) praktyk(cid:241) stosowan(cid:241) w programowaniu jest jak najszybsze inicjowanie wska(cid:274)nika, co ilustruje poni(cid:276)szy przyk(cid:228)ad: int num; int *pi; pi = num; Wy(cid:316)wietlanie warto(cid:316)ci wska(cid:346)ników W praktyce bardzo rzadko spotkasz zmienne posiadaj(cid:241)ce adresy takie jak 100 i 104. Adres zmiennej mo(cid:276)na wy(cid:264)wietli(cid:232) za pomoc(cid:241) nast(cid:246)puj(cid:241)cych instrukcji: int num = 0; int *pi = num; printf( Adres num: d Wartosc: d\n , num, num); printf( Adres pi: d Wartosc: d\n , pi, pi); 24 (cid:95) Rozdzia(cid:293) 1. Wst(cid:253)p Kup książkęPoleć książkę Po wykonaniu powy(cid:276)szych instrukcji uzyskasz dane wyj(cid:264)ciowe podobne do poni(cid:276)szych. W tym przyk(cid:228)adzie podali(cid:264)my prawdziwe adresy. Adresy uzyskane przez Ciebie b(cid:246)d(cid:241) prawdopodobnie inne. Adres num: 4520836 Wartosc: 0 Adres pi: 4520824 Wartosc: 4520836 Korzystaj(cid:241)c z funkcji printf podczas pracy ze wska(cid:274)nikami, mo(cid:276)esz sto- sowa(cid:232) inne przydatne specyfikatory pola. Przedstawiono je w tabeli 1.2. Tabela 1.2. Specyfikatory pola Specyfikator x o p Funkcja specyfikatora wy(cid:316)wietla warto(cid:316)(cid:235) w postaci liczby w systemie szesnastkowym wy(cid:316)wietla warto(cid:316)(cid:235) w postaci liczby w systemie ósemkowym wy(cid:316)wietla warto(cid:316)(cid:235) w(cid:293)a(cid:316)ciw(cid:233) dla implementacji, zwykle jest to liczba w postaci szesnastkowej Poni(cid:276)sze przyk(cid:228)ady ilustruj(cid:241) zastosowanie tych specyfikatorów: printf( Adres pi: d Wartosc: d\n , pi, pi); printf( Adres pi: x Wartosc: x\n , pi, pi); printf( Adres pi: o Wartosc: o\n , pi, pi); printf( Adres pi: p Wartosc: p\n , pi, pi); Powy(cid:276)szy ci(cid:241)g instrukcji spowoduje wy(cid:264)wietlenie adresu i zawarto(cid:264)ci pi. W tym przypadku pi przechowuje adres num. Adres pi: 4520824 Wartosc: 4520836 Adres pi: 44fb78 Wartosc: 44fb84 Adres pi: 21175570 Wartosc: 21175604 Adres pi: 0044FB78 Wartosc: 0044FB84 Specyfikator p ró(cid:276)ni si(cid:246) od specyfikatora x. Zwykle wy(cid:264)wietla liczb(cid:246) w systemie szesnastkowym, stosuj(cid:241)c wielkie litery. O ile nie zaznaczono inaczej, specyfikator p b(cid:246)dzie stosowany do wy(cid:264)wietlania adresów. Konsekwentne wy(cid:264)wietlanie warto(cid:264)ci wska(cid:274)ników na ró(cid:276)nych platfor- mach jest zadaniem trudnym. Jednym ze sposobów na to jest rzutowanie wska(cid:274)nika jako wska(cid:274)nik na void, a nast(cid:246)pnie wy(cid:264)wietlenie go za pomoc(cid:241) specyfikatora p: printf( Wartosc pi: p\n , (void*)pi); Wska(cid:274)niki na void szerzej omówiono w podrozdziale „Wska(cid:274)niki na void”. Aby prezentowane przyk(cid:228)ady by(cid:228)y bardziej zrozumia(cid:228)e, b(cid:246)dziemy stosowa(cid:232) specyfikator p bez rzutowania adresu na wska(cid:274)nik na void. Wska(cid:346)niki i pami(cid:253)(cid:235) (cid:95) 25 Kup książkęPoleć książkę Pami(cid:253)(cid:235) wirtualna i wska(cid:346)niki W rzeczywisto(cid:264)ci wy(cid:264)wietlanie adresów wska(cid:274)ników jest jeszcze bardziej z(cid:228)o(cid:276)one. Adresy wska(cid:274)ników wy(cid:264)wietlane przez wirtualny system opera- cyjny prawdopodobnie nie b(cid:246)d(cid:241) ich prawdziwymi, fizycznymi adresami. Wirtualny system operacyjny pozwala na umieszczenie programu w fi- zycznej pami(cid:246)ci komputera po uprzednim podzieleniu go na kilka cz(cid:246)(cid:264)ci. Aplikacja jest dzielona na strony (ramki). Strony odzwierciedlaj(cid:241) obszary g(cid:228)ównej pami(cid:246)ci komputera. Strony aplikacji s(cid:241) alokowane w ró(cid:276)nych, niekoniecznie s(cid:241)siaduj(cid:241)cych ze sob(cid:241) obszarach pami(cid:246)ci. Ponadto strony programu wcale nie musz(cid:241) znajdowa(cid:232) si(cid:246) jednocze(cid:264)nie w pami(cid:246)ci. Je(cid:276)eli system operacyjny potrzebuje pami(cid:246)ci obecnie zajmowanej przez ramk(cid:246), ramka mo(cid:276)e zosta(cid:232) przeniesiona do pami(cid:246)ci pomocniczej. Gdy ramka znów b(cid:246)dzie potrzebna, system operacyjny mo(cid:276)e j(cid:241) przenie(cid:264)(cid:232) z powrotem do pami(cid:246)ci, nawet pod inny adres. Ta funkcja systemu operacyjnego spra- wia, (cid:276)e zarz(cid:241)dzanie pami(cid:246)ci(cid:241) jest procesem bardzo elastycznym. Ka(cid:276)dy program zak(cid:228)ada potencjaln(cid:241) mo(cid:276)liwo(cid:264)(cid:232) uzyskania dost(cid:246)pu do ca- (cid:228)ej pami(cid:246)ci dost(cid:246)pnej fizycznie w komputerze. W rzeczywisto(cid:264)ci jest nieco inaczej. Program korzysta z adresów wirtualnych. Gdy zachodzi potrzeba, system operacyjny mapuje adresy wirtualne do pami(cid:246)ci fizycznej. Oznacza to, (cid:276)e podczas wykonywania programu kod i dane przechowy- wane przez stron(cid:246) mog(cid:241) znajdowa(cid:232) si(cid:246) w ró(cid:276)nych fizycznych lokacjach. Wirtualne adresy aplikacji nie zmieniaj(cid:241) si(cid:246). S(cid:241) to w(cid:228)a(cid:264)nie te adresy, które analizowali(cid:264)my, przygl(cid:241)daj(cid:241)c si(cid:246) zawarto(cid:264)ci wska(cid:274)ników. System operacyjny mapuje adresy wirtualne na adresy rzeczywiste w sposób transparentny. Jest to rzecz w pe(cid:228)ni obs(cid:228)ugiwana przez system operacyjny. Programista nie ma nad tym procesem kontroli, a tak(cid:276)e nie musi si(cid:246) nim przejmowa(cid:232). Zrozumienie tego zagadnienia pomo(cid:276)e w wyja(cid:264)nieniu adresów zwraca- nych przez program dzia(cid:228)aj(cid:241)cy w wirtualnym systemie operacyjnym. Wy(cid:293)uskiwanie wska(cid:346)nika za pomoc(cid:233) operatora adresowania po(cid:316)redniego Operator adresowania po(cid:264)redniego — * — zwraca warto(cid:264)(cid:232), na któr(cid:241) wskazuje zmienna wska(cid:274)nika. Tak(cid:241) operacj(cid:246) nazywa si(cid:246) wy(cid:228)uskaniem (dereferencj(cid:241)) wska(cid:274)nika. W poni(cid:276)szym przyk(cid:228)adzie zadeklarowano i za- inicjowano zmienne num i wska(cid:274)nik pi: int num = 5; int *pi = num; 26 (cid:95) Rozdzia(cid:293) 1. Wst(cid:253)p Kup książkęPoleć książkę Zastosujmy operator adresowania po(cid:264)redniego, aby wy(cid:264)wietli(cid:232) 5 — war- to(cid:264)(cid:232) przechowywan(cid:241) przez num: printf( p\n ,*pi); // wy(cid:286)wietla 5 Rezultat dereferencji mo(cid:276)emy równie(cid:276) wykorzysta(cid:232) w roli lvalue (warto- (cid:264)ci lewostronnej). Termin ten odnosi si(cid:246) do argumentu znajduj(cid:241)cego si(cid:246) po lewej stronie operatora przypisania. Wszystkie warto(cid:264)ci lewostronne musz(cid:241) by(cid:232) modyfikowalne w celu przeprowadzenia operacji przypisania. W poni(cid:276)szym przyk(cid:228)adzie przypiszesz warto(cid:264)(cid:232) 200 do zmiennej typu integer wskazywanej przez pi. W zwi(cid:241)zku z tym, (cid:276)e wska(cid:274)nik wskazuje na zmien- n(cid:241) num, warto(cid:264)(cid:232) 200 zostanie przypisana do tej zmiennej. Rysunek 1.5 ilu- struje wp(cid:228)yw tej operacji na stan pami(cid:246)ci. *pi = 200; printf( d\n ,num); // wy(cid:286)wietla 200 Rysunek 1.5. Przydzielenie pami(cid:246)ci podczas korzystania z operatora dereferencji Wska(cid:346)niki na funkcje Wska(cid:274)nik mo(cid:276)e by(cid:232) zadeklarowany tak, aby wskazywa(cid:228) na funkcj(cid:246). Zapis takiej deklaracji jest nieco skomplikowany. Poni(cid:276)ej przedstawiono sposób deklaracji wska(cid:274)nika na funkcj(cid:246). Nie przekazujemy (cid:276)adnych argumentów do funkcji, a funkcja niczego nie zwraca. Wska(cid:274)nik nazywamy foo: void (*foo)(); Wska(cid:274)niki na funkcj(cid:246) s(cid:241) obszernym tematem poruszonym w rozdziale 3. Poj(cid:253)cie warto(cid:316)ci null Zagadnienia zwi(cid:241)zane z warto(cid:264)ci(cid:241) null s(cid:241) ciekawe, aczkolwiek cz(cid:246)sto mylone. Pomy(cid:228)ka mo(cid:276)e nast(cid:241)pi(cid:232) w wyniku tego, (cid:276)e cz(cid:246)sto mamy do czynienia z ró(cid:276)- nymi, cho(cid:232) podobnymi do siebie poj(cid:246)ciami, takimi jak: (cid:120) brak warto(cid:264)ci, (cid:120) sta(cid:228)a b(cid:246)d(cid:241)ca wska(cid:274)nikiem zerowym, (cid:120) makro NULL, Wska(cid:346)niki i pami(cid:253)(cid:235) (cid:95) 27 Kup książkęPoleć książkę (cid:120) znak NUL w ASCII, (cid:120) pusty (cid:228)a(cid:254)cuch znakowy, (cid:120) porównanie do warto(cid:264)ci null. Przypisanie warto(cid:264)ci NULL wska(cid:274)nikowi skutkuje tym, (cid:276)e wska(cid:274)nik nie b(cid:246)- dzie niczego wskazywa(cid:228). Poj(cid:246)cie null (braku warto(cid:264)ci) odnosi si(cid:246) do tego, (cid:276)e wska(cid:274)nik mo(cid:276)e przechowywa(cid:232) okre(cid:264)lon(cid:241) warto(cid:264)(cid:232), która nie jest równa innemu wska(cid:274)nikowi. Wska(cid:274)nik pusty nie wskazuje adresu pami(cid:246)ci. Dwa puste wska(cid:274)niki s(cid:241) zawsze równe. Pustym mo(cid:276)na uczyni(cid:232) wska(cid:274)nik do- wolnego typu (np. wska(cid:274)nik na znak, wska(cid:274)nik na zmienn(cid:241) typu integer), jednak(cid:276)e w praktyce jest to rzadko stosowany zabieg. Poj(cid:246)cie braku warto(cid:264)ci jest pewn(cid:241) abstrakcj(cid:241) obs(cid:228)ugiwan(cid:241) za pomoc(cid:241) sta(cid:228)ej wska(cid:274)nika pustego. Sta(cid:228)a ta mo(cid:276)e, ale nie musi, by(cid:232) równa zeru. Programi- sta j(cid:246)zyka C nie musi si(cid:246) przejmowa(cid:232) jej wewn(cid:246)trzn(cid:241) reprezentacj(cid:241). Makro NULL jest zerow(cid:241) sta(cid:228)(cid:241) typu integer rzutowan(cid:241) na wska(cid:274)nik na void. W wielu bibliotekach jest ona zdefiniowana w nast(cid:246)puj(cid:241)cy sposób: #define NULL ((void *)0) To jest w(cid:228)a(cid:264)nie to, co zwykle nazywamy pustym wska(cid:274)nikiem. Jego defini- cj(cid:246) mo(cid:276)esz znale(cid:274)(cid:232) w ró(cid:276)nych plikach nag(cid:228)ówkowych, takich jak: stddef.h, stdlib.h, i stdio.h. Je(cid:276)eli kompilator stosuje niezerowy wzorzec do reprezentacji warto(cid:264)ci zero- wej, to taki kompilator musi zapewni(cid:232) to, (cid:276)e wszystkie wska(cid:274)niki, w których kontek(cid:264)cie zastosowano 0 i NULL, b(cid:246)d(cid:241) traktowane jako puste. W(cid:228)a(cid:264)ciwa wewn(cid:246)trzna reprezentacja braku warto(cid:264)ci jest definiowana przez imple- mentacj(cid:246). Symbole NULL i 0 s(cid:241) stosowane na poziomie j(cid:246)zyka tylko w celu utworzenia pustego wska(cid:274)nika. Znak NUL w ASCII jest bajtem zawieraj(cid:241)cym same zera. Jednak(cid:276)e jest to co(cid:264) zupe(cid:228)nie innego ni(cid:276) pusty wska(cid:274)nik. (cid:227)a(cid:254)cuch w j(cid:246)zyku C jest zapisywany jako ci(cid:241)g znaków zako(cid:254)czonych warto(cid:264)ci(cid:241) zerow(cid:241). Pusty (cid:228)a(cid:254)cuch nie za- wiera (cid:276)adnych znaków. Pusta instrukcja jest to instrukcja sk(cid:228)adaj(cid:241)ca si(cid:246) z samego (cid:264)rednika. Jak si(cid:246) pó(cid:274)niej przekonasz, pusty wska(cid:274)nik bardzo si(cid:246) przydaje do im- plementacji ró(cid:276)nych struktur danych, takich jak np. listy powi(cid:241)zane, gdzie jest on stosowany do oznaczania ko(cid:254)ca listy. Je(cid:276)eli naszym zamiarem jest przypisanie warto(cid:264)ci zerowej wska(cid:274)nikowi pi, mo(cid:276)emy to zrobi(cid:232) w nast(cid:246)puj(cid:241)cy sposób: pi = NULL; 28 (cid:95) Rozdzia(cid:293) 1. Wst(cid:253)p Kup książkęPoleć książkę Pusty wska(cid:274)nik to nie to samo co wska(cid:274)nik niezainicjowany. Wska(cid:274)nik niezainicjowany mo(cid:276)e zawiera(cid:232) dowoln(cid:241) warto(cid:264)(cid:232). Z kolei pusty wska(cid:274)nik nie wskazuje (cid:276)adnego miejsca w pami(cid:246)ci. Co ciekawe, mo(cid:276)emy przypisa(cid:232) wska(cid:274)nikowi warto(cid:264)(cid:232) zerow(cid:241), ale nie mo- (cid:276)emy mu przypisa(cid:232) (cid:276)adnej innej warto(cid:264)ci typu integer. Przyjrzyj si(cid:246) nast(cid:246)- puj(cid:241)cym operacjom przypisania: pi = 0; pi = NULL; pi = 100; // spowoduje powstanie b(cid:225)(cid:266)du sk(cid:225)adni pi = num; // spowoduje powstanie b(cid:225)(cid:266)du sk(cid:225)adni Wska(cid:274)nik mo(cid:276)e by(cid:232) zastosowany jako samodzielny argument wyra(cid:276)enia logicznego. Sprawd(cid:274)my na przyk(cid:228)ad, czy w wyniku zastosowania poni(cid:276)- szego kodu wska(cid:274)nik b(cid:246)dzie pusty: if(pi) { // wska(cid:296)nik nie jest pusty } else { // wska(cid:296)nik jest pusty } Ka(cid:276)de z dwóch zastosowanych wyra(cid:276)e(cid:254) jest prawid(cid:228)owe, jed- nak(cid:276)e takie ich stosowanie jest zb(cid:246)dne. Bardziej czytelne, acz- kolwiek niekonieczne, jest bezpo(cid:264)rednie porównanie z NULL. Je(cid:276)eli w tym kontek(cid:264)cie wska(cid:274)nikowi pi przypisano warto(cid:264)(cid:232) NULL, b(cid:246)dzie ona interpretowana jako zero binarne. Instrukcja else zostanie wykonana, je(cid:276)eli pi b(cid:246)dzie zawiera(cid:232) NULL, poniewa(cid:276) w j(cid:246)zyku C zero jest binarn(cid:241) repre- zentacj(cid:241) fa(cid:228)szu. if(pi == NULL) ... if(pi != NULL) ... Nie powinno si(cid:246) dokonywa(cid:232) dereferencji pustych wska(cid:274)ników, poniewa(cid:276) nie zawieraj(cid:241) one prawid(cid:228)owego adresu. Próba wy- konania takiej operacji b(cid:246)dzie skutkowa(cid:232) zako(cid:254)czeniem dzia(cid:228)ania programu. Przypisywa(cid:235) warto(cid:316)(cid:235) zerow(cid:233) czy nie? Co jest lepsze podczas pracy ze wska(cid:274)nikami? Przypisywanie im warto(cid:264)ci 0 czy NULL? Ka(cid:276)dy wybór jest dobry. Niektórzy programi(cid:264)ci wol(cid:241) stosowa(cid:232) NULL, poniewa(cid:276) przypomina im to o tym, (cid:276)e pracuj(cid:241) ze wska(cid:274)nikami. Inni uwa(cid:276)aj(cid:241), (cid:276)e nie jest to konieczne, poniewa(cid:276) zero jest po prostu ukryte. Wska(cid:346)niki i pami(cid:253)(cid:235) (cid:95) 29 Kup książkęPoleć książkę Nie powinno si(cid:246) jednak(cid:276)e stosowa(cid:232) NULL w kontek(cid:264)cie innym ni(cid:276) wska(cid:274)niki. Nie zawsze da to po(cid:276)(cid:241)dany efekt. Z pewno(cid:264)ci(cid:241) b(cid:246)dzie problematyczne, gdy zostanie zastosowane zamiast znaku ASCII NUL. Znak ten w j(cid:246)zyku C nie jest definiowany przez (cid:276)aden standardowy plik nag(cid:228)ówkowy. Jest on ekwiwalentem (cid:228)a(cid:254)cucha znakowego \0, który, jako warto(cid:264)(cid:232) dziesi(cid:246)tna, oznacza zero. Znaczenie zera zmienia si(cid:246) w zale(cid:276)no(cid:264)ci od kontekstu, w jakim zosta(cid:228)o ono u(cid:276)yte. W jednym kontek(cid:264)cie mo(cid:276)e oznacza(cid:232) liczb(cid:246) ca(cid:228)kowit(cid:241), w innym pu- sty wska(cid:274)nik. Przeanalizuj poni(cid:276)szy przyk(cid:228)ad: int num; int *pi = 0; // zero odnosi si(cid:266) do pustego wska(cid:296)nika pi = num; *pi = 0; // zero odnosi si(cid:266) do elementu b(cid:266)d(cid:261)cego liczb(cid:261) ca(cid:225)kowit(cid:261) Przyzwyczaili(cid:264)my si(cid:246) do operatorów pe(cid:228)ni(cid:241)cych wiele funkcji. Takim ope- ratorem jest na przyk(cid:228)ad gwiazdka. Jest ona stosowana do deklarowania wska(cid:274)ników, dereferencji wska(cid:274)ników, a tak(cid:276)e jest operatorem mno(cid:276)enia. Zero jest równie(cid:276) elementem pe(cid:228)ni(cid:241)cym wiele funkcji. Mo(cid:276)e by(cid:232) to dla Ciebie k(cid:228)opotliwe, zw(cid:228)aszcza je(cid:276)eli nie jeste(cid:264) przyzwyczajony do tego, (cid:276)e argu- menty operacji mog(cid:241) pe(cid:228)ni(cid:232) wiele funkcji. Wska(cid:346)niki na void Wska(cid:274)nik na void jest wska(cid:274)nikiem ogólnego stosowania. Jest on przezna- czony do przechowywania odniesie(cid:254) do danych dowolnego typu. Oto przy- k(cid:228)adowy wska(cid:274)nik na void: void *pv; Przedstawiony wska(cid:274)nik posiada dwie interesuj(cid:241)ce w(cid:228)a(cid:264)ciwo(cid:264)ci: (cid:120) Wska(cid:274)nik na void ma tak(cid:241) sam(cid:241) reprezentacj(cid:246) i organizacj(cid:246) pami(cid:246)ci jak wska(cid:274)nik na char. (cid:120) Wska(cid:274)nik na void nigdy nie b(cid:246)dzie równy innemu wska(cid:274)nikowi. Jed- nak(cid:276)e dwa wska(cid:274)niki na void, do których przypisano warto(cid:264)(cid:232) NULL, b(cid:246)d(cid:241) sobie równe. Ka(cid:276)dy wska(cid:274)nik mo(cid:276)e zosta(cid:232) przypisany do wska(cid:274)nika na void. Pó(cid:274)niej taki wska(cid:274)nik mo(cid:276)na z powrotem rzutowa(cid:232) na jego pocz(cid:241)tkowy typ. Po takiej operacji warto(cid:264)(cid:232) wska(cid:274)nika b(cid:246)dzie równa warto(cid:264)ci wska(cid:274)nika przed zmianami. Tak(cid:241) operacj(cid:246) pokazano poni(cid:276)ej. Wska(cid:274)nik int jest przypisywany do wska(cid:274)nika na void, a nast(cid:246)pnie wraca do swojej pierwotnej postaci: int num; int *pi = num; printf( Wartosc pi: p\n , pi); 30 (cid:95) Rozdzia(cid:293) 1. Wst(cid:253)p Kup książkęPoleć książkę void* pv = pi; pi = (int*) pv; printf( Wartosc pi: p\n , pi); Adresy wy(cid:264)wietlone w wyniku dzia(cid:228)ania tego programu b(cid:246)d(cid:241) identyczne: Value of pi: 100 Value of pi: 100 Wska(cid:274)niki na void s(cid:241) stosowane przy wska(cid:274)nikach na dane, a nie wska(cid:274)- nikach na funkcje. W podrozdziale „Polimorfizm w j(cid:246)zyku C” ponownie przyjrzymy si(cid:246) zastosowaniu wska(cid:274)ników na void w odniesieniu do za- chowa(cid:254) polimorficznych. B(cid:241)d(cid:274) ostro(cid:276)ny, gdy stosujesz wska(cid:274)niki na void. Je(cid:276)eli przepro- wadzisz operacj(cid:246) rzutowania dowolnego wska(cid:274)nika na void, nic nie b(cid:246)dzie zabezpiecza(cid:232) przed ewentualnym rzutowaniem go na inny typ wska(cid:274)nika. Operator sizeof mo(cid:276)e by(cid:232) stosowany ze wska(cid:274)nikami na void, jednak(cid:276)e nie mo(cid:276)na go stosowa(cid:232) w nast(cid:246)puj(cid:241)cy sposób: size_t size = sizeof(void*); // niedozwolona operacja size_t size = sizeof(void); // niedozwolona operacja Typ size_t jest typem danych stosowanym do rozmiarów. Omówiono go w podrozdziale „Predefiniowane typy zwi(cid:241)zane ze wska(cid:274)nikami”. Wska(cid:346)niki globalne i statyczne W chwili uruchomienia programu wska(cid:274)nik jest inicjowany warto(cid:264)ci(cid:241) NULL (je(cid:276)eli jest zadeklarowany jako globalny lub statyczny). Poni(cid:276)ej przedsta- wiono przyk(cid:228)ady wska(cid:274)ników globalnego i statycznego. int *globalpi; void foo() { static int *staticpi; ... } int main() { ... } Na rysunku 1.6 przedstawiono u(cid:228)o(cid:276)enie tych wska(cid:274)ników w pami(cid:246)ci. Ramki stosu s(cid:241) odk(cid:228)adane na stos, a sterta jest wykorzystywana do dynamicznej alokacji pami(cid:246)ci. Przestrze(cid:254) pami(cid:246)ci ponad stosem jest wype(cid:228)niana zmien- nymi globalnymi i statycznymi. Jest to tylko diagram ideowy. Zmienne glo- balne i statyczne s(cid:241) cz(cid:246)sto umieszczane w segmencie danych oddzielonym od segmentu, w którym znajduj(cid:241) si(cid:246) stos i sterta. Sterta i stos programu zostan(cid:241) szczegó(cid:228)owo omówione w rozdziale „Sterta i stos programu”. Wska(cid:346)niki i pami(cid:253)(cid:235) (cid:95) 31 Kup książkęPoleć książkę Rysunek 1.6. Wska(cid:274)niki globalne i statyczne — alokacja pami(cid:246)ci Rodzaje wska(cid:346)ników i ich rozmiary Rozmiar wska(cid:274)nika jest problemem, którym programista zaczyna si(cid:246) mar- twi(cid:232), gdy musi zadba(cid:232) o przeno(cid:264)no(cid:264)(cid:232) i kompatybilno(cid:264)(cid:232) aplikacji. W wi(cid:246)k- szo(cid:264)ci nowoczesnych platform rozmiar wska(cid:274)nika na dane jest zwykle iden- tyczny, niezale(cid:276)ny od typu wska(cid:274)nika. Wska(cid:274)nik do znaku ma taki sam rozmiar jak wska(cid:274)nik do struktury. Dzieje si(cid:246) tak nawet pomimo tego, (cid:276)e standard j(cid:246)zyka C nie okre(cid:264)la tego, (cid:276)e rozmiary wska(cid:274)ników maj(cid:241) by(cid:232) iden- tyczne. Jednak(cid:276)e wska(cid:274)nik na funkcj(cid:246) mo(cid:276)e si(cid:246) ró(cid:276)ni(cid:232) pod wzgl(cid:246)dem roz- miaru od wska(cid:274)nika na dane. Rozmiar wska(cid:274)nika zale(cid:276)y od maszyny oraz kompilatora. Na przyk(cid:228)ad w nowych wersjach systemu Windows wska(cid:274)niki maj(cid:241) rozmiar 32 lub 64 bitów. W systemie DOS oraz Windows 3.1 wska(cid:274)niki mia(cid:228)y d(cid:228)ugo(cid:264)(cid:232) 16 lub 32 bitów. Modele pami(cid:253)ci Wprowadzenie maszyn 64-bitowych sprawi(cid:228)o, (cid:276)e ró(cid:276)nice w ilo(cid:264)ci pami(cid:246)ci alokowanej dla poszczególnych typów danych sta(cid:228)y si(cid:246) zauwa(cid:276)alne. Ró(cid:276)ne komputery i kompilatory oferuj(cid:241) ró(cid:276)ne opcje alokowania danych. Poni(cid:276)ej zaprezentowano popularne notacje s(cid:228)u(cid:276)(cid:241)ce do opisu ró(cid:276)nych modeli danych: I In L Ln LL LLn P Pn Ka(cid:276)da wielka litera symbolizuje dane typu integer (I), dane typu long (L) lub wska(cid:274)nik (P). Ma(cid:228)e litery symbolizuj(cid:241) liczb(cid:246) bitów alokowanych dla da- nego typu danych. Tabela 1.3 ilustruje to dok(cid:228)adniej. Podano w niej rozmiar w bitach. 32 (cid:95) Rozdzia(cid:293) 1. Wst(cid:253)p Kup książkęPoleć książkę Tabela 1.3. Komputery i modele pami(cid:246)ci Typ danych (w j(cid:253)zyku C) char short _int32 int long long long wska(cid:346)nik LP64 8 16 32 64 64 ILP64 8 16 32 64 64 64 LLP64 8 16 32 32 64 64 ILP32 8 16 32 32 32 LP32 8 16 16 32 32 Model zale(cid:276)y od systemu i kompilatora. System operacyjny mo(cid:276)e obs(cid:228)u- giwa(cid:232) wi(cid:246)cej ni(cid:276) jeden model. W takim przypadku mo(cid:276)liwe jest zarz(cid:241)dza- nie przy u(cid:276)yciu odpowiednich opcji kompilatora. Predefiniowane typy zwi(cid:233)zane ze wska(cid:346)nikami Istniej(cid:241) cztery predefiniowane typy, które s(cid:241) cz(cid:246)sto u(cid:276)ywane ze wska(cid:274)- nikami: size_t Zapewnia rozmiarom bezpieczny typ. ptrdiff_t Obs(cid:228)uguje arytmetyk(cid:246) wska(cid:274)ników. intptr_t i uintprt_t Stosowane do przechowywania adresów wska(cid:274)ników. W poni(cid:276)szych podrozdzia(cid:228)ach przedstawiono zastosowanie ka(cid:276)dego z wy- mienionych wy(cid:276)ej typów, z wyj(cid:241)tkiem ptrdiff_t, który zostanie omówiony w podrozdziale „Odejmowanie wska(cid:274)ników”. Typ size_t Typ size_t reprezentuje maksymalny rozmiar dowolnego obiektu istniej(cid:241)ce- go w j(cid:246)zyku C. Do reprezentacji stosowana jest liczba ca(cid:228)kowita bez znaku. Rozmiar nie mo(cid:276)e by(cid:232) liczb(cid:241) ujemn(cid:241). Typ size_t s(cid:228)u(cid:276)y do deklarowania rozmiaru zgodnie z dost(cid:246)pnym adresowalnym obszarem pami(cid:246)ci. Typ size_t jest stosowany jako typ zwracany dla operatora sizeof. Ponadto jest on ar- gumentem dla wielu funkcji, takich jak np. malloc i strlen. Rodzaje wska(cid:346)ników i ich rozmiary (cid:95) 33 Kup książkęPoleć książkę Dobr(cid:241) praktyk(cid:241) jest stosowanie size_t podczas deklaracji zmien- nych okre(cid:264)laj(cid:241)cych rozmiary liczb, (cid:228)a(cid:254)cuchów znaków oraz tablic. Typ ten powinien by(cid:232) stosowany do liczników p(cid:246)tli, indekso- wania tablic, a czasami tak(cid:276)e do arytmetyki wska(cid:274)nikowej. Deklaracja size_t jest okre(cid:264)lana przez implementacj(cid:246). Mo(cid:276)na j(cid:241) odnale(cid:274)(cid:232) w wielu standardowych nag(cid:228)ówkach, takich jak stdio.h i stdlib.h. Zwykle jest ona definiowana w nast(cid:246)puj(cid:241)cy sposób: #ifndef __SIZE_T #define __SIZE_T typedef unsigned int size_t; #endif Dyrektywa define zapewnia to, (cid:276)e deklaracja b(cid:246)dzie wy(cid:228)(cid:241)cznie jednokrotna. Rzeczywisty rozmiar b(cid:246)dzie zale(cid:276)a(cid:228) od implementacji. W przypadku syste- mów 32-bitowych size_t b(cid:246)dzie mia(cid:228) zwykle d(cid:228)ugo(cid:264)(cid:232) 32 bitów, a w przy- padku systemów 64-bitowych d(cid:228)ugo(cid:264)(cid:232) ta wyniesie 64 bity. Zwykle najwy(cid:276)sz(cid:241) warto(cid:264)ci(cid:241) size_t jest SIZE_MAX. Typ size_t mo(cid:276)e by(cid:232) zwykle stosowany do przechowywania wska(cid:274)nika. Nie mo(cid:276)na jednak(cid:276)e zak(cid:228)ada(cid:232), (cid:276)e size_t b(cid:246)dzie ta- kiego samego rozmiaru jak wska(cid:274)nik. W kolejnym podrozdziale dowiesz si(cid:246), (cid:276)e lepiej w tym celu skorzysta(cid:232) z intptr_t. Musisz by(cid:232) ostro(cid:276)ny podczas wy(cid:264)wietlania warto(cid:264)ci zdefiniowanych jako size_t. S(cid:241) to warto(cid:264)ci bez znaku (typu unsigned), a wi(cid:246)c je(cid:276)eli wybierzesz nieprawid(cid:228)owy specyfikator formatu, wy(cid:264)wietlisz nieprawid(cid:228)owe dane. Zalecanym specyfikatorem formatu jest zu, jednak(cid:276)e nie zawsze jest on dost(cid:246)pny. Dopuszcza si(cid:246) równie(cid:276) stosowanie specyfikatorów u lub lu. Przyjrzyj si(cid:246) poni(cid:276)szemu przyk(cid:228)adowi. Najpierw zadeklarowano w nim zmienn(cid:241) jako size_t, a nast(cid:246)pnie wy(cid:264)wietlono j(cid:241) przy u(cid:276)yciu dwóch ró(cid:276)- nych specyfikatorów: size_t sizet = -5; printf( d\n ,sizet); printf( zu\n ,sizet); Zmienna typu size_t powinna by(cid:232) u(cid:276)ywana do dodatnich liczb ca(cid:228)kowitych. Stosowanie warto(cid:264)ci ujemnej mo(cid:276)e prowadzi(cid:232) do problemów. Gdy przy- piszemy liczb(cid:246) ujemn(cid:241) do size_t, a nast(cid:246)pnie j(cid:241) wy(cid:264)wietlimy, korzystaj(cid:241)c ze specyfikatorów d i zu, otrzymamy nast(cid:246)puj(cid:241)ce wyniki: -5 4294967291 34 (cid:95) Rozdzia(cid:293) 1. Wst(cid:253)p Kup książkęPoleć książkę Korzystaj(cid:241)c ze specyfikatora d, interpretujemy size_t jako liczb(cid:246) ca(cid:228)ko- wit(cid:241) ze znakiem. Wy(cid:264)wietlono –5, poniewa(cid:276) size_t przechowuje –5. Spe- cyfikator zu interpretuje zawarto(cid:264)(cid:232) size_t jako liczb(cid:246) ca(cid:228)kowit(cid:241) bez znaku. Podczas interpretowania liczby –5 jako liczby ca(cid:228)kowitej bez znaku naj- bardziej znacz(cid:241)cy bit tej liczby jest zmieniany na jedynk(cid:246), co jest interpre- towane jako liczba 2 podniesiona do wysokiej pot(cid:246)gi. Z tego powodu po u(cid:276)yciu specyfikatora zu wy(cid:264)wietlona zosta(cid:228)a tak du(cid:276)a warto(cid:264)(cid:232). Liczba dodatnia przy u(cid:276)yciu obu specyfikatorów zostanie wy(cid:264)wietlona poprawnie: sizet = 5; printf( d\n ,sizet); // Wyswietli 5 printf( zu\n ,sizet); // Wyswietli 5 Zmiennej typu size_t przypisuj tylko liczby dodatnie. Stosowanie operatora sizeof ze wska(cid:346)nikami Operator sizeof mo(cid:276)e by(cid:232) stosowany do okre(cid:264)lenia rozmiaru wska(cid:274)nika. Poni(cid:276)szy kod wy(cid:264)wietla rozmiar wska(cid:274)nika na obiekt typu char: printf( Rozmiar *char: d\n ,sizeof(char*)); Stosuj(cid:241)c ten kod, otrzymamy nast(cid:246)puj(cid:241)ce dane wyj(cid:264)ciowe: Rozmiar *char: 4 Gdy chcesz wy(cid:264)wietli(cid:232) rozmiar wska(cid:274)nika, zawsze korzystaj z operatora sizeof. Wska(cid:274)niki na funkcje mog(cid:241) mie(cid:232) ró(cid:276)ne rozmiary. Zwykle ten rozmiar jest okre- (cid:264)lony dla danej kombinacji systemu operacyjnego i kompilatora. Wiele kom- pilatorów obs(cid:228)uguje zarówno aplikacje 32-bitowe, jak i 64-bitowe. Mo(cid:276)liwa jest wi(cid:246)c sytuacja, w której ten sam program, skompilowany w inaczej skonfi- gurowanym kompilatorze, b(cid:246)dzie zawiera(cid:228) wska(cid:274)niki o ró(cid:276)nych rozmiarach. W architekturze harwardzkiej kod programu i dane s(cid:241) przechowywane w fizycznie oddzielnych pami(cid:246)ciach. Przyk(cid:228)adem uk(cid:228)adu o takiej archi- tekturze jest mikrokontroler Intel MCS-51 (8051). Co prawda Intel zaprze- sta(cid:228) produkcji tego uk(cid:228)adu, ale spotykanych i nadal produkowanych jest wiele uk(cid:228)adów binarnie z nim kompatybilnych. Kompilator Small Device C Compiler (w skrócie SDCC) obs(cid:228)uguje ten rodzaj mikrouk(cid:228)adów. Proce- sor ten obs(cid:228)uguje wska(cid:274)niki o rozmiarze od 1 do 4 bajtów. Zale(cid:276)nie od potrzeby rozmiar wska(cid:274)nika musi zosta(cid:232) zdefiniowany, poniewa(cid:276) w tym (cid:264)rodowisku wska(cid:274)niki nie maj(cid:241) sta(cid:228)ego rozmiaru. Rodzaje wska(cid:346)ników i ich rozmiary (cid:95) 35 Kup książkęPoleć książkę Typy intptr_t i uintptr_t Typy intptr_t i uintptr_t stosuje si(cid:246) do przechowywania adresów wska(cid:274)- ników. Dzi(cid:246)ki nim mo(cid:276)na wygodnie i bezpiecznie deklarowa(cid:232) wska(cid:274)niki. Maj(cid:241) one rozmiary identyczne z podstawowym rozmiarem wska(cid:274)nika w da- nym systemie. Mo(cid:276)na je stosowa(cid:232) do konwersji wska(cid:274)nika na jego reprezen- tacj(cid:246) w postaci obiektu typu integer. Typ uintptr_t jest bezznakow(cid:241) wersj(cid:241) intptr_t. Typ intptr_t jest preferowany w przypadku wi(cid:246)kszo(cid:264)ci operacji. Typ uintptr_t nie jest tak elastyczny jak intptr_t. Poni(cid:276)szy przyk(cid:228)ad pokazuje sposób u(cid:276)ycia intptr_t: int num; intptr_t *pi = num; Je(cid:276)eli spróbujesz przypisa(cid:232) (tak jak pokazano poni(cid:276)ej) adres obiektu typu integer do wska(cid:274)nika typu uintptr_t, kompilator wy(cid:264)wietli informacj(cid:246) o b(cid:228)(cid:246)- dzie sk(cid:228)adni: uintptr_t *pu = num; Tre(cid:264)(cid:232) komunikatu o b(cid:228)(cid:246)dzie konwersji b(cid:246)dzie nast(cid:246)puj(cid:241)ca: error: invalid conversion from int* to uintptr_t* {aka unsigned int*} [-fpermissive] Aby unikn(cid:241)(cid:232) komunikatu o b(cid:228)(cid:246)dzie, mus
Pobierz darmowy fragment (pdf)

Gdzie kupić całą publikację:

Wskaźniki w języku C. Przewodnik
Autor:

Opinie na temat publikacji:


Inne popularne pozycje z tej kategorii:


Czytaj również:


Prowadzisz stronę lub blog? Wstaw link do fragmentu tej książki i współpracuj z Cyfroteką: