Mesmo os idiomas em que você tem manipulação explícita de ponteiros como C, são sempre passados por valor (você pode passá-los por referência, mas esse não é o comportamento padrão).
Qual é o benefício disso, por que tantos idiomas são transmitidos por valores e por que outros são transmitidos por referência ? (Entendo que Haskell é passado por referência, embora não tenha certeza).
programming-languages
language-design
Meu código não possui bugs
fonte
fonte
void acceptEntireProgrammingLanguageByValue(C++);
temp := a; a := b; b := temp;
E quando retornar, os valores dea
eb
serão trocados. Não há como fazer isso em C; você tem que passar ponteiros paraa
eb
e aswap
rotina tem de agir sobre os valores que eles apontam.Respostas:
Passar por valor é geralmente mais seguro que passar por referência, porque você não pode modificar acidentalmente os parâmetros para seu método / função. Isso torna o idioma mais simples de usar, pois você não precisa se preocupar com as variáveis que atribui a uma função. Você sabe que eles não serão alterados, e isso é frequentemente o que você espera .
No entanto, se você deseja modificar os parâmetros, é necessário ter alguma operação explícita para esclarecer isso (passe um ponteiro). Isso forçará todos os chamadores a fazerem a chamada de maneira um pouco diferente (
&variable
, em C) e explicita que o parâmetro variável pode ser alterado.Portanto, agora você pode assumir que uma função não alterará seu parâmetro variável, a menos que seja explicitamente marcado para fazê-lo (exigindo que você passe um ponteiro). Essa é uma solução mais segura e limpa do que a alternativa: suponha que tudo pode alterar seus parâmetros, a menos que eles digam que não podem.
fonte
Chamada por valor e chamada por referência são técnicas de implementação que foram confundidas com os modos de passagem de parâmetros há muito tempo.
No começo, havia FORTRAN. O FORTRAN só tinha chamada por referência, uma vez que as sub-rotinas precisavam modificar seus parâmetros, e os ciclos de computação eram muito caros para permitir vários modos de passagem de parâmetros, e não se sabia o suficiente sobre programação quando o FORTRAN foi definido pela primeira vez.
ALGOL surgiu com chamada por nome e chamada por valor. Chamada por valor era para coisas que não deveriam ser alteradas (parâmetros de entrada). Chamada por nome era para parâmetros de saída. A chamada por nome acabou por ser um grande problema, e o ALGOL 68 a abandonou.
A PASCAL forneceu chamada por valor e chamada por referência. Não foi possível ao programador dizer ao compilador que estava passando um objeto grande (geralmente uma matriz) por referência, para evitar explodir a pilha de parâmetros, mas que o objeto não deve ser alterado.
PASCAL adicionou ponteiros ao léxico do design de idiomas.
C forneceu chamada por valor e simulação por chamada, definindo um operador kludge para retornar um ponteiro para um objeto arbitrário na memória.
Os idiomas posteriores copiaram C, principalmente porque os designers nunca haviam visto mais nada. É provavelmente por isso que a chamada por valor é tão popular.
O C ++ adicionou um kludge em cima do C kludge para fornecer chamada por referência.
Agora, como resultado direto de chamada por valor vs. chamada por referência vs. chamada por ponteiro kludge, C e C ++ (programadores) têm dores de cabeça horríveis com ponteiros const e ponteiros para const (somente leitura) objetos.
Ada conseguiu evitar todo esse pesadelo.
O Ada não possui chamada explícita por valor x chamada por referência. Em vez disso, Ada possui parâmetros (que podem ser lidos, mas não escritos), parâmetros de saída (que DEVEM ser escritos antes que possam ser lidos) e parâmetros de saída, que podem ser lidos e escritos em qualquer ordem. O compilador decide se um parâmetro específico é passado por valor ou por referência: é transparente para o programador.
fonte
In the beginning, there was FORTRAN
.0
prefixo é inconsistente com todos os outros prefixos dez que não sejam de base. Isso pode ser um pouco desculpável em C (e principalmente em linguagens compatíveis com a fonte, por exemplo, C ++, Objective C), se octal foi a única base alternativa quando introduzida, mas quando as linguagens mais modernas usam ambos0x
e0
desde o início, apenas as faz parecer mal. pensando.A passagem por referência permite efeitos colaterais não intencionais muito sutis, que são muito difíceis de serem quase impossíveis de rastrear quando começam a causar o comportamento não intencional.
A passagem por valor, especialmente
final
,static
ou osconst
parâmetros de entrada fazem com que toda essa classe de erros desapareça.Linguagens imutáveis são ainda mais determinísticas e mais fáceis de raciocinar e entender o que está acontecendo e o que se espera que saia de uma função.
fonte
Swap
hacks que não usam uma variável temporária, embora provavelmente não sejam um problema, mostram o quão difícil pode ser um exemplo mais complexo para depurar.O objetivo de dividir programas grandes em pequenas sub-rotinas é que você pode raciocinar sobre as sub-rotinas independentemente. Passagem por referência quebra essa propriedade. (Como o estado mutável compartilhado.)
Na verdade, C é sempre passagem por valor, nunca passagem por referência. Você pode pegar um endereço de algo e passar esse endereço, mas o endereço ainda será passado por valor.
Há duas razões principais para usar o passe por referência:
Pessoalmente, acho que o número 1 é falso: quase sempre é uma desculpa para API ruim e / ou design de linguagem:
Você também pode simular vários valores de retorno empacotando-os em alguma estrutura de dados leve, como uma tupla. Isso funciona particularmente bem se o idioma oferecer suporte à correspondência ou desestruturação de padrões. Por exemplo, Ruby:
Muitas vezes, você nem precisa de vários valores de retorno. Por exemplo, um padrão popular é que a sub-rotina retorna um código de erro e o resultado real é gravado de volta por referência. Em vez disso, você deve lançar uma exceção se o erro for inesperado ou retornar um
Either<Exception, T>
se o erro for esperado. Outro padrão é retornar um booleano que informa se a operação foi bem-sucedida e retornar o resultado real por referência. Novamente, se a falha for inesperada, você deve lançar uma exceção, se a falha for esperada, por exemplo, ao procurar um valor em um dicionário, você deve retornar umMaybe<T>
.A passagem por referência pode ser mais eficiente que a passagem por valor, porque você não precisa copiar o valor.
Não, Haskell não é passado por referência. Nem é passar por valor. Passagem por referência e passagem por valor são estratégias estritas de avaliação, mas Haskell não é rigoroso.
De fato, a Especificação Haskell não especifica nenhuma estratégia de avaliação específica. A maioria das implementações da Hakell usa uma combinação de chamada por nome e chamada por necessidade (uma variante de chamada por nome com memorização), mas o padrão não exige isso.
Observe que, mesmo para idiomas estritos, não faz sentido distinguir entre passar por referência ou passar por valor para idiomas funcionais, pois a diferença entre eles só pode ser observada se você alterar a referência. Portanto, o implementador é livre para escolher entre os dois, sem quebrar a semântica da linguagem.
fonte
Há um comportamento diferente, dependendo do modelo de chamada da linguagem, do tipo de argumentos e dos modelos de memória da linguagem.
Com tipos nativos simples, a passagem por valor permite que você passe o valor pelos registradores. Isso pode ser muito rápido, já que o valor não precisa ser carregado da memória nem salvado de volta. Otimização semelhante também é possível simplesmente reutilizando a memória usada pelo argumento depois que o chamado é concluído, sem arriscar mexer com a cópia do objeto do chamador. Se o argumento fosse um objeto temporário, você provavelmente salvaria uma cópia completa fazendo isso (o C ++ 11 torna esse tipo de otimização ainda mais óbvio com a nova referência correta e sua semântica de movimento).
Em muitas linguagens OO (C ++ é mais uma exceção nesse contexto), você não pode transmitir um objeto por valor. Você é forçado a passar por referência. Isso torna o código polimórfico por padrão e está mais alinhado com a noção de instâncias próprias do OO. Além disso, se você deseja passar por valor, você deve fazer uma cópia, reconhecendo o custo do desempenho que gerou essa ação. Nesse caso, o idioma escolhe para você a abordagem com maior probabilidade de oferecer o melhor desempenho.
No caso de linguagens funcionais, acho que passar por valor ou referência é simplesmente uma questão de otimização. Como as funções nessa linguagem são puras e livres de efeitos colaterais, não há realmente nenhuma razão para copiar o valor, exceto a velocidade. Tenho certeza de que essa linguagem geralmente compartilhava a mesma cópia de objetos com os mesmos valores, uma possibilidade disponível apenas se você usasse uma semântica de referência de passagem (const). O Python também usou esse truque para seqüências inteiras e comuns (como métodos e nomes de classes), explicando por que números inteiros e seqüências de caracteres são objetos constantes no Python. Isso também ajuda a otimizar o código novamente, permitindo, por exemplo, comparação de ponteiros em vez de comparação de conteúdo e fazendo uma avaliação lenta de alguns dados internos.
fonte
Você pode passar uma expressão por valor, é natural. Passar expressão por referência (temporária) é ... estranho.
fonte
Se você passar por referência, estará efetivamente sempre trabalhando com valores globais, com todos os problemas das globais (ou seja, escopo e efeitos colaterais indesejados).
As referências, assim como as globais, às vezes são benéficas, mas não devem ser sua primeira escolha.
fonte