Шаг 213.
Язык программирования C#. Начала.
Указатели. Указатели и текст
На этом шаге мы рассмотрим связь указателей и текстовых значений.
Как мы знаем, текстовые значения в языке C# реализуются в виде объектов класса String. Стандартная схема такая: имеется объектная переменная
класса String, которая ссылается на объект этого же класса, а объект, в свою очередь, содержит текстовое значение. И, как мы помним, ранее утверждалось,
что текстовые объекты неизменны: нельзя поменять текст в текстовом объекте, а можно только создать новый объект и ссылку на этот новый объект записать в объектную
переменную. Создается иллюзия, что текст изменился.
Реальность немного сложнее. Технически текст реализуется в виде символьного массива. Эта "традиция" уходит корнями в языки C и C++. В этих
языках в конце такого символьного массива размещается нуль-символ '\0', который служит индикатором окончания текста. Данный символ (не следует его
путать с символьным изображением цифры ноль - это не одно и то же) имеет нулевой код и добавляется в конец символьного массива автоматически. Но это, еще
раз подчеркнем, в языках C и C++. В языке C# упомянутый выше символьный массив "спрятан" в текстовом объекте, на который ссылается
переменная класса String. И хотя в целях совместимости обычно в конце символьного массива нуль-символ '\0' добавляется, никакой особой роли в
C# он не играет. Размер текста запоминается отдельно, а нуль-символ обрабатывается, как и все прочие символы.
Резюмируем. Текстовая строка в языке C# реализуется как объект. В этом объекте "спрятан" символьный массив. Массив содержит символы из текста.
Обычно в конце символьного массива добавляется нуль-символ, чтобы обеспечить корректную обработку текста в случае совместного использования разных
технологий. Но этот нуль-символ не является частью текстовой строки. Также нуль-символ не является индикатором окончания текста. Размер текстовой строки
запоминается в виде целого числа, которое тоже "спрятано" в текстовом объекте.
Используя указатели, мы можем "проникнуть" в текстовый объект и получить прямой доступ к элементам символьного массива, который фактически содержит
текст. С помощью указателей мы можем не только прочитать отдельные символы в массиве, но также и изменить их непосредственно в текстовом объекте.
Пример, в котором иллюстрируется данный подход, представлен в примере ниже.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace pr213_1
{
// Класс с главным методом:
class Program
{
// Главный метод:
unsafe static void Main()
{
// Текстовая строка:
String txt = "Программируем на C#";
// Отображение текста:
Console.WriteLine(txt);
// Указатель на начальный элемент строки:
fixed(char* p = txt) {
// Перебор символов строки:
for(int k = 0; p[k] != '\0'; k++){
// Отображение символа:
Console.Write("|" + p[k]);
// Изменение значения символа в объекте:
p[k] = (char)('A' + k);
}
Console.WriteLine("|");
}
// Отображение текста:
Console.WriteLine(txt);
}
}
}
Архив проекта можно взять
здесь.
Результат выполнения программы будет следующим:
Рис.1. Результат выполнения программы
Мы объявляем текстовую переменную txt со значением "Программируем на C#". В fixed-блоке объявляется символьный указатель p,
которому в качестве значения присваивается переменная txt. В результате в указатель p записывается адрес начального элемента символьного массива,
содержащего текст. Получается так: текстовая переменная txt содержит ссылку на объект, который содержит символьный массив с текстом (символы из
текста являются элементами массива). Указатель p ссылается на начальный элемент в этом символьном массиве. Индексируя указатель в цикле,
последовательно получаем доступ к каждому элементу в символьном массиве. Оператор конструкция цикла, пока истинно условие p[k]!='\0', то есть
пока не будет прочитан нуль-символ. За каждую итерацию цикла сначала отображается текущее символьное значение p[k] элемента массива, а затем
командой
этому элементу присваивается новое значение. В результате в текстовом объекте, на который ссылается переменная
txt, символы текста меняются на цепочку
символов из алфавита, начиная с буквы
'A'.
Выше мы исходили из того, что символьный массив, через который реализуется текст, завершается нуль-символом '\0'. Но это не самый надежный
критерий проверки окончания текста. Более того, теоретически сам текст может содержать в себе нуль-символ. Поэтому в общем случае разумнее использовать
свойство Length.
На следующем шаге мы рассмотрим многоуровневую адресацию.
Предыдущий шаг
Содержание
Следующий шаг