Путаница в том, использовать ли исправлено с небезопасным кодом и stackalloc - PullRequest
3 голосов
/ 27 июля 2010

Ниже приведен блок кода с закомментированной строкой. То, что происходит в методе CreateArray, является тем же, что и закомментированная строка. У меня вопрос, почему это работает, когда строка b->ArrayItems = d не закомментирована, но возвращает закомментированный, когда закомментировано? Я не думаю, что мне нужно что-то «исправлять», потому что вся информация неуправляема. Это предположение неверно?

class Program
{
    unsafe static void Main(string[] args)
    {
        someInstance* b = stackalloc someInstance[1];
        someInstance* d = stackalloc someInstance[8];

        b->CreateArray();
//      b->ArrayItems = d;

        *(b->ArrayItems)++ = new someInstance() { IntConstant = 5 };
        *(b->ArrayItems)++ = new someInstance() { IntConstant = 6 }; 

        Console.WriteLine((b)->ArrayItems->IntConstant);
        Console.WriteLine(((b)->ArrayItems - 1)->IntConstant);
        Console.WriteLine(((b)->ArrayItems - 2)->IntConstant);
        Console.Read();
    }
}

public unsafe struct someInstance
{
    public someInstance* ArrayItems;
    public int IntConstant;
    public void CreateArray()
    {
        someInstance* d = stackalloc someInstance[8];
        ArrayItems = d;
    }
}

Ответы [ 3 ]

13 голосов
/ 27 июля 2010

У меня вопрос, почему он работает, когда строка не закомментирована, но возвращает закомментированный мусор, когда закомментирован.

Закомментированная строка - это то, что маскирует ошибкавызвано CreateArray.Комментируя это, выявляет ошибку.Но ошибка существует независимо от этого.

Как ясно указывается в спецификации:

Все выделенные в стеке блоки памяти, созданные во время выполнения члена функции, автоматически отбрасываются при возврате этого члена функции.

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

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

Тем не менее, давайте обратимся к тому, что кажется вам большим заблуждением, а именно, «когда вам нужно исправить память, чтобыуказатель?"Ответ прост. Вы должны исправить память, если и только если это подвижная память. Исправление превращает подвижную память в неподвижную память; это то, что исправление для .

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

1 голос
/ 27 июля 2010

Ваше предположение частично верно, но неправильно понято.Вот цитата с этой страницы MSDN :

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

Некоторые операторы будут автоматически распределять переменные в стеке (т. Е. Типы значений внутри метода), другие должны быть указаны специально с помощью stackalloc.

Память, выделенная для стека, отбрасывается после завершения метода, поэтому возникает ваша проблема (см. Ответ Эрика Липпертса, написавшего это до меня).

1 голос
/ 27 июля 2010

Stackalloc выделяет некоторое пространство в стеке вызовов, которое затем теряется, когда вы выходите за пределы текущего уровня контекста (например, оставляя метод). Ваша проблема в том, что когда stackalloc находится внутри метода, то эта область стека больше не ваша, когда вы выходите из этого метода.

Итак, если вы сделаете это:

foo()
{
    stuff = stackalloc byte[1]
    Do something with stuff
}

«stuff» действителен только внутри foo, когда вы выходите из foo, стек возвращается назад, что означает, что если вы сделаете это:

foo()
{
    byte* allocate()
    {
        return stackalloc[1]
    }

    stuff = allocate()
    do something with stuff
}

тогда возвращаемое значение allocate становится мусором, когда вы покидаете метод allocate, что означает, что "stuff" никогда не имеет никакого смысла.

...