Дороговкази в програмуванні, devprog блог для програмістів
На написання даної замітки мене підштовхнуло те, що в підручниках з програмування не приділяється достатньої уваги такій важливій темі, як покажчики. Тобто, увага, то звичайно, приділяється, але ось принцип навчання роботи з покажчиками або не дуже зрозумілий або, що ще гірше, взагалі не ясний. Таким чином, на різних форумах і конференціях з програмування кожен третій програміст просить допомогти йому розібратися з цією темою. Ця невелика замітка постарається заповнити цю прогалину. Всі приклади будуть на різних мовах програмування - так щоб всім було зрозуміло, незалежно від того, яку мову ви вивчаєте. Я так само припускаю, що ви знайомі хоча б з одним з трьох мов програмування - це Pascal \ Delphi, C ++ і Асемблер.
Як ви знаєте, кожен тип змінної має свій розмір. Так, наприклад, під тип integer - процесор виділяє 2 байта пам'яті, а під тип byte - 1 байт. Під longint - DWORD - тобто «подвійне слово», що означає 4 байта. Так, WORD - це 2 байта, або «слово». Так, один байт - це дві цифри від 00 до FF (в шістнадцятковій системі числення). Для кожного типу даних - свій розмір в пам'яті (який саме, можна дізнатися в довіднику за мовою).
Тобто, як ви вже зрозуміли, якщо дивитися по нашій таблиці, і якби ми описали змінну - покажчик на тип Integer, то вона б зайняла рівно 2 байта в пам'яті і наша таблиця, тобто пам'ять, прийняла б такий вигляд:
Для чого використовуються покажчики?
Як ви знаєте, виклик процедур і функцій в мовах програмування виконується наступним чином:
# іncludе <іostrеam.h>
Іnt maіn (int argc, char * argv [])
char * lpString; // оголосивши змінну-покажчик на тип char
char String [] = "Наша нова рядок»; // оголосивши масив (рядок)
cout < cout < return 0; // Кінець програми До речі, для того щоб взяти значення безпосередньо з осередку - використовується операція разименовиванія покажчика, шляхом приставки у вигляді символу «*» перед початком змінної. cout < cout <<*lpString < Те отримали б на виході всього один символ - «Н». Наведу приклади на інших мовах: begin program test1; До речі, зверніть увагу, як я виводжу рядок: program test1; Pointer_String: lpStr; vString: String; clrscr; Pointer_String: = @vString; writeln (Pointer_String ^); Тут, ми явно вказуємо типи вказівників. Тобто, наприклад, lpStr - це покажчик на тип String і тд. І при зверненні до разименованному вказівником, ми вже вільні не вказувати, на що саме посилається наш покажчик. Ну, і, врешті-решт, подивимося приклад на мові Асемблера з використанням поширеного компілятора - MASM32. Я не буду приводити весь код, а покажу тільки найважливіше: String db «Assembly string!», 00; Оголосили масив з нульовим байтом на кінці push offset String; кладемо в стек зміщення змінної String push 0; викликаємо вікно Count dd 00h; Як бачите, цей асемблер справляється з такими конструкціями дуже легко. Покажчики - прекрасний інструмент роботи з рядками Розробимо програму, яка б заміняла в рядку всі символи «*» на прогалини і друкувала результат в консолі. Варіант на Сі: # іncludе <іostrеam.h> voіd main () lpCharString = szText [0]; for (size = lstrlen (lpCharString); size! = 0; size-, lpCharString ++) if (* lpCharString == '*') * lpCharString = 0x20; cout <<*lpCharString; var for Counter: = 1 to iSize-2 do Як бачите теж все досить зручно і наочно. Але одну річ я все - таки поясню. Бачите, де в циклі я вказую -2 від загальної довжини? Так ось, формат Delphi рядків такий, що перед самою рядком варто поле типу WORD і в цьому полі міститься довжина рядка. Виглядає це ось так: А так як нам вони не потрібні, ми просто їх забираємо і все. Хоча якщо не забирати - все буде працювати звичайно. Але я вам просто показав, який формат Delphi рядків. В іншому, в цій програмі все повністю ідентично тій, яка запрограмована на Сі. З паскалем справи йдуть так само.
Тобто, якби ми замість рядка:
MessageBox (0, lpString, String1,0); // виводимо вікно з текстом але з різних джерел
end.
var str: string;
lpStr: pointer;
begin
lpStr: = @ str;
str: = 'Pascal pointer!';
writeln (string (lpStr ^));
readln;
dispose (lpStr);
lpStr: = nil;
end.
uses crt;
type
lpStr = ^ string;
lpInt = ^ integer;
lpReal = ^ real;
Pointer_Integer: lpInt;
Pointer_Real: lpReal;
vInteger: Integer;
vReal: Real;
vString: = 'Where is my pointer?';
vInteger: = 100;
vReal: = 456.3000;
Pointer_Integer: = @vInteger;
Pointer_Real: = @vReal;
writeln (Pointer_Integer ^);
writeln (Pointer_Real ^);
...
... тут ми чистимо наші покажчики ...
...
pop [lpString]; витягуємо її з стека і кладемо в lpString
push [lpString]; c покажчиками на рядки
push [lpString]
push HWND_DESKTOP
lpString dd 00h;
string db «Hello Wordl», 00
Ну, начебто з покажчиками розібралися. Тепер закріпимо все однією зручною таблицею і продовжимо працювати з покажчиками.
# іncludе
char * lpCharString;
char szText [] = "1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 0";
int a;
int size;
>
>
szStr: array [0..20] of Char;
lpStr: ^ Char;
iSize: Integer;
Counter: Integer;
begin
szStr: = '1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 0';
lpStr: = @szStr;
iSize: = Length (szStr);
begin
if lpStr ^ = '*' then begin
lpStr ^: = '';
end;
write (lpStr ^);
inc (lpStr);
end;
Readln;
end.Схожі статті