Конвертировать из двойного массива в указатель - PullRequest
6 голосов
/ 10 марта 2010

у меня такой класс

public unsafe class EigenSolver
{
   public double* aPtr
   {get; private set;}
   public EigenSolver(double* ap)
   {
      aPtr = ap;
   }
   public EigenSolver(double[] aa)
   {
     // how to convert from aa double array to pointer?
   }

   public void Solve()
   {
     Interop.CallFortranCode(aPtr);
   }
}

Как вы можете догадаться, мне нужно преобразовать массив double в указатель. Как это сделать?

Примечание: функция взаимодействия Interop.CallFortranCode(double* dPtr) - это то, что я не могу изменить.

Примечание 2. Оба конструктора необходимы, потому что некоторые из моих пользователей API хотят передавать указатели, а некоторые хотели бы передавать массив. Я не могу заставить их выбирать.

Ответы [ 2 ]

9 голосов
/ 10 марта 2010

Используйте оператор fixed:

fixed (double* aaPtr = aa) {
   // You can use the pointer in here.
}

Хотя в контексте fixed память для вашей переменной закреплена, поэтому сборщик мусора не будет пытаться переместить ее.

Я бы взял этот подход вместо:

public class EigenSolver
{
   public double[] _aa;
   /*
   There really is no reason to allow callers to pass a pointer here, 
   just make them pass the array.
   public EigenSolver(double* ap)
   {
      aPtr = ap;
   }
   */
   public EigenSolver(double[] aa)
   {
     _aa = aa;
   }

   public void Solve()
   {
     unsafe {
        fixed (double* ptr = _aa) {
           Interop.CallFortranCode(ptr);
        }
     }
   }
}

Это предполагает, конечно, что CallFortranCode не пытается использовать указатель вне вызова. Когда фиксированный оператор выходит из области видимости, указатель становится недействительным ...

UPDATE:

Невозможно взять адрес аргумента double[] aa и сохранить его в поле вашего экземпляра. Даже если компилятор разрешит вам, GC будет перемещать эту память, оставляя ваш указатель бесполезным.

Возможно, вы могли бы сделать это: Используйте Marshal.AllocHGlobal, чтобы выделить достаточно памяти для хранения всех элементов массива (aa.Length * sizeof(double))). Затем используйте Marshal.Copy, чтобы скопировать содержимое массива в недавно выделенную память:

bool _ownsPointer; 
public EigenSolver(double[] aa) {
   IntPtr arrayStore = (double*)Marshal.AllocHGlobal(aa.Length * sizeof(double));
   Marshal.Copy(aa, 0, arrayStore, aa.Length);
   this.aPtr = (double*)arrayStore.ToPointer();
   _ownsPointer = true;
}

~EigenSolver {
   if (_ownsPointer) {
      Marshal.FreeHGlobal(new IntPtr(this.aPtr));
   }
}

Надеюсь, это работает ...

Andrew

1 голос
/ 10 марта 2010

Хотя fixed действительно является ответом, который вы ищете, вы не можете использовать его так, как пытаетесь. Как только блок fixed заканчивается (на закрывающей скобке), указатель становится недействительным. Таким образом, вы не можете использовать его в конструкторе для хранения указателя. Не следует этого делать, поскольку закрепление управляемого объекта в памяти в течение продолжительных периодов времени приводит к резкому снижению производительности.

Вместо этого я бы передал массив (как управляемый объект) вашей функции Solve. Таким образом, вы можете записать его, просто чтобы сделать вызов intertran interop, а затем позволить gc позаботиться об этом.

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