Jak se C ukazatele vlastně mapují do paměti pole

8

V C jdou pole a ukazatele ruku v ruce. Bez pochopení jejich vztahů nemůžete napsat efektivní kód. Zvládnout to chce čas. Ale odměna – okamžitá kontrola paměti – přichází okamžitě.

Podívejme se na základní příklad. Deklarujete dvě celočíselná pole o velikosti deset.

Zkuste tento kód zkompilovat. Nepřeloží se. Jedno pole nelze přiřadit druhému pomocí znaménka rovná se. C nepodporuje kopírování datových bloků prostřednictvím příkazů přiřazení. Chcete-li zkopírovat a do b, musíte projít každý prvek.

Tuto smyčku byste mohli zkomprimovat do jednoho řádku pomocí čárky, ale to by bylo méně čitelné. Standardním přístupem je použití funkce memcpy z hlavičkového souboru string.h. Efektivně provádí kopírování byte po byte.

Selhání přiřazení b=a odhaluje základní pravidlo. V C nejsou proměnné pole samy o sobě pole. Jedná se o pevné ukazatele. a a b jsou konstantní identifikátory, které ukazují na počáteční adresu jejich paměťových bloků. a ukazuje na „a[0]“. b ukazuje na „b[0]“. Tyto adresy jsou neměnné. Název proměnné nelze přesměrovat na jiné místo v paměti. To je důvod, proč se zadání nezdaří. Kompilátor považuje název pole za konstantní ukazatel.

Protože název je v podstatě ukazatel, můžete pro přístup k datům použít aritmetiku ukazatele. Zde se syntaxe stává mocným nástrojem.

Zde je p proměnná ukazatele. Proměnnou p můžete přiřadit adresu pole a. Výraz p=a zkopíruje adresu. Nyní p obsahuje stejnou hodnotu jako název pole a. Dereferencování p pomocí *p vrátí první prvek. Výstup bude nulový.

Toto rozlišení je důležité, protože ukazatelové proměnné lze upravit. Pomocí aritmetiky můžete přinutit p, aby ukazovalo na a[1] nebo a[5]. Samotný název pole zůstává pevný na začátku. Tato flexibilita umožňuje funkcím procházet pamětí bez kopírování velkých datových struktur. To je základ nízkoúrovňového výkonu v C.

Pochopení tohoto spojení zabrání jemným chybám. Vysvětluje, proč některé funkce přijímají ukazatele místo syntaxe pole. Objasňuje strukturu paměti. Tyto znalosti zanedbávejte a budete se potýkat s chybami segmentace nebo neúmyslným přepsáním dat. Zvládněte to a uvidíte, co přesně stroj dělá.

Syntaxe se může na první pohled zdát neobvyklá. Dvojí význam hranatých závorek a hvězdiček začátečníky mate. Ale když se model vyjasní, abstrakce zmizí. Zůstávají pouze adresy a hodnoty. Nic víc.

Stojí to za námahu? Kód je rychlejší. Sníží se spotřeba paměti. Získáte přímý přístup k operacím na úrovni zařízení. Většina programů C spoléhá

Proměnnou pole můžete přímo přiřadit ukazateli, protože pod kapotou jsou v podstatě totéž.

p=a; nefunguje kvůli magii, ale protože se a rozpadne na ukazatel. Konkrétně ukazuje na paměťovou adresu prvního prvku pole (s indexem 0). Protože tento prvek je celé číslo, a funguje jako ukazatel na jediné celé číslo. Deklarování p jako ukazatel na celé číslo a přiřazení hodnoty a jednoduše dává dvěma proměnným sdílenou adresu. Místo toho můžete napsat p=&a[0];. Výsledek bude identický.

Jakmile je p připojeno k začátku a, můžete se pohybovat. Názvy polí jako a jsou konstanty; Jedná se o pevné adresy, které nelze změnit. Ukazatele jako p se chovají jinak. Můžete je pohybovat pomocí aritmetiky ukazatele.

Tady C ukazuje svou vynalézavost.

Když napíšete p++;, kompilátor k adrese nepřidá jen jeden bajt. Ví, že p ukazuje na celé číslo. Místo toho přidá sizeof(int) bajtů a zasáhne přesně další prvek. Pokud by p ukazovalo na strukturu o délce 100 bajtů, p++ by posunulo ukazatel dopředu o 100 bajtů. C dělá všechny výpočty za vás. Jednoduše pohnete ukazatelem.

Kopírování polí bez indexů

Pomocí těchto ukazatelů můžete zkopírovat jedno pole do druhého. Místo standardní smyčky s indexy:

Můžete použít ukazatele:

Je to podrobné. Dá se komprimovat.

Nebo vše zkomprimujte do jednoho řádku.

To je místo, kde operátoři dělají těžkou práci. Operátor * má vyšší prioritu než ++, ale po přírůstku dojde po přijetí hodnoty. Takže *p++ získá aktuální hodnotu a poté přesune ukazatel. *q++ dělá totéž pro cíl. Mezi těmito akcemi dochází k přiřazení. Je kompaktní. Je to rychlé. Ale je také snadné jej špatně přečíst, pokud si nedáte pozor.

Nebezpečná zóna

Co se stane, když z pole vyjmete p nebo q?

C tě nezastaví.

Kompilátor předpokládá, že víte, co děláte. Bude pokračovat ve zvyšování ukazatelů a kopírování dat do paměti, která nepatří do pole. Ostatní proměnné můžete přepsat. Můžete zaneřádit hromadu. Můžete způsobit selhání programu později na místě, které s chybou nesouvisí.

To je důvod, proč se kontrola hranic v C provádí ručně. Jazyk vám důvěřuje. Když překročíte pravidla, zaplatíte cenu.

Předávání polí funkcím

Pole můžete funkcím předat dvěma způsoby. Vypadají jinak, ale chovají se podobně.

Zvažte funkci, která vydává pole:

Nebo verze s ukazateli:

Je vyžadován parametr nia (počet prvků v poli). Pole v C neukládají svou velikost s sebou. Funkce musí vědět, kdy se zastavit.

V obou případech předáváte ukazatel na začátek pole, nikoli