Existem pois métodos pelo qual a linguagem de máquina pode passar argumentos para um subprograma (função). O primeiro método é enviar um parâmetro por valor. Este método copia o valor do argumento para um parâmetro formal de função. Portanto, quaisquer mudanças neste parâmetro ocorridas dentro da função não têm influência no correspondente argumento usado na chamada da função.

//+------------------------------------------------------------------+
//| Passando parâmetros por valor |
//+------------------------------------------------------------------+
double FirstMethod(int i,int j)
{
double res;
//---
i*=2;
j/=2;
res=i+j;
//---
return(res);
}
//+------------------------------------------------------------------+
//| Programa Script da função start (iniciar) |
//+------------------------------------------------------------------+
void OnStart()
{
//---
int a=14,b=8;
Print("a e b antes chamada:",a," ",b);
double d=FirstMethod(a,b);
Print("a e b após chamada:",a," ",b);
}
//--- Resultado da execução do script
// a e b antes chamada: 14 8
// a e b após chamada: 14 8

O segundo método é passar por referência. Neste caso, a referência para um parâmetro (não seu valor) é passada para um parâmetro de função. Dentro da função, ele é usado para referenciar o verdadeiro parâmetro especificado na chamada. Isso significa que mudanças no parâmetro afetarão o argumento usado na chamada da função.

//+------------------------------------------------------------------+
//| Passando parâmetros por referência |
//+------------------------------------------------------------------+
double SecondMethod(int &i,int &j)
{
double res;
//---
i*=2;
j/=2;
res=i+j;
//---
return(res);
}
//+------------------------------------------------------------------+
//| Programa Script da função start (iniciar) |
//+------------------------------------------------------------------+
void OnStart()
{
//---
int a=14,b=8;
Print("a e b antes chamada:",a," ",b);
double d=SecondMethod(a,b);
Print("a e b após chamada:",a," ",b);
}
//+------------------------------------------------------------------+
//--- resultado da execução do script
// a e b antes chamada: 14 8
// a e b após chamada: 28 4

MQL5 usa ambos os métodos, com uma exceção, arrays, variáveis tipo estrutura e objetos de classe são sempre passados por referência. A fim de evitar modificações nos parâmetros reais (argumentos passados na chamada da função) use o especificador de acesso const. Ao se tentar modificar o conteúdo de uma variável declarada com o especificador const, o compilador gerará um erro.

Observação
Deve se notar que os parâmetros são passados para uma função em ordem inversa, ou seja, o último parâmetro é calculado e passado primeiro, depois o último mas apenas um, etc. O último parâmetro calculado e passado é aquele que está em primeiro lugar depois da abertura dos parênteses.

Exemplo:

void OnStart()
{
//---
int a[]={0,1,2};
int i=0;

func(a[i],a[i++],"First call (i = "+string(i)+")");
func(a[i++],a[i],"Second call (i = "+string(i)+")");
// Result:
// First call (i = 0) : par1 = 1 par2 = 0
// Second call (i = 1) : par1 = 1 par2 = 1

}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void func(int par1,int par2,string comment)
{
Print(comment,": par1 = ",par1," par2 = ",par2);
}

Na primeira chamada (ver exemplo acima) a variável i é usada pela primeira vez na concatenação de strings:

"Primeira chamada (i = "+string(i)+")"

Aqui o valor não muda. Então a variável i é usada no cálculo do elemento do array a[i++], ou seja, quando o elemento do array com índice i é acessado, a variável i é incrementada. E só depois disso o primeiro parâmetro com valor alterado da variável i é calculado.

Na segunda chamada o mesmo valor de i (calculado sobre a primeira fase da função chamada) é utilizado no cálculo de todos os três parâmetros. Somente depois de os primeiros parâmetros serem calculados, a variável i é alterada novamente.