Указатели в языке С#

Основные достоинства языка C#, его схема работы с памятью. Сущность небезопасного кода. Синтаксис указателей, их предназначение, преобразование, инициализация, арифметические операции и приоритетность их выполнения. Содержание операции stackalloc.

Рубрика Программирование, компьютеры и кибернетика
Вид лекция
Язык русский
Дата добавления 20.12.2011
Размер файла 24,5 K

Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже

Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.

Размещено на http://www.allbest.ru/

Размещено на http://www.allbest.ru/

Лекция 10. Указатели в языке С#

Небезопасный код

язык память указатель stackalloc

Одним из основных достоинств языка C# является его схема работы с памятью: автоматическое выделение памяти под объекты и автоматическая уборка мусора. При этом невозможно обратиться по несуществующему адресу памяти или выйти за границы массива, что делает программы более надежными и безопасными.

В некоторых случаях возникает необходимость работать с адресами памяти непосредственно, например, при взаимодействии с операционной системой, написании драйверов и др. Такую возможность представляет так называемый небезопасный (unsafe) код.

Небезопасным называется код, выполнение которого среда CLR не контролирует. Он работает напрямую с адресами областей памяти посредством указателей и должен быть явным образом помечен с помощью ключевого слова unsafe, которое определяет так называемый небезопасный контекст выполнения.

Оператор unsafe имеет следующий синтаксис:unsafe блок операторов

Все операторы, входящие в блок, выполняются в небезопасном контексте.

Компиляция кода, содержащего небезопасные фрагменты, должна производиться с ключом / unsafe. Этот режим можно установить путём настройки среды Visual Studio (ProjectPropertiesConfigurationBuildAllow Unasfe Code).

Синтаксис указателей

Указатели предназначены для хранения адресов областей памяти. Синтаксис объявления указателя:

Тип данных* переменная;

Тип данных в этом объявлении не может быть классом, но может быть структурой, перечислением, указателем, а так же одним из стандартных типов: sbyte, byte, short, ushort, unt, unit, long, ulong, char, float, double, decimal, bool, void.

Примеры объявления указателей:

int* a; //указатель на int

Node* pNode; //указатель на структуру Node

void* p; //указатель на неопределенный тип

int*[] m; //одномерный массив указателей на int

int** d;//указатель на указатель на int

В одном операторе можно описать несколько указателей одного и того же типа, например:

int*a,b,c;//три указателя на int

Указатели являются отдельной категорией типов данных. Они не наследуются от типа object, и преобразование между типом object и типами указателей невозможно. В частности, для них не выполняется упаковка и распаковка. Однако допускаются преобразования между разными типами указателей, а так же указателями и целыми.

Указатели могут являться локальными переменными, полями, параметрами и возвращаемыми значениями функции. Эти величины подчиняются общим правилам определения области действия и времени жизни.

Указатель void

Указатель типа void означает, что он ссылается на переменную неизвестного типа. Указатель на тип void применяется в тех случаях, когда конкретный тип объекта, адрес которого требуется хранить, не определён (например, если в одной и той же переменной в разные моменты времени требуется хранить адреса объектов различных типов).

Указателю на тип void можно присвоить значение указателя любого типа, а также сравнить его с любыми указателями, но перед выполнением каких-либо действий с областью памяти, на которую он ссылается, требуется преобразовать его к конкретному типу явным образом. Следовательно,

Указатель void может находиться только в левой части оператора присваивания.

Преобразование указателей

Для указателей поддерживаются неявные преобразования из любого типа указателя к типу void*. Любому указателю можно присвоить константу null. Кроме того, допускаются явные преобразования:

o между указателями любого типа;

o между указателями любого типа и целыми типами (со знаками и без знака).

Корректность преобразований лежит на совести программиста. Преобразования никак не влияют на величины, на которые ссылаются указатели, но при попытке получения значения по указателю несоответствующего типа поведение программы не определенно.

Инициализация указателей

Инициализация указателей может быть осуществлена различными способами.

1 способ. Присваивание указателю адреса существующего объекта:

o С помощью операции получения адреса:

int a=5; //целая переменная

int*p=&a; //в указатель записывается адрес a

o С помощью значения другого инициализированного указателя:

int*p1=p;// указатель p уже инициализирован

o С помощью имени массива, которое трактуется как адрес:

int[]b= new int[]{10,20,30,40};//массив

fixed (int*p2=b) {...};//присваивание указателю р2 адреса начала массива

fixed (int*р2=&b[0]){...};//то же самое

2 способ. Присваивание указателю адреса области памяти в явном виде:

сhar*p3=(char*)0x12F69E;

здесь 0x12F69E - шестнадцатеричная константа, (Char*) - операция явного приведения типа: константа преобразуется к типу указателя на char.

3 способ. Присваивание нулевого значения:

int*p4=null;

4 способ. Выделение области памяти в стеке и присваивание её адреса указателю:

int*р5=stackalloc int[10];

Здесь операция stackalloc выполняет выделение памяти под 10 величин типа int (массив из 10 элементов) и записывает адрес начала этой области памяти в переменную p5, которая может трактоваться как имя массива, так и указателем.

Операции с указателями

Основные операции с указателями приведены в таблице 10.1.

Таблица 10.1- Основные операции с указателями

Операция

Описание

*

Разадресация - получение значения, которое находится по адресу, хранящемуся в указателе.

->

Доступ к элементу структуры через указатель

[]

Доступ к элементу массива через указатель

&

Получение адреса переменной

++,--

Увеличение и уменьшение значения указателя на один адресуемый элемент

+,-

Сложение с целой величиной и вычитание указателей

= =,!=,<>,<=,>=

Сравнение адресов, хранящихся в указателях. Выполняется как сравнение беззнаковых целых величин.

stackalloc

Выделение памяти в стеке под переменную, на которую ссылается указатель

Основными операциями с указателями являются * и &.

Обе эти операции являются унарными, т. е. имеют один операнд, перед которыми они ставятся. Операция & соответствует операции "взять адрес". Операция * соответствует словам "значение, расположенное по указанному адресу".

Операция * называется разадресацией или разыменованием. Она предназначена для доступа к величине, адрес которой хранится в указателе. Эту операцию можно использовать как для получения, так и для изменения значения величины.

Пример:

int a=5;//целая переменная

int*p=&a;//инициализация указателя адресом переменной а

Console.WriteLine (*p);//операция разадресации, результат равен 5

Console.WriteLine (++(*p));//результат 6

int[]b=new int[] {10,20,30,40}; //массив

fixed (int*t=b)//инициализация указателя адресом начала массива

{

int*z=t;//инициализация указателя значением другого указателя

for (int i=0;i<b.length;++i)

{

t[i]+=5; //доступ к элементу массива (увеличение на 5)

*z+=5; //доступ с помощью адресации (увеличение ещё на 5)

++z;//инкремент указателя

}

}

В приведённом примере доступ к элементам массива выполняется двумя способами: путём индексации указателя t и путём разадресации указателя z, значение которого инкрементируется при каждом проходе цикла для перехода к следующему элементу массива.

Конструкцию *переменная можно использовать в левой части оператора присваивания, так как она определяет адрес области памяти. Для простоты эту конструкцию можно считать именем переменной, на которую ссылается указатель. С ней допустимы всей действия, определённые для величин соответствующего типа.

Оператор fixed фиксирует объект, адрес которого заносится в указатель, для того чтобы его не перемещал сборщик мусора и, таким образом, указатель остался корректным. Фиксация происходит на время выполнения блока, который записан после круглых скобок.

Арифметические операции с указателями

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

Инкремент перемещает указатель к следующему элементу массива, декремент - к предыдущему.

Разность двух указателей - это разность их значений, деленная на размер типа в байтах. Так, если р и р1 - указатели на элементы одного и того же массива, то операция р - р1 дает такой же результат, как и вычитание индексов соответствующих элементов массива.

К указателям можно прибавлять целое число. Пусть указатель р имеет значение 2000 и указывает на тип byte. Тогда в результате выполнения оператора:

p=p+3;

значение указателя р будет 2003. Если же указатель р1=2000 был бы указателем па float, то после применения оператора:

р1=р1+10;

значение р1 было бы 2040.

Общая формула для вычисления значения указателя после выполнения операции:

р=р+n;

будет иметь вид:

<р>=<р>+n*<количество байт памяти базового типа указателя >

Вывод. При сложении указателя с целым числом фактически значение указателя изменяется на величину sizeof (тип), где sizeof - операция получения размера величины указанного типа (в байтах). Эта операция применяется только в небезопасном контексте, с её помощью можно получать размеры не только стандартных, но и пользовательских типов данных.

Если указатель на определенный тип увеличивается или уменьшается на константу, его значение изменяется на величину этой константы, умноженную на размер объекта данного типа.

Другие арифметические операции над указателями запрещены, например нельзя сложить два указателя, умножить указатель на число и т. д.

Указатели можно сравнивать. Применимы все шесть операций:

<,>,<=, >=, =,= =, !=

Сравнение р<g означает, что адрес, находящийся в р, меньше адреса, находящегося в g. Если р и g указывают на элементы одного массива, то индекс элемента, на который указывает р, меньше индекса массива, на который указывает g.

Пример применения арифметических операций с указателями:

//демонстрация операции инкремента

int * p;

int a = 5;

p = &a;

Console.WriteLine("{0:X}",(uint)p);// вывод значения указателя(адреса), //тип uint-беззнаковый целый тип в unicode-формате(16-ное число)

p++;//операция инкремента. Значение указателя(адрес) увеличивается на 4 //байта

Console.Write("{0:X}", (uint)p);

// аналогичные действия, только применимые для типа long:

long *p1;

p1++;//значение указателя(адрес) увеличится на 8 байт!

//демонстрация операции разности указателей

int []a=new int[]{1,2,3};

fixed (int* p1 = a)

{

int* p2 = p1;

p2 ++;

Console.WriteLine(p2-p1);

}

//демонстрация операции сложения указателя с целым числом

int *p3=p2+2; // к указателю прибавили 2 элемента!

Console.WriteLine(*p3);//значение по указателю равно 3

Приоритетность выполнений операций с указателями

При записи выражений с указателями следует обращать внимание на приоритеты операций. В качестве примера рассмотрим последовательность действий, заданную в операторе: *p++=10;

Поскольку инкремент постфиксный, он выполняется после выполнения операции присваивания. Таким образом, сначала по адресу, записанному в указателе p, будет записано значение 10, а затем указатель увеличится на количество байтов, соответствующее его типу. То же самое можно записать подробнее:

*p=10;

p++;

Выражение (*p)++, напротив, инкрементирует значение, на которое ссылается указатель.

В следующем примере каждый байт беззнакового целого числа x выводится на консоль с помощью указателя t:

uint x = 0xAB10234F;

byte*t=(byte*)&x;

for (int i=0;i<4;++i)

Console.Write (“{0:X}”, *t++);//результат 4F 23 10 AB

Первоначально указатель t был установлен на младший байт переменной x.

Указатель на указатель

В языке С# возможна также ситуация, когда указатель указывает на указатель. В этом случае описание будет иметь следующий вид:

int **point;

Переменная point имеет тип указатель на указатель на int. Чтобы получить целочисленное значение переменной, на которую указывает point следует использовать:

**point;

Пример:

int i=7;

int *p;

int **pp;

p=&i;

pp=&p;

Console.WriteLine("i={0:X} p= {1:X} pp= {2:X}",i,(uint)p,(uint)pp);

Console.WriteLine("i={0} *p= {1} **pp= {2}",i,*p,**pp);

++*p;

Console.WriteLine("i={0} *p= {1} **pp= {2}", i, *p, **pp);

**pp=12;

Console.WriteLine("i={0} *p= {1} **pp= {2}", i, *p, **pp);

Операция stackalloc

Операция stackalloc позволяет выделять память в стеке под заданное количество величин заданного типа:

stackalloc тип [ количество ]

Количество задаётся целочисленным выражением. Если памяти недостаточно, генерируется исключение System.StackOverflowException. Выделеная память ничем не инициализируется и автоматически освобождается при завершении блока, содержащего эту операцию. Пример выделения память под пять элементов типа int и заполнения числами от 0 до 4:

int*p=stackalloc int [5];

for (int i=0; i<5; ++i)

{

p[i]=i;

Console.Write (p[i] +” “); //Результат 0 1 2 3 4

}

Размещено на Allbest.ru


Подобные документы

  • Двоично-десятичный формат (BCD - Binary Coded Decimal). Преобразование ASCII формата в двоичный формат. Арифметические инструкции и флаги. Форматы арифметических данных. Выполнение арифметических операции. Сложение. Вычитание. Умножение. Деление.

    доклад [16,2 K], добавлен 22.09.2008

  • Ознакомление с понятием, особенностями объявления, инициализацией и принципами работы с одномерными и двумерными массивами. Изучение смысла тернарной операции вывода элементов матрицы. Рассмотрение сущности и способов использования указателей переменных.

    лабораторная работа [22,1 K], добавлен 15.07.2010

  • Организация и назначение консольного ввода-вывода, необходимые для этого функции и их применение. Библиотеки, организующие функционирование потокового ввода-вывода, выполняемые операции. Арифметические операции и математические функции в среде С++.

    лабораторная работа [33,8 K], добавлен 15.07.2009

  • Типы команд, синтаксис ассемблера и код операции, по которому транслируется команда. Команды вычисления и непосредственной пересылки данных между регистрами. Поле для определения операции вычисления. Управление последовательностью выполнения программы.

    реферат [29,1 K], добавлен 13.11.2009

  • Общее описание функций Oracle SQL, их особенности, классификация и типы, преобразование регистра символов и манипулирование строками. Работа со строковыми функциями. Арифметические операции с датами. Функции преобразования и основные операции над ними.

    курсовая работа [464,4 K], добавлен 24.12.2014

  • Основные понятия и назначение языка программирования СИ. Скалярные типы данных. Арифметические, логические и битовые операции над переменными целочисленного, вещественного, символьного, перечислимого типов. Примеры программ, выполняющие операции над ними.

    презентация [269,9 K], добавлен 26.07.2013

  • Традиционные, специальные и дополнительные реляционные операции. Синтаксис и конструкции языка структурированных запросов SQL, типы данных. Запросы, выполняющие реляционные операции вычитания, пересечения и деления. Создание и обслуживание таблиц.

    контрольная работа [57,1 K], добавлен 21.06.2016

  • Инициализация элементов данных класса в программе С++ с использованием конструктора, который запускается для каждого объекта. Применение операции ссылки в языке С++ для взятия адреса объекта. Деструкция как освобождение заказанной памяти, закрытие файлов.

    реферат [20,1 K], добавлен 30.10.2011

  • Роль и практическое значение автоматизации вычислений и обработки данных. Представление информации в компьютере, сущность системы счисления. Перевод числа из одной системы счисления в другую. Арифметические операции в позиционных системах счисления.

    контрольная работа [1,2 M], добавлен 23.10.2009

  • Разработка структурной, функциональной и принципиальной схем блока выполнения арифметической операции над числами, представленными в дополнительном коде в форме с плавающей запятой. Алгоритмы выполнения операции умножения. Анализ временных задержек.

    курсовая работа [287,7 K], добавлен 07.06.2013

Работы в архивах красиво оформлены согласно требованиям ВУЗов и содержат рисунки, диаграммы, формулы и т.д.
PPT, PPTX и PDF-файлы представлены только в архивах.
Рекомендуем скачать работу.