Buenas,
He estado trabajando en mis bibliotecas, y mientras seguía avanzando sobre mis algoritmos llegué a material que ofrece el MIT que a mi ver le terminaría poniendo la cereza al postre.
A los algoritmos ya los tengo estudiado y estoy en proceso de portarlo a mi querido Lazarus.
Mi duda ahora se debe a que el material de estudio, y que ya había visto de otras fuentes de consulta y en bibliotecas libres aprovechan y recomiendan mucho el uso de punteros y la aritmética. Y si bien le voy entiendiendo hay cosillas que se me escapan un poco. Miedo a los punteros le estoy perdiendo gracias a Dios.
Hasta donde yo tengo entendido podemos reservar memoria con AllocMem() y obtener el puntero a ese bloque. Este bloque de memoria lo podríamos usar como vector, matriz, etc.
Suponiendo que queremos trabajar con tipo double, podríamos hacer algo como esto:
procedure TForm1.Button1Click(Sender: TObject); var Matrix: PDouble; begin Matrix := AllocMem(ROWS*COLS*SizeOf(Double)); // operamos FreeMem(Matrix); end;
En el código se aprecia que se reservan los bytes necesarios para la cantidad requerida y adecuada para el tipo. Para ser exactos: ROWS*COLS*8 bytes
Ahora bien, mi primera duda es cómo se debiera de ubicarse en cada "celda" de 8 bytes. ¿Es válido esto?
procedure TForm1.Button1Click(Sender: TObject); var Matrix: PDouble; i, Size: integer; begin //reservamos Size := ROWS * COLS; Matrix := AllocMem(Size * SizeOf(Double)); // operamos for i := 0 to Size - 1 do Matrix[i] := random; // liberamos FreeMem(Matrix); end;
¿O debe hacerse así?
procedure TForm1.Button1Click(Sender: TObject); var Matrix: PDouble; i, Size: integer; begin //reservamos Size := ROWS * COLS; Matrix := AllocMem(Size * SizeOf(Double)); // operamos for i := 0 to Size - 1 do (Matrix + i)^ := random; // liberamos FreeMem(Matrix); end;
¿O es lo mismo?
Esto lo pregunto tras leer este hilo en el foro de Lazarus. No me queda bien claro.
Relacionada con esta duda, me asalta otra. Esta aritmética de punteros tiene sentido mientras la memoria reservada sea contínua, al menos así es como lo tengo entendido. Y lo que leí en unos comentarios en una biblioteca llama mrMath me deja un poco intranquilo:
function MtxAlloc( NumBytes : TASMNativeInt ) : Pointer; begin assert(NumBytes and $7 = 0, 'Numbytes not multiple of 8'); Result := nil; if NumBytes <= 0 then exit; // align to next multiple of 16 bytes if NumBytes and $F <> 0 then NumBytes := NumBytes and $FFFFFFF0 + 16; Result := GetMemory(NumBytes); if Assigned(Result) then begin // normally getMemory should return aligned bytes -> // but just in case: if (TASMNativeUInt( Result ) and $F) <> 0 then begin PDouble(Result)^ := 0; inc(PByte(Result), 8); memInitfunc(Result, NumBytes - 8, 0); dec(PByte(Result), 8); end else memInitFunc(Result, NumBytes, 0); end; end;
Ahi tiene anotado: que normalmente GetMemory devuelve bytes alineados. La ayuda no dice nada del tema:
GetMemory allocates a memory block.
GetMemory allocates a block of the given Size on the heap, and returns the address of this memory. The bytes of the allocated buffer are not set to zero. To dispose of the buffer, use FreeMemory. If there is not enough memory available to allocate the block, an EOutOfMemory exception is raised.
If the memory needs to be zero-initialized, you can use AllocMem.
Note: GetMemory is the C++ compatible version of GetMem.
La llamada de AllocMem del código inicial que expuse, y que corresponde a Lazarus, no es más que una envoltura la llamada a GetMem. Así lo deja expreso su documentación:
Allocate and clear memory.
DeclarationSource position: heaph.inc line 93
function AllocMem(
Size: PtrUInt
):pointer;
DescriptionAllocMem calls getmem GetMem, and clears the allocated memory, i.e. the allocated memory is filled with Size zero bytes.
Esto dice GetMem:
procedure Getmem(
out p: pointer;
Size: PtrUInt
);
function GetMem(size: PtrUInt
):pointer;
DescriptionGetmem reserves Size bytes memory on the heap, and returns a pointer to this memory in p. What happens if no more memory is available, depends on the value of the variable ReturnNilIfGrowHeapfails: if the variable is True then Nil is returned. If the variable is False, a run-time error is generated. The default value is False, so by default an error is generated.
Ahora bien, podemos usar directamente la llamada MAlloc de la librería Cmem, ya que en última todo se delega a esta, y que no es más de lo mismo que vinimos usando: es un wrapper a la biblioteca de C++. ¡Por lo que todo termina en roma!
Declaration
Source position: cmem.pp line 51
function Malloc(
Size: ptruint
):Pointer;
ArgumentsSize
Requested size for the new memory block.
Function resultA pointer to the newly allocated memory block
DescriptionMalloc is the external declaration of the C librarys malloc call. It accepts a size parameter, and returns a pointer to a memory block of the requested size or Nil if no more memory could be allocated.
El asunto es que no veo en ninguna parte de la doc, de delphi como de Lazarus, que se aclare el punto si efectivamente hay vía segura de que la memoria estará alineada y continua. O si existe posibilidad de que no.
¿Alguno sabría aclararme estas dudas?
Saludos,