В настоящата статия ще говорим за void указателите в езика C. Забележете – C, а не C++, тъй като указателите оригинално произлизат от C, а поддръжката им в C++ е изцяло наследена от C, и C като цяло е по-добре стандартизиран и по-прост език от C++. Въпреки това, концепциите, техниките и примерите, които ще покажа са почти напълно приложими и в C++.
И така, ако вече сте на “ти” с използването на указатели, а термина “void указател” ви звучи странно защото сте се заблудили, че всеки указател трябва да има тип, то тогава е време да научите нещо ново за невероятната гъвкавост която предоставя единствения по рода си език – C.
Един по-дълбок поглед върху указателите
Сами по себе си, всички указатели, независимо дали са декларирани от тип char *, int *, FILE *, struct mystruct *, mytype_t *, или от който и да е друг вграден или дефиниран от потребителя тип, имат фиксиран размер и на машинно ниво не се различават с нищо по между си. Размерите им са фиксирани, това обикновено е една машинна дума. На практика, в езика C всеки указател е с дължината на int, а int е една машинна дума (с дължина съответно 2/4/8 байта при 16/32/64 битови системи). Мислете за указателите като за нормални числови променливи, със свойството, че числото което съдържат е всъщност адрес в паметта. Сега сигурно се питате: защо в такъв случай указателите изобщо трябва да се декларират с тип? Всъщност, понятието “тип на указател” е една абстракция която показва на компилатора колко байта е дължината на соченият блок от памет (а не на самия указател!), като се започне от указания начален адрес. Когато указателя се “дереференцира”, т.е. извлича се сочената стойност, това има значение защото компилатора трябва да знае точно колко е дължината на сочения блок. Ако ви е трудно да си представите горната картина, разгледайте следното:
Паметта всъщност представлява един дълъг масив от байтове, където всеки байт (елемент) има адрес (индекс) и стойността на този байт може да се извлече като се индексира чрез адреса. Както сигурно знаете, един байт съдържа 8 бита, а това прави възможно съществуването на 2^8 = 256 = 0x100 различни стойности вътре.
Декларирали сме два указателя, myptr1 и myptr2. Както виждате, независимо че единият сочи към тип short, а другият към mytype, и двата са с дължина 4 байта (зелено оцветените клетки, приемаме 32 битова система). Стойността на всеки един от тези указатели всъщност е число, което представлява начален адрес на блок от паметта, съответно 533 (0x00000215) и 536 (0x00000218). Както всички останали променливи, дори и самите указатели имат адрес в паметта където са разположени – съответно 548 и 543. Приемаме също, че short е с дължина 2 байта, а mytype е потребителски дефиниран тип с дължина 5. От илюстрацията сигурно забелязвате, че единствената операция, заради която указателите трябва да имат тип е операцията “извличане на сочената стойност” или по-краткото “дереференциране”. С други думи, компилатора трябва да покаже на програмата с колко байта нататък от началния адрес да се чете, за да се извлече правилната стойност на променливата. За short това е 2, за mytype е 5 (синьо оцветените клетки).