На этом шаге мы рассмотрим создание и использование таких массивов.
Указатели можно использовать практически так же, как и другие переменные. Нужно только помнить, что при использовании указателей мы имеем дело с адресами. С другой стороны, использование указателей иногда позволяет привнести в программу некоторую пикантность. В примере ниже представлена программа, в которой задействованы массивы указателей.
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace pr215_1 { class Program { unsafe static void Main() { // Переменные: int x = 100, y = 200, z = 300; // Массив указателей на целочисленные значения: int*[] nums = new int* [3]; // Значение первого элемента массива: nums[0] = &x; // Значение второго элемента массива: nums[1] = &y; // Значение третьего элемента массива: nums[2] = &z; // Отображение значений переменных: Console.WriteLine("Числа: {0}, {1} и {2}", nums[0][0], *nums[1], nums[2][0]); // Массив указателей на символьные значения: char*[] symbs = new char* [3]; // Значение первого элемента массива: symbs[0] = (char*)&x; // Значение второго элемента массива: symbs[1] = (char*)&y; // Значение третьего элемента массива: symbs[2] = (char*)&z; // Символьная переменная: char s = 'A'; // Заполнение значениями области памяти, // выделенной под переменные: for(int i = 0; i < symbs.Length; i++) { for(int j = 0; j < sizeof(int)/sizeof(char); j++) { // В блок памяти записывается значение: symbs[i][j] = s; // Новое значение символьной переменной: s++; // Отображение значения из блока памяти: Console.Write(symbs[i][j] + " "); } Console.WriteLine(); } // Проверка результата: Console.WriteLine("Числа: {0}, {1} и {2}", x, y, z); Console.WriteLine("Проверка: {0}, {1} и {2}", nums[0][0], *nums[1], nums[2][0]); Console.WriteLine("Еще раз: {0}, {1} и {2}", *(int*)symbs[0], *(int*)symbs[1],*(int*)symbs[2]); Console.WriteLine("Для сравнения: {0}, {1} и {2}", *symbs[0], *symbs[1], *symbs[2]); } } }
В результате выполнения программы в консольном окне отображаются следующие сообщения:
Рис.1. Результат выполнения программы
Проанализируем программный код и результат его выполнения. Мы объявляем три целочисленные переменные x, у и z (со значениями 10 0, 200 и 300 соответственно). Массив из трех указателей на целочисленные значения создается командой
int*[] nums = new int* [3]; .
Элементы массива nums являются указателями, и в качестве значений им можно присваивать адреса целочисленных переменных, что мы и делаем с помощью команд
nums[0] = &x; , nums[1] = &y; и nums[2] = &z; .
Таким образом, nums[0] - это адрес переменной x, nums[1] - это адрес переменной у, а nums[2] - это адрес переменной z. С учетом правил адресной арифметики получаем, что nums[0][0] - это значение переменной x, *nums[1] - это значение переменной у, а nums[2][0] - это значение переменной z.
Командой
char*[] symbs = new char* [3];
symbs[0] = (char*)&x; , symbs[1] = (char*)&y; и symbs[2] = (char*)&z; .
Область памяти, выделенная под импровизированный двумерный символьный массив, используется также при работе с массивом nums, и в нее же записаны значения переменных x, у и z. Например, значение переменной x можно узнать по имени этой переменной с помощью инструкции nums[0][0] или с помощью выражения *(int*)symbs[0]. В последнем случае следует учесть, что symbs[0] есть указатель на первые два байта из области памяти, выделенной под переменную х. Выражение (int*)symbs[0] с явным приведением типа есть указатель на все четыре байта области, выделенной под переменную х. А выражение *(int*)symbs[0] - это значение в области памяти, выделенной под переменную х. Что касается значения переменной х, то в первые два байта этой переменной записывался код символа 'A' (это число 65), а в два других байта записан код символа 'B' (это число 66). Если бинарный код в четырех байтах интерпретировать как значение типа int, то с учетом того, что смещение на два однобайтовых блока означает умножение на 216 = 65 536, то получаем 65 + 66 * 65 536 = 4 325 441. Ну а значение выражения *symbs[0] - это символ 'A', записанный в первые два байта области памяти, выделенной под переменную х.
На следующем шаге мы резюмируем все ранее сказанное по данной теме на предыдущих шагах.