Шаг 212.
Язык программирования C#. Начала.
Указатели. Указатели и массивы

    На этом шаге мы рассмотрим связь указателей и массивов.

    Указатели в языке C# тесно связаны с массивами. Точнее, при работе с массивами весьма эффектно могут использоваться указатели. Чтобы понять, почему такое возможно, необходимо учесть несколько обстоятельств. А именно:

    Зная имя массива, мы можем получить доступ к области памяти, в которую записан первый (начальный) элемент. Поскольку элементы массива расположены подряд, то, индексируя указатель (или выполняя иные операции адресной арифметики), мы можем получить доступ к любому элементу массива. Правда автоматического контроля на предмет выхода за пределы массива уже не будет. Инструкция fixed в данном случае нужна для того, чтобы гарантировать неизменность места размещения массива в памяти (это позволяет избежать перемещения массива при оптимизации использования ресурсов). Небольшой пример, в котором совместно используются указатель и массив, представлен в примере ниже.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace pr212_1
{
    // Класс с главным методом: 
    class Program
    {
        // Главный метод:
        unsafe static void Main()
        {
            // Размер массива: 
            int size = 10;
            // Объявление указателя: 
            int* q;
            // Создание массива: 
            int[] nums = new int[size];
            Console.WriteLine("Заполнение массива:");
            // Используем fixed-блок: 
            fixed(int* p = nums){
                // Указателю присваивается значение: 
                q = p + size - 1;
                // Перебор элементов массива: 
                for(int k = 0; k < nums.Length; k++){
                    // Элементу массива присваивается значение: 
                    p[k] = k + 1;
                    // Отображение значения элемента массива:
                    Console.Write("|" + nums[k]);
                }
                Console.WriteLine("|");
                // Перебор элементов массива с помощью указателя: 
                while(p <= q) {
                    // Возведение значения элемента в квадрат:
                    (*q) *= (*q);
                    // Изменение значения указателя:
                    q--;
                }
            }
            // Отображение содержимого массива:
            Console.WriteLine("Maссив после изменения:"); 
            for(int k = 0;k < nums.Length; k++){
                // Отображение значения элемента:
                 Console.Write("|" + nums[k]);
            }
            Console.WriteLine("|");
        }
    }
}
Архив проекта можно взять здесь.

    Как выглядит результат выполнения программы, показано ниже:


Рис.1. Результат выполнения программы

    В этой программе мы создаем целочисленный массив nums. Размер массива определяется переменной size (значение 10). Также мы объявляем указатель q на целочисленное значение. Затем используется fixed-блок, в котором объявляется указатель p на целое число, а значением указателю присваивается имя массива nums. Поскольку имя массива является указателем на начальный элемент массива, то указатель p ссылается на этот же начальный элемент. Командой

  q = p + size - 1; 
присваивается значение указателю q. Учитывая, что переменная size определяет размер массива, а указатель p содержит ссылку на начальный элемент массива, несложно сообразить, что указатель q будет ссылаться на последний элемент в массиве.

    Для заполнения массива использована конструкция цикла for, в котором индексная переменная k последовательно принимает значения индексов элементов массива nums. При этом значения элементам массива присваиваются командой

  p[k] = k + 1;     . 
Поскольку указатель р, как отмечалось выше, ссылается на начальный элемент массива, то из правила индексирования указателей следует, что выражение вида p[k] аналогично выражению nums[k]. Убеждаемся в этом, отображая в консольном окне значения элементов массива.

    Еще раз элементы массива перебираются с помощью цикла while. Эта конструкция цикла выполняется, пока истинно условие p<=q (адрес в указателе p не превышает адрес в указателе q).


Напомним, что в начальный момент указатель p ссылается на начальный элемент массива, а указатель q ссылается на последний элемент массива.

    За каждый цикл командой

  (*q) *= (*q); 
текущее значение в элементе, на который ссылается указатель q, умножается на себя же (то есть значение элемента возводится в квадрат), после чего командой
  q--; 
указатель q перебрасывается на предыдущий элемент в массиве. Проверка показывает, что после выполнения цикла все элементы массива действительно возведены в квадрат.

    На следующем шаге мы рассмотрим связь указателей и текста.




Предыдущий шаг Содержание Следующий шаг