Почему доступ к памяти в самом низком адресном пространстве (не нулевом) сообщается как. NullReferenceException в .NET? - PullRequest
30 голосов
/ 29 октября 2011

Это приводит к выбрасыванию AccessViolationException:

using System;

namespace TestApplication
{
    internal static class Program
    {
        private static unsafe void Main()
        {
            ulong* addr = (ulong*)Int64.MaxValue;
            ulong val = *addr;
        }
    }
}

Это приводит к выбрасыванию NullReferenceException:

using System;

namespace TestApplication
{
    internal static class Program
    {
        private static unsafe void Main()
        {
            ulong* addr = (ulong*)0x000000000000FF;
            ulong val = *addr;
        }
    }
}

Они оба являются недействительными указателями и нарушают правила доступа к памяти. Почему исключение NullReferenceException?

Ответы [ 4 ]

43 голосов
/ 29 октября 2011

Это вызвано дизайнерским решением Windows, принятым много лет назад. Нижние 64 килобайта адресного пространства зарезервированы. О доступе к любому адресу в этом диапазоне сообщается с исключением нулевой ссылки вместо основного нарушения доступа. Это был мудрый выбор, нулевой указатель может производить чтение или запись по адресам, которые на самом деле не равны нулю. Например, читая поле объекта класса C ++, оно имеет смещение от начала объекта. Если указатель объекта равен нулю, код будет бомбить чтение по адресу, который больше 0.

C # не имеет такой же проблемы, язык гарантирует, что нулевая ссылка будет перехвачена, прежде чем вы сможете вызвать метод экземпляра класса. Однако это зависит от языка, это не функция CLR. Вы можете написать управляемый код в C ++ / CLI и генерировать ненулевые разыменования нулевого указателя. Вызов метода для объекта nullptr работает. Этот метод весело выполнится. И вызвать другие методы экземпляра. Пока он не попытается получить доступ к переменной экземпляра или вызвать виртуальный метод, который требует разыменования this , kaboom затем.

Гарантия C # очень хорошая, она значительно упрощает диагностику проблем с нулевыми ссылками, поскольку они генерируются на месте вызова и не бомбят где-то внутри вложенного метода. И это существенно безопаснее: переменная экземпляра может не вызывать исключение для очень больших объектов, когда ее смещение превышает 64 КБ. Довольно сложно сделать это в управляемом коде, в отличие от C ++. Но не приходит бесплатно, объяснено в этом сообщении в блоге .

17 голосов
/ 29 октября 2011

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

Из ваших результатов видно, что CLR делает вывод, что нарушения доступа по адресам, очень близким к 0, вызваны нулевой ссылкой. Потому что они почти наверняка были сгенерированы нулевой ссылкой плюс смещение поля. Использование вами небезопасного кода обманывает эту эвристику.

3 голосов
/ 29 октября 2011

Это может быть проблема семантики.

Ваш первый пример пытается разыменовать указатель, содержимое которого является адресом Int64.MaxValue, а не указателем на переменную, имеющую значение из Int64.MaxValue.

Похоже, вы пытаетесь прочитать значение, хранящееся по адресу Int64.MaxValue, которое, очевидно, не находится в диапазоне, принадлежащем вашему процессу.

Вы имеете в виду что-то подобное?

        static unsafe void Main(string[] args)
        {
            ulong val = 1;// some variable space to store an integer
            ulong* addr = &val;
            ulong read = *addr;

            Console.WriteLine("Val at {0} = {1}", (ulong)addr, read);

#if DEBUG 
            Console.WriteLine("Press enter to continue");
            Console.ReadLine();
#endif
        }
2 голосов
/ 29 октября 2011

от http://msdn.microsoft.com/en-us/library/system.accessviolationexception.aspx

Информация о версии

Это исключение является новым в .NET Framework версии 2.0. В ранее версии .NET Framework, нарушение прав доступа в неуправляемом коде или небезопасный управляемый код представлен исключением NullReferenceException в управляемый код. NullReferenceException также бросается, когда нуль ссылка разыменовывается в проверяемом управляемом коде, вхождение это не связано с повреждением данных, и нет никакого способа различать две ситуации в версиях 1.0 или 1.1.

Администраторы могут разрешить выбранным приложениям вернуться к поведение .NET Framework версии 1.1. Поместите следующую строку в разделе Элемент файла конфигурации для Применение:

прочее <legacyNullReferenceExceptionPolicy enabled = "1"/>

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