|
Программирование >> Обработка исключительных ситуаций
348 Глава 15. Дополнительные средства С# Небезопасным называется код, выполнение которого среда CLR не контролирует. Он работает напрямую с адресами областей памяти посредством указателей и должен быть явным образом помечен с помощью ключевого слова unsafe, которое определяет так называемый небезопасный контекст выполнения. Ключевое слово unsafe может использоваться либо как спецификатор, либо как оператор. В первом случае его указывают наряду с другими спецификаторами при описании класса, делегата, структуры, метода, поля и т. д. - везде, где допустимы другие спецификаторы. Это определяет небезопасный контекст для описываемого элемента, например: public unsafe struct Node { public int Value; . public Node* Left; public Node* Right; Вся структура Node помечается как небезопасная, что делает возможным использование в ней указателей Left и Right. Можно применить и другой вариант описания, в котором небезопасными объявляются только соответствующие поля структуры: public struct Node public int Value; public, unsafe Node* Left; public unsafe Node* Right; Оператор unsafe имеет следующий синтаксис: unsafe блок Все операторы, входящие в блок, выполняются в небезопасном контексте. ПРИМЕЧАНИЕ- Компиляция кода, содержащего небезопасные фрагменты, должна производить! с ключом /unsafe. Этот режим можно установить путем настройки среды Visu Studio (Project Properties Configuration Properties Build Allow Unsafe Cod* Синтаксис указателей Указатели предназначены для хранения адресов областей памяти. Синтакс объявления указателя: тип* переменная; Здесь тип - это тип величины, на которую указывает пережиная, то есть ве. чины, хранящейся по записанному в переменной адресу. Тип не может 6i классом, но может быть структурой, перечислением, указателем, а также од* Небезопасный код 349 из стандартных типов: sbyte, byte, short, ushort, int, uint, long, uiong, char, float, double, decimal, bool и void. Последнее означает, что указатель ссылается на переменную неизвестного типа. Указатель на тип voi d применяется в тех случаях, когда конкретный тип объекта, адрес которого требуется хранить, не определен (например, если в одной и той же переменной в разные моменты времени требуется хранить адреса объектов различных типов). Указателю на тип voi d можно присвоить значение указателя любого типа, а также сравнивать его с любыми указателями, но перед выполнением каких-либо действий с областью памяти, на которую он ссылается, требуется преобразовать его к конкретному типу явным образом. Примеры объявления указателей: int* а; указатель на int Node* pNode; указатель на описанную ранее структуру Node void* р; указатель на неопределенный тип int*[] m; одномерн1й массив указателей на int int** d; указатель на указатель на int В одном операторе можно описать несколько указателей одного и того же типа, например: int* а. Ь, с: три указателя на int Указатели являются отдельной категорией типов данных. Они не наследуются от типа object, и преобразование между типом object и типами указателей невозможно. В частности, для них не выполняется упаковка и распаковка. Однако допускаются преобразования между разными типами указателей, а также указателями и целыми. ПРИМЕЧАНИЕ- Именно потому что указатели мог содержать адрес любой области памяти и, следовательно, изменить ее, операции с ними и называются небезопасными. Величины типа указателя могут являться локальными переменными, полями, параметрами и возвращаемым значением функции. Эти величины подчиняются общим правилам определения области действия и времени жизни. Преобразования указателей Для указателей поддерживаются неявные преобразования из любого типа указателя к типу void*. Любому указателю можно присвоить константу null. Кроме того, допускаются явные преобразования: между указателями любого типа; между указателями любого типа и целыми типами (со знаком и без знака). 350 Глава 15. Дополнительные средства С# Корректность преобразований лежит на совести программиста. Преобразования никак не влияют на величины, на которые ссылаются указатели, но при попытке получения значения по указателю несоответствующего типа поведение програм-1 мы не определено . Инициализация указателей Ниже перечислены способы присваивания значений указателям: 1. Присваивание указателю адреса существующего объекта: О с помощью операции получения адреса: int а = 5: целая переменная int* р = &а; в указатель запис1вается адрес а ПРИМЕЧАНИЕ- Программист должен сам следить за тем, чтобы переменные, на которые ссылается указатель, были правильно инициализированы. О с помощью значения другого инициализированного указателя: int* Г = р; О с помощью имени массива, которое трактуется как адрес: int[] b = new int[] {10. 20. 30, 50}; массив fixed ( int* t = b ) { ... }; присваивание адреса начала массива fixed ( int* t = &b[0] ){...}; то самое ПРИМЕЧАНИ Е- Оператор f i xed рассматривается позже. 2 . Присваивание указателю адреса области памяти в явном виде: char* V = (char *)0xl2F69E; Здесь 0xl2F69E - шестнадцатеричная константа, (char *) - операция приведения типа: константа преобразуется к типу указателя на char. ПРИМЕЧАНИЕ- Использовать этот способ можно только в том случае, если адрес вам точно известен, в противном случае может возникнуть исключение. 3. Присваивание нулевого значения: i nt* хх = nul 1 : 1 Как правило, если в документации встречается оборот неопределенное поведение (undefined behavior), ничего хорошего это не сулит.
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |