*(р+6) : 6
*(р+7) : 7
*(р+8) : 8
*(р+9) : 9
Как следует из результата выполнения приведенной выше программы, общая форма выражения с указателем
*(ptr + i)
может быть заменена следующим синтаксисом индексирования массива.
ptr[i]
Что касается индексирования указателей, то необходимо иметь в виду следующее. Во-первых, при таком индексировании контроль границ массива не осуществляется. Поэтому указатель может обращаться к элементу вне границ массива. И во-вторых, для указателя не предусмотрено свойство Length
, определяющее длину массива. Поэтому, если используется указатель, длина массива заранее неизвестна.
Символьные строки реализованы в C# в виде объектов. Тем не менее отдельные символы в строке могут быть доступны по указателю. Для этого указателю типа char*
присваивается адрес начала символьной строки в следующем операторе с модификатором fixed
.
fixed(char* р = str) { // ...
После выполнения оператора с модификатором fixed
переменная р
будет указывать на начало массива символов, составляющих строку. Этот массив оканчивается символом конца строки, т.е. нулевым символом. Поэтому данное обстоятельство можно использовать для проверки конца массива. В C/C++ строки реализуются в виде массивов, оканчивающихся символом конца строки, а следовательно, получив указатель типа char*
на строку, ею можно манипулировать таким же образом, как и в C/C++.
Ниже приведена программа, демонстрирующая доступ к символьной строке по указателю типа char*
.
// Использовать модификатор fixed для получения
// указателя на начало строки.
using System;
class FixedString {
unsafe static void Main() {
string str = "это тест";
// Получить указатель р на начало строки str.
fixed(char* p = str) {
// Вывести содержимое строки str по указателю р.
for(int i=0; p[i] != 0; i++)
Console.Write(p[i]);
Console.WriteLine();
}
}
}
Эта программа дает следующий результат.
это тест
Один указатель может указывать на другой, а тот, свою очередь, — на целевое значение. Это так называемая
Многоуровневая непрямая адресация может быть продолжена до любого предела, но потребность более чем в двух уровнях адресации по указателям возникает крайне редко. На самом деле чрезмерная непрямая адресация очень трудно прослеживается и чревата ошибками.
Переменная, являющаяся указателем на указатель, должна быть объявлена как таковая. Для этого достаточно указать дополнительный знак * после имени типа переменной. Например, в следующем объявлении компилятор уведомляется о том, что переменная q является указателем на указатель и относится к типу int
.
int** q;
Следует, однако, иметь в виду, что переменная q является указателем не на целое значение, а на указатель типа int
.
Для доступа к целевому значению, косвенно адресуемому по указателю на указатель, следует дважды применить оператор *
, как в приведенном ниже примере.
using System;
class Multiplelndirect {
unsafe static void Main() {
int x; // содержит значение типа int
int* p; // содержит указатель типа int
int** q; // содержит указатель на указатель типа int
x = 10;
p = &x // поместить адрес переменной х в переменной р
q = &p // поместить адрес переменной р в переменной q
Console.WriteLine(**q); // вывести значение переменной х
}
}
Результатом выполнения этой программы будет выведенное на экран значение 10 переменной х. В данной программе переменная р объявляется как указатель на значение типа int, а переменная q — как указатель на указатель типа int
.
И последнее замечание: не путайте многоуровневую непрямую адресацию со структурами данных высокого уровня, в том числе связными списками, так как это совершенно разные понятия.