Глава 4
210 }
//
Ⱦɟɫɬɪɭɤɬɨɪ:
MyClass(){
Console.WriteLine(
Ǝɍɞɚɥɟɧ ɨɛɴɟɤɬ ɫ ɩɨɥɟɦ Ǝ+number);
}
}
//
Ƚɥɚɜɧɵɣ ɤɥɚɫɫ ɩɪɨɝɪɚɦɦɵ:
class FixedDemo{
//
Ƚɥɚɜɧɵɣ ɦɟɬɨɞ:
unsafe static void Main(){
//
ɂɫɩɨɥɶɡɨɜɚɧ fixed-ɛɥɨɤ:
fixed(int* p=&new MyClass(123).number){
//
Ɉɬɨɛɪɚɠɟɧɢɟ ɡɧɚɱɟɧɢɹ ɩɨɥɹ:
Console.WriteLine(
ƎɁɧɚɱɟɧɢɟ ɩɨɥɹ number: Ǝ+*p);
//
ɉɨɥɸ ɩɪɢɫɜɚɢɜɚɟɬɫɹ ɧɨɜɨɟ ɡɧɚɱɟɧɢɟ:
*p=321;
//
Ɉɬɨɛɪɚɠɟɧɢɟ ɫɨɨɛɳɟɧɢɹ:
Console.WriteLine(
ƎɁɚɜɟɪɲɟɧɢɟ fixed-ɛɥɨɤɚƎ);
}
Результат выполнения программы такой:
Результат выполнения программы (из листинга 4.5)Ɂɧɚɱɟɧɢɟ ɩɨɥɹ number: 123
Ɂɚɜɟɪɲɟɧɢɟ fixed-ɛɥɨɤɚ
ɍɞɚɥɟɧ ɨɛɴɟɤɬ ɫ ɩɨɥɟɦ В программе описывается классу которого есть открытое целочисленное поле number, конструктор с одним аргументом (определяет значение поля, а также деструктор, который при удалении объекта из памяти выводит в консоль соответствующее сообщение со значением поля number удаляемого объекта
Указатели
211Главный метод состоит из блока, в котором объявлен указатель на целочисленное значение, и этому указателю в качестве значения присвоено выражение &new MyClass(123).number. Проанализируем его. Инструкция new MyClass(123) создает объект класса
MyClass
, и поле number этого объекта равно 123. Значением инструкции является ссылка на созданный объект. Эта ссылка никуда не записывается, и поэтому объект является анонимным. Сразу после создания он попадает в очередь на удаление. Точнее, должен был бы попасть. Инструкцией new MyClass(123).number мы обращаемся к полю number этого объекта, а символ & перед всем выражением означает, что нас интересует адрес этого поля. Таким образом, значением выражения
&new
MyClass(123).number является адрес поля number
созданного анонимного объекта, и этот адрес записывается в указатель p. В итоге объект, хотя они анонимный, гарантированно не удаляется из памяти, пока выполняется fixed-блок.
Значение поля объекта проверяем с использованием выражения *p. Командой *p=321 полю объекта присваивается новое значение. После этого завершается выполнение блока (об этом выводится сообщение) и завершается выполнение главного метода. Но перед этим созданный объект удаляется из памяти. Для данного объекта вызывается деструктор, ив консольном окне появляется сообщение со значением поля удаляемого объекта. Убеждаемся, что речь идет о том объекте, на поле number которого ссылался указатель Указатели и массивы
Живьем брать демонов!
из к/ф Иван Васильевич меняет профессию»Указатели в языке C# тесно связаны с массивами. Точнее, при работе с массивами весьма эффектно могут использоваться указатели. Чтобы понять, почему такое возможно, необходимо учесть несколько обстоятельств. А именно Имя массива является указателем на первый элемент массива В памяти элементы массива расположены подряд, один за другим Указатель на массив определяется в блоке fixed
Глава Зная имя массива, мы можем получить доступ к области памяти, в которую записан первый (начальный) элемент. Поскольку элементы массива расположены подряд, то, индексируя указатель (или выполняя иные операции адресной арифметики, мы можем получить доступ к любому элементу массива. Правда автоматического контроля на предмет выхода за пределы массива уже не будет. Инструкция fixed в
данном случае нужна для того, чтобы гарантировать неизменность места размещения массива в памяти (это позволяет избежать перемещения массива при оптимизации использования ресурсов. Небольшой пример, в котором совместно используются указатель и массив, представлен в листинге Листинг 4.6. Указатели и массивы System;
//
Ʉɥɚɫɫ ɫ ɝɥɚɜɧɵɦ ɦɟɬɨɞɨɦ:
class ArrayAndPointerDemo{
//
Ƚɥɚɜɧɵɣ ɦɟɬɨɞ:
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
//
ɗɥɟɦɟɧɬɭ ɦɚɫɫɢɜɚ ɩɪɢɫɜɚɢɜɚɟɬɫɹ ɡɧɚɱɟɧɢɟ:
p[k]=k+1;
//
Ɉɬɨɛɪɚɠɟɧɢɟ ɡɧɚɱɟɧɢɹ ɷɥɟɦɟɧɬɚ ɦɚɫɫɢɜɚ:
Console.Write(
Ǝ|Ǝ+nums[k]);
Указатели }
Console.WriteLine(
Ǝ|Ǝ);
//
ɉɟɪɟɛɨɪ ɷɥɟɦɟɧɬɨɜ ɦɚɫɫɢɜɚ ɫ ɩɨɦɨɳɶɸ ɭɤɚɡɚɬɟɥɹ:
while(p<=q){
//
ȼɨɡɜɟɞɟɧɢɟ ɡɧɚɱɟɧɢɹ ɷɥɟɦɟɧɬɚ ɜ ɤɜɚɞɪɚɬ:
(*q)*=(*q);
//
ɂɡɦɟɧɟɧɢɟ ɡɧɚɱɟɧɢɹ ɭɤɚɡɚɬɟɥɹ:
q--;
}
}
//
Ɉɬɨɛɪɚɠɟɧɢɟ ɫɨɞɟɪɠɢɦɨɝɨ ɦɚɫɫɢɜɚ:
Console.WriteLine(
ƎɆɚɫɫɢɜ ɩɨɫɥɟ ɢɡɦɟɧɟɧɢɹ:Ǝ);
for(int k=0;k //
Ɉɬɨɛɪɚɠɟɧɢɟ ɡɧɚɱɟɧɢɹ ɷɥɟɦɟɧɬɚ:
Console.Write(
Ǝ|Ǝ+nums[k]);
}
Console.WriteLine(
Ǝ|Ǝ);
Как выглядит результат выполнения программы, показано ниже:
Результат выполнения программы (из листинга 4.6)
Ɂɚɩɨɥɧɟɧɢɟ ɦɚɫɫɢɜɚ:
|1|2|3|4|5|6|7|8|9|10|
Ɇɚɫɫɢɜ ɩɨɫɥɟ В этой программе мы создаем целочисленный массив nums. Размер массива определяется переменной size (значение 10). Также мы объявляем указатель q на целочисленное значение. Затем используется fixed- блок, в котором объявляется указатель p на целое число, а значением указателю присваивается имя массива nums. Поскольку имя массива
Глава является указателем на начальный элемент массива, то указатель p ссылается на этот же начальный элемент. Командой q=p+size-1 присваивается значение указателю q. Учитывая, что переменная size определяет размер массива, а указатель p содержит ссылку на начальный элемент массива, несложно сообразить, что указатель q будет ссылаться на последний элемент в массиве.
Для заполнения массива использован оператор цикла for, в котором индексная переменная k последовательно принимает значения индексов элементов массива nums. При этом значения элементам массива присваиваются командой p[k]=k+1. Поскольку указатель p, как отмечалось выше, ссылается на начальный элемент массива, то из правила индексирования указателей следует, что выражение вида p[k] аналогично выражению nums[k]. Убеждаемся в этом, отображая в консольном окне значения элементов массива.
Еще раз элементы массива перебираются с помощью оператора цикла while
. Оператор цикла выполняется, пока истинно условие p<=q (адрес в указателе p не превышает адрес в указателе q).
{
i НАЗ А МЕТКУ Напомним, что в начальный момент указатель p
ссылается на начальный элемент массива, а указатель q
ссылается на последний элемент массива.
За каждый цикл командой (*q)*=(*q) текущее значение в элементе, на который ссылается указатель q, умножается на себя же (то есть значение элемента возводится в квадрат, после чего командой q-- указатель q
перебрасывается на предыдущий элемент в массиве. Проверка показывает, что после выполнения оператора цикла все элементы массива действительно возведены в квадрат.
Указатели и текст
Зина, подскажи мне что-нибудь по-славянски!
из к/ф Иван Васильевич меняет профессию»
Как мы знаем, текстовые значения в языке C# реализуются в виде объектов класса String. Стандартная схема такая имеется объектная переменная класса String, которая ссылается на объект этого же класса,
Указатели
215а объект, в свою очередь, содержит текстовое значение. И, как мы помним, ранее утверждалось, что текстовые объекты неизменны нельзя поменять текст в текстовом объекте, а можно только создать новый объект и ссылку на этот новый объект записать в объектную переменную. Создается иллюзия, что текст изменился.
Реальность немного сложнее. Технически текст реализуется в виде символьного массива. Эта традиция уходит корнями в языки C и C++. В этих языках в конце такого символьного массива размещается нуль-сим- вол
ƍ\0ƍ, который служит индикатором окончания текста. Данный символ (не следует его путать с символьным изображением цифры ноль — это не одно и тоже) имеет нулевой код и добавляется вконец символьного массива автоматически. Но это, еще раз подчеркнем, в языках C и C++. В языке
C# упомянутый выше символьный массив спрятан в текстовом объекте, на который ссылается переменная класса String. И хотя в целях совместимости обычно в конце символьного массива нуль-символ
ƍ\0ƍ добавляется, никакой особой роли вон не играет. Размер текста запоминается отдельно, а нуль-символ обрабатывается, как и все прочие символы ПОДРОБНОСТИ bРезюмируем. Текстовая строка в языке C# реализуется как объект. В этом объекте спрятан символьный массив. Массив содержит символы из текста. Обычно в конце символьного массива добавляется нуль-символ, чтобы обеспечить корректную обработку текста в случае совместного использования разных технологий. Но этот нуль-символ не является частью текстовой строки. Также нуль-символ не является индикатором окончания текста. Размер текстовой строки запоминается в виде целого числа, которое тоже спрятано в текстовом объекте.
Используя указатели, мы можем проникнуть в текстовый объект и получить прямой
доступ к элементам символьного массива, который фактически содержит текст. С помощью указателей мы можем не только прочитать отдельные символы в массиве, но также и изменить их непосредственно в текстовом объекте. Пример, в котором иллюстрируется данный подход, представлен в листинге Листинг 4.7. Указатели и текст System;
//
Ʉɥɚɫɫ ɫ ɝɥɚɜɧɵɦ ɦɟɬɨɞɨɦ:
class StringAndPointerDemo{
Глава 4
216
//
Ƚɥɚɜɧɵɣ ɦɟɬɨɞ:
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);
Результат выполнения программы будет следующим:
Результат выполнения программы (из листинга 4.7)
ɉɪɨɝɪɚɦɦɢɪɭɟɦ ɧɚ C#
|
ɉ|ɪ|ɨ|ɝ|ɪ|ɚ|ɦ|ɦ|ɢ|ɪ|ɭ|ɟ|ɦ| |ɧ|ɚ| Мы объявляем текстовую переменную txt со значением
Ǝɉɪɨɝɪɚɦɦɢ-
ɪɭɟɦ ɧɚ C#Ǝ. В блоке объявляется символьный указатель p, которому в качестве значения присваивается переменная txt. В результате в указатель p записывается адрес начального элемента символьного
Указатели
217массива, содержащего текст. Получается так текстовая переменная txt содержит ссылку на объект, который содержит символьный массив стек- стом (символы из текста являются элементами массива. Указатель p ссылается на начальный элемент в этом символьном массиве. Индексируя указатель в операторе цикла, последовательно получаем доступ к каждому элементу в символьном массиве. Оператор цикла выполняется, пока истинно условие p[k]!=
ƍ\0ƍ, то есть пока не будет прочитан нуль-сим- вол. За каждую итерацию цикла сначала отображается текущее символьное значение p[k] элемента массива, а затем командой p[k]=(char)
(
ƍAƍ+k) этому элементу присваивается новое значение. В результате в текстовом объекте, на который ссылается переменная txt, символы текста меняются на цепочку символов из алфавита, начиная с буквы
ƍAƍ.
{
i НАЗ А МЕТКУ Выше мы исходили из того, что символьный массив, через который реализуется текст, завершается нуль-символом '\0'
. Но это не самый надежный критерий проверки окончания текста. Более того, теоретически сам текст может содержать в себе нуль-символ. Поэтому в общем случае разумнее использовать свойство Многоуровневая адресацияПередайте
Зинаиде Михайловне, что Розалия
Францевна говорила Анне Ивановне «Капи- толина Никифоровна дубленки предлагает».
из к/ф Иван Васильевич меняет профессию»В языке C# мы можем создавать указатели на указатели. Другими словами, мы можем объявить указатель, значением которого является адрес другого указателя, в который записывается адрес обычной переменой. Допускается создание и более глубоких цепочек указателей, нона практике это используется не так часто.
Чтобы понять, как объявляется указатель на указатель, следует учесть, что при объявлении указателя с использованием идентификатора типа, назначение которого может ссылаться указатель, указывается звездочка *. Например, инструкция int* используется при объявлении указателя на переменную типа int. Тогда при объявлении указателя на указатель
Глава назначение типа int используется идентификатор int**. Как иллюстрацию к использованию указателей на указатели рассмотрим программу в листинге Листинг 4.8. Многоуровневая адресация System;
//
Ƚɥɚɜɧɵɣ ɤɥɚɫɫ:
class PointerToPointerDemo{
//
Ƚɥɚɜɧɵɣ ɦɟɬɨɞ:
unsafe static void Main(){
//
ɐɟɥɨɱɢɫɥɟɧɧɵɟ ɩɟɪɟɦɟɧɧɵɟ:
int A,B;
//
ɍɤɚɡɚɬɟɥɶ ɧɚ ɰɟɥɨɱɢɫɥɟɧɧɭɸ ɩɟɪɟɦɟɧɧɭɸ:
int* p;
//
ɍɤɚɡɚɬɟɥɶ ɧɚ ɭɤɚɡɚɬɟɥɶ ɧɚ ɰɟɥɨɱɢɫɥɟɧɧɭɸ
//
ɩɟɪɟɦɟɧɧɭɸ:
int** q;
//
Ɂɧɚɱɟɧɢɟ ɭɤɚɡɚɬɟɥɹ ɧɚ ɭɤɚɡɚɬɟɥɶ:
q=&p;
//
Ɂɧɚɱɟɧɢɟ ɭɤɚɡɚɬɟɥɹ:
p=&A;
//
ɉɟɪɟɦɟɧɧɨɣ A ɩɪɢɫɜɚɢɜɚɟɬɫɹ ɡɧɚɱɟɧɢɟ
// (
ɱɟɪɟɡ ɭɤɚɡɚɬɟɥɶ ɧɚ ɭɤɚɡɚɬɟɥɶ):
**q=123;
//
ɉɪɨɜɟɪɤɚ ɡɧɚɱɟɧɢɹ ɩɟɪɟɦɟɧɧɨɣ A:
Console.WriteLine(A);
//
ɇɨɜɨɟ ɡɧɚɱɟɧɢɟ ɭɤɚɡɚɬɟɥɹ:
*q=&B;
//
ɉɟɪɟɦɟɧɧɨɣ B ɩɪɢɫɜɚɢɜɚɟɬɫɹ ɡɧɚɱɟɧɢɟ
// (
ɱɟɪɟɡ ɭɤɚɡɚɬɟɥɶ):
*p=321;
//
ɉɪɨɜɟɪɤɚ ɡɧɚɱɟɧɢɹ ɩɟɪɟɦɟɧɧɨɣ B:
Console.WriteLine(B);
Указатели Ниже показано, каким будет результат выполнения программы:
Результат выполнения программы (из листинга 4.8)123 В программе мы объявляем переменные A и B типа int, указатель p назначение типа int, а командой int** q объявляется указатель q, который может ссылаться на указатель, который, в свою очередь, может ссылаться на переменную типа Командой q=&p в указатель q записывается адрес переменной p, которая сама является указателем. Указателю p значение присваивается командой p=&A. Что мы получили Имеются переменные (указатель ведь — это тоже переменная) A, p и q. Адрес переменной A записан в переменную, а адрес переменной p записан в переменную q. Выражение
*q
— это значение, записанное по адресу, который хранится в q. В указателе хранится адрес указателя p. Поэтому выражение *q является эквивалентом p. Далее, если *q — это тоже, что и p, то **q — это тоже, что и *p. А *p — это значение переменной A, поскольку указатель p ссылается на переменную A. Поэтому при выполнении команды **q=123 переменной A присваивается значение А вот когда выполняется команда *q=&B, тов указатель p записывается адрес переменной B. После этого выполнение команды *p=321 приводит к тому, что переменной B
присваивается значение Массив указателейМинуточку! За чей счет этот банкет Кто оплачивать будет?
из к/ф Иван Васильевич меняет профессию»Указатели можно использовать практически также, как и другие переменные. Нужно только помнить, что при использовании указателей
Глава мы имеем дело с адресами. С другой стороны, использование указателей иногда позволяет привнести в программу некоторую пикантность. В листинге 4.9 представлена программа, в которой задействованы массивы указателей.
Листинг 4.9. Массив указателей System;
class ArrayOfPointersDemo{
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 for(int j=0;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]);
В результате выполнения программы в консольном окне отображаются следующие сообщения:
Результат выполнения программы (из листинга 4.9)
ɑɢɫɥɚ: 100, 200 ɢ 300
A B
C D
E F
ɑɢɫɥɚ: 4325441, 4456515 ɢ 4587589
ɉɪɨɜɟɪɤɚ: 4325441, 4456515 ɢ 4587589
ȿɳɟ ɪɚɡ: 4325441, 4456515 ɢ 4587589
Ⱦɥɹ ɫɪɚɜɧɟɧɢɹ: A, C ɢ E
Глава Проанализируем программный код и результат его выполнения. Мы объявляем три целочисленные переменные x, y и z (со значениями 100, 200 и 300 соответственно. Массив из трех указателей на целочисленные значения создается командой int*[] nums=new int*[3]. Элементы массива nums являются указателями, ив качестве значений им можно присваивать адреса целочисленных переменных, что мы и делаем с помощью команд nums[0]=&x
, nums[1]=&y и nums[2]=&z. Таким образом, nums[0] — это адрес переменной x, nums[1] — это адрес переменной y, а nums[2] — это адрес переменной z. С учетом правил адресной арифметики получаем, что nums[0][0]
— это значение переменной x, *nums[1] — это значение переменной, а nums[2][0] — это значение переменной Командой char*[] symbs=new char*[3] создается массив из трех указателей на символьные значения. Значения элементам массива присваиваем командами symbs[0]=(char*)&x, symbs[1]=(char*)&y и symbs[2]=(char*)&z. Поскольку переменные x, y и z целочисленные, а элементы массива symbs являются указателями на символьные значения, тонам пришлось выполнить явное приведение типа. Что получается Например, значением элемента symbs[0] является адрес переменной x. Но переменная записана в 4 байтах, а указатель symbs[0] получает доступ к блокам размером в 2 байта. Поэтому symbs[0][0] — это значение интерпретируемое как символ) в первых двух однобайтовых блоках области памяти, выделенной под переменную x. А выражение symbs[0][1] — это символьное значение в следующих двух однобайтовых блоках области памяти, выделенной под переменную x. Это же касается других элементов массива symbs и переменных y и z. В
итоге получается, что на основе трех блоков памяти (каждый по 4 байта, выделенных под переменные x
, y и z, мы организовали видимость символьного массива из трех строки двух столбцов. Этот импровизированный двумерный символьный массив заполняется с помощью вложенных операторов цикла, а соответствующие символьные значения отображаются в консольном окне ПОДРОБНОСТИ bЗначение выражения symbs.Length
— это количество элементов в массиве symbs
(то есть
3
). Выражение sizeof(int)/sizeof
(char)
определяет, сколько блоков помещается внутри int
- блока (результат равен Область памяти, выделенная под импровизированный двумерный символьный массив, используется также при работе с массивом nums,
Указатели