2009-04-03 12 views
9

ya que leer acerca de que pasa por referencia y por lo¿Cómo se pasa una matriz por referencia en Delphi?

procedure test(var x:integer); 
begin 
    x:=x+5; 
end; 

actualizaciones de código anteriores por lo que el 5 por referencia. Supuse que si estaba actualizando una matriz por referencia podría declarar var X: array of bla ... tener algunos errores vinculados y solo quería saber si debería estar usando el tipo de datos para el puntero a los datos o si el puntero siempre es int ... solo para saber si es la forma en que estoy haciendo mi aprobación por referencia u otra cosa en mi código que es el problema.

Respuesta

19

Si pasa la matriz dinámica como un parámetro que no es var, el compilador realizará una copia.

El siguiente ejemplo de código pequeño demuestra que al mostrar 37/42 en el título del formulario.

procedure IncArray1(data: array of integer); 
var i : integer; 
begin 
    for i := Low(data) to High(data) do 
    data[i] := data[i] + 5; 
end; 

procedure IncArray2(var data: array of integer); 
var i : integer; 
begin 
    for i := Low(data) to High(data) do 
    data[i] := data[i] + 5; 
end; 

procedure TForm8.FormCreate(Sender: TObject); 
var 
    data: array of integer; 
begin 
    SetLength(data, 1); 
    data[0] := 37; 
    IncArray1(data); 
    Caption := IntToStr(data[0]); 
    IncArray2(data); 
    Caption := Caption + '/' + IntToStr(data[0]); 
end; 

Si nos fijamos en el código ensamblador generado, IncArray1 comienza con

004552B4 8BCA    mov ecx,edx 
004552B6 85C9    test ecx,ecx 
004552B8 7807    js $004552c1 
004552BA 8B1C88   mov ebx,[eax+ecx*4] 
004552BD 49    dec ecx 
004552BE 53    push ebx 
004552BF 79F9    jns $004552ba 
004552C1 8BC4    mov eax,esp 

Esta matriz de origen código copia a la pila y establece eax a la dirección del primer elemento (= dirección almacenada en el puntero de la pila después del último empujón). La pila crece para que el código comience con el último elemento (edx contiene Alto (datos) cuando se llama IncArray1) y repite (elemento de lectura, elemento de inserción, índice de disminución) hasta llegar al elemento 0.

IncArray2 no contiene tal código. La persona que llama almacena la dirección de los datos en el registro eax antes de llamar a IncArray2 e IncArray2 solo utiliza esta dirección.

En caso de que no desee utilizar 'var' por alguna razón, puede pasar una dirección de los datos a su método. Pero como no puede usar datos de sintaxis:^array of integer 'en la declaración de parámetros, tendrá que declarar un tipo para sus datos. Y tendría que usar 'datos ^' en lugar de 'datos' en todas partes en el método.

type 
    TData = array of integer; 
    PData = ^TData; 

procedure IncArray(data: PData); 
var i : integer; 
begin 
    for i := Low(data^) to High(data^) do 
    data^[i] := data^[i] + 5; 
end; 

procedure TForm8.FormCreate(Sender: TObject); 
var 
    data: TData; 
begin 
    SetLength(data, 2); 
    data[0] := 37; 
    IncArray(@data); 
    Caption := IntToStr(data[0]); 
end; 

probado con Delphi 2007.

+0

Ok, ¿hay alguna manera de hacerlo? Tengo una gran matriz en la que quiero trabajar por referencia. copiar la matriz es muy costoso. – Arthur

+0

Sí, al igual que IncArray2 en el código anterior: prefijo 'var' de usuario. – gabr

+0

@gabr, ¿podría verificar si se copia una matriz dinámica cuando no se muta?Sé que las cadenas son copy-on-write. –

6

respuesta de Gabr es correcta, pero el punto clave está enterrado profundamente bastante que voy a llevarlo a cabo como un post aparte:

Definir los tipos de primera! En este caso específico, el compilador aceptó un conjunto de enteros allí, pero eso es solo porque tiene un significado especial y es NOT lo que esperaba. Cualquier otro intento de definir un tipo en la definición del procedimiento simplemente habría fallado.

A diferencia de C, si quieres dos cosas sean compatibles asignación tiene que declararlos como siendo el mismo tipo , no simplemente dos tipos que se construyó la misma:

Var 
    A : Array [1..4] of Integer; 
    B : Array [1..4] of Integer; 

Begin 
    A := B; 

no se compilará . Más bien:

Type 
    Array4 = array [1..4] of Integer; 

Var 
    A : Array4; 
    B : Array4; 

Begin 
    A := B; 

y el compilador hace lo que cabría esperar.

+0

Después de haber escapado para cambiar el código después de la publicación de Gabr ahora tengo el problema exacto que estás diciendo, no vendrá pila, dice que los tipos formales deben ser los mismos .. A y B para mí están en unidades diferentes .. ¿que puedo hacer? – Arthur

+0

Supongamos que la unidad A "usa" la unidad B. Declara el tipo compartido en la unidad B, y ambos pueden "ver" esa definición. – Argalatyr

+2

Si ejecuta el código de gabr pero con toda la "matriz de enteros" reemplazada por TIntArray (donde TIntArray = matriz de entero), entonces la matriz siempre se pasa por referencia, y los datos [0] son ​​37 + 5 + 5 = 49. ¿No es ese tipo de extraño? –

Cuestiones relacionadas