Преобразуйте от двойного массива до указателя

У меня есть такой класс

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 Interop.CallFortranCode(double* dPtr) что-то, что я не могу изменить.

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

6
задан Graviton 10 March 2010 в 07:24
поделиться

2 ответа

Используйте оператор 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

9
ответ дан 10 December 2019 в 00:36
поделиться

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

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

1
ответ дан 10 December 2019 в 00:36
поделиться
Другие вопросы по тегам:

Похожие вопросы: