В чем разница между out и int* c#

#c#

#c#

Вопрос:

Насколько я читал, отправка ключевого слова в c# изменит параметр. Так в чем же разница между этими двумя случаями:

  1. использование вне:
     helper(out param){...}   func(){   int param = 0;   helper(out param)}  
  2. использование указателя:
     helper(int* param){..}    func(){   int param = 0;   helper(amp;param)}  

Они такие же ? Tnx.

Комментарии:

1. [Предполагая, что C#] Указатель требует небезопасного кода и может привести к исключениям из памяти при неправильном использовании. Это также не гарантирует, что указанный элемент инициализируется вызовом. out Параметр не страдает от этих проблем. Также обратите внимание, что вы должны указать тип out параметра: helper(out int param)

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

3. Ваш код полон синтаксических ошибок.

4. Вообще говоря, вы не должны использовать указатели, как в C/C . Вместо этого в C# вы должны использовать ссылки (где out здесь один вариант).

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

Ответ №1:

Эти две вещи концептуально похожи. Ключевое различие здесь заключается в:

  • int* является неуправляемым указателем
  • ref int является управляемым указателем
  • out int на самом деле это просто ref int с некоторыми дополнительными ограждениями компилятора вокруг определенного назначения

Итак: в чем разница между управляемым и неуправляемым указателем? Основные различия:

  • GC может правильно отслеживать управляемые указатели
  • это означает, что вам не нужно закреплять/исправлять некоторые вещи, прежде чем вы сможете их использовать
  • и поэтому вам не нужно использовать unsafe
  • и это более поддается проверке
  • и все больше людей понимают код

Наоборот:

  • вы можете хранить неуправляемый указатель в поле типа (управляемые указатели могут храниться только в стеке).
  • но если этот неуправляемый указатель указывает на что-то, что переместилось/ушло: БУМ

Итак: в любом сценарии, где вы можете использовать управляемый указатель ( ref или out ), вместо неуправляемого указателя: вы должны. Честно говоря, очень немногие разработчики C# полностью понимают нюансы управляемых указателей, но еще меньше полностью понимают нюансы неуправляемых указателей.

Комментарии:

1. Обратите также внимание, что вы не можете использовать указатель на управляемый тип, такой как a struct . Указатели можно использовать только с примитивными типами.

2. @JohnAlexiou IIRC у вас может быть неуправляемый указатель на структуру, но эта структура не может иметь внутренних ссылок в виде полей (и для управляемых указателей на тип значения: она может иметь внутренние ссылки, если захочет)

Ответ №2:

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

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

Указатель в C#, который используется для передачи аргументов по ссылке. Или мы можем сказать, что если какие-либо изменения, внесенные в этот аргумент в методе, будут отражены в указателе этой переменной, когда элемент управления вернется к вызывающему методу.

Указатели обычно используются для взаимодействия с C и не должны использоваться в повседневном кодировании на C#. Мы можем использовать тип ссылки.

Комментарии:

1. «как ссылочный тип» — нет: он передает (управляемую) ссылку, но может относиться к типу значения; передача по ссылке и ссылочный тип-это совершенно разные понятия

Ответ №3:

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

Но интересно посмотреть, как они отличаются друг от друга в ассемблерном коде более низкого уровня.

Рассмотрим минимальный код скелета.

 static class Program {  static unsafe void Main(string[] args)  {  int x;  SetArg(out x);  Console.WriteLine(x);   int y;  SetArg(amp;y);  Console.WriteLine(y);  }   static void SetArg(out int x)  {  x = 10;  }   static unsafe void SetArg(int* ptr)  {  *ptr = 10;  } }  

и результирующий код сборки, который встроен

 SetArg(out x); 00007FFD4EB80890 int 3  00007FFD4EB80891 sub esp,28h  00007FFD4EB80894 xor eax,eax  00007FFD4EB80896 mov dword ptr [rsp 24h],eax   Console.WriteLine(x); 00007FFD4EB8089A mov ecx,0Ah ! case a) 00007FFD4EB8089F call 00007FFDAA4E2890    int y;  SetArg(amp;y); 00007FFD4EB808A4 lea rcx,[rsp 24h]  00007FFD4EB808A9 mov dword ptr [rcx],0Ah ! case b)  Console.WriteLine(y); 00007FFD4EB808AF mov ecx,dword ptr [rsp 24h]  00007FFD4EB808B3 call 00007FFDAA4E2890  00007FFD4EB808B8 nop  00007FFD4EB808B9 add rsp,28h  00007FFD4EB808BD ret   

В первом случае с out оператором значение 10 = 0Ah записывается непосредственно в регистр ecx , прежде Writeline() чем функция запишет его в консоль. Это супер оптимизировано, так как в этой операции не используется память. Вот почему не рекомендуется микрооптимизировать ваш код, а просто передавать намерение с ref/out помощью ключевых слов и позволять компилятору выполнять свою работу.

Во втором случае с int* аргументом значение 10 = 0Ah записывается в адрес памяти в стеке, соответствующий аргументу функции, а затем загружается до ecx с этого адреса перед вызовом Writeline() . Здесь код не только небезопасен, но и менее оптимизирован, поскольку программист заставил компилятор хранить промежуточное значение в стеке перед использованием.

Примечание. Чем является альтернативное определение SetArg()

 static unsafe void SetArg(int* ptr)  {  ptr[0] = 10; // same as ptr* = 10  }  

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

 ptr[-1] = 10;  

или что-либо еще, кроме ptr[0] того, что является предполагаемой ячейкой памяти, то вы повредили память процесса, вызвав хаос.