На этом шаге мы рассмотрим простой пример использования указателей.
Перед тем как рассмотреть первый пример, отметим некоторые особенности способа реализации программ, в которых используются указатели.
Как мы увидим далее, получив доступ к области памяти, мы автоматически получаем доступ и к соседним ячейкам. Формально это означает, что мы получаем возможность выполнять операции с памятью напрямую. Такие операции считаются небезопасными, поскольку исполнительная система, под управлением которой выполняются программы, не может проконтролировать безопасность выполнения этих операций. Поэтому соответствующий код считается небезопасным - основной груз ответственности по реализации корректной и неконфликтной работы программы ложится на плечи программиста. В языке C# небезопасный код выделяется в отдельные блоки, которые помечаются ключевым словом unsafe. Например, если в главном методе программы используются указатели, то главный метод можно описать с ключевым словом unsafe (или пометить этим ключевым словом блок, в котором указатели задействованы). Также следует изменить настройки приложения, разрешив использование небезопасного кода. Для этого на вкладке свойств проекта (можно открыть с помощью команды Properties (Свойства) в контекстном меню проекта или в главном меню Project (Проект)) в разделе сборки (раздел Build (Построение)) следует установить флажок опции, разрешающей использование небезопасного кода (опция Allow unsafe code (Разрешить небезопасный код)).
Теперь перейдем к рассмотрению примера, в котором используются указатели. Интересующая нас программа представлена в примере ниже.
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace pr206_1 { // Класс с главным методом: class Program { // Главный метод (описан с ключевым словом unsafe): unsafe static void Main() { // Объявление целочисленной переменной: int n; // Объявление указателя на значение типа int: int* p; // Присваивание указателю значения: p = &n; // Через указатель присваивается значение переменной: *p = 123; // Отображение значения переменной: Console.WriteLine("Значение переменной n={0}", n); Console.WriteLine("Значение выражения *p={0}", *p); Console.WriteLine("Адрес переменной n: {0}", (uint)p); Console.WriteLine(); // Объявление указателя на значение типа byte: byte* q; // Объявление указателя на значение типа char: char* s; // Присваивание указателей и явное приведение типа: q = (byte*)p; s = (char*)p; // Переменной присваивается новое значение: n = 65601; // Проверка значений указателей: Console.WriteLine("Адрес в указателе p: {0}", (uint)p); Console.WriteLine("Адрес в указателе q: {0}", (uint)q); Console.WriteLine("Адрес в указателе s: {0}", (uint)s); Console.WriteLine(); // Отображение значения через указатель: Console.WriteLine("Значение типа int: {0}", *p); Console.WriteLine("Значение типа byte: {0}", *q); Console.WriteLine("Значение типа char: \'{0}\'", *s); Console.WriteLine("Значение переменной n={0}", n); Console.WriteLine(); // Присваивание значения через указатель: *s = 'F'; // Проверка значений: Console.WriteLine("Значение типа int: {0}", *p); Console.WriteLine("Значение типа byte: {0}", *q); Console.WriteLine("Значение типа char: \'{0}\'", *s); Console.WriteLine("Значение переменной n={0}", n); // Задержка: Console.ReadLine(); } } }
Для того чтобы скомпилировать эту программу, после того как создан проект и введен программный код, следует открыть окно свойств проекта. Например, в пункте меню Project (Проект) выбрать команду Properties (Свойства), как показано на рисунке 1.
Рис.1. В пункте меню Project (Проект) выбирается команда Properties (Свойства)
Должна открыться вкладка свойств проекта, как показано на рисунке 2.
Рис.2. Настройка режима использования небезопасного кода на вкладке свойств проекта
Там следует выбрать раздел Build (Построение) и установить флажок опции Allow unsafe code (Разрешить небезопасный код) (рисунок 2). После этого вкладку свойств проекта можно закрыть. Для компилирования и запуска программы на выполнение используем команду Start Without Debugging (Запуск без отладки) из меню Debug (Отладка) (рисунок 3) или нажимаем комбинацию клавиш Ctrl+F5.
Рис.3. Запуск программы на выполнение
Результат выполнения программы представлен ниже:
Рис.4. Результат выполнения программы
Главный метод программы описывается с ключевым словом unsafe. В теле метода объявляется переменная n типа int, а также указатель p на значение типа int. Командой
p = &n;
Значение переменной n присваивается не напрямую, а через указатель p. Для этого мы использовали команду
*p = 123; .
Кроме указателя р, в программе командами
byte* q;
char* s;
q = (byte*)p;
s = (char*)p;
Командой
n = 65601;
65 601 = 65 536 + 64 + 1 = 216 + 26 + 20.
00000000 00000001 00000000 01000001.
При вычислении значения выражения *q используется только первый байт с кодом 01000001. Этот код интерпретируется как неотрицательное целочисленное значение. Если перевести данный двоичный код в десятичный, то получим число 65.
Наконец, значение выражения *s вычисляется по двум байтам (первому и второму). Получаем бинарный код 00000000 01000001, в котором второй байт нулевой (состоит из одних нулей). Поэтому формально бинарный код соответствует числу 65. Но поскольку s является указателем на символьное значение, то результат интерпретируется как символ. В кодовой таблице символов код 65 имеет буква 'A' (английский алфавит).
При выполнении команды
*s = 'F';
Когда вычисляется значение выражения *q, то используется только первый байт с кодом 01000110, означающим число 70. При проверке значения выражения *s естественным образом получаем значение 'F'.
На следующем шаге мы рассмотрим адресную арифметику.