Olá, seja bem-vindo de volta.
Nesta palestra, quero falar sobre um conceito chamado passagem por referência.
Quando começamos a falar sobre ponteiros, mencionei uma das vantagens é que você
não precisa usar tanto variáveis globais.
Porque quando você passa um ponteiro para
uma função, as funções podem usar esse parâmetro. E ao modificar esse parâmetro, os dados
serão modificados fora da função.
Portanto, este é um conceito valioso e valioso.
Vamos conversar sobre isso. Este é mais um conceito. C realmente não emprega isso.
E vou mostrar como e dar alguns exemplos
de como isso funciona.
Portanto, há esse conceito de passagem por valor. Isso tem a ver com funções.
Falamos sobre como passar dados para uma função, mas como eles são realmente
passados em termos de linguagem no compilador.
Portanto, há um conceito de passagem por valor. E é sobre
isso que conversamos até agora. Sempre que chamamos a função
além da função scanf, mas toda vez que chamamos a função,
quando você modifica os dados dentro da função, eles não
são modificados fora da função.
Então, falamos sobre variáveis locais que são um conceito diferente.
Mas quando você passa dados para uma função, há duas maneiras de passá-los.
Você pode passar por valor, pode passar por referência.
C passa exclusivamente por valor, assim como
java, todos
passam por valor. Mas simula passagem por referência
porque você copia endereços.
E assim, embora esse conceito
de passagem por referência não seja especificamente empregado por
c, ele o simula. E para entender o que significa passar
por referência e por que isso é importante, vamos falar sobre isso.
Mas a passagem por valor ocorre quando uma função copia o valor real de um
argumento para o parâmetro formal da função.
E é por isso que se você tem um
inteiro, um número flutuante ou um duplo e passa isso como um argumento para
uma função, essa variável real não é modificada fora da função porque é copiada.
Portanto, as alterações feitas no parâmetro dentro da função não têm efeito no argumento.
O argumento transmitido não é modificado.
Portanto, quase atua como uma variável local porque é uma cópia.
O que realmente está acontecendo nos bastidores é que está sendo copiado e passado para a função.
O programa C usa chamada por valor para passar argumentos.
Tudo se chama por passagem de valor por valor.
Esses termos são usados de forma intercambiável.
Portanto, novamente, isso significa que o código dentro de uma função não pode alterar os argumentos usados para chamar a função.
Não há alterações nos valores, embora eles tenham sido alterados dentro da função.
Parece que são variáveis locais.
E é por isso que antes, até este
ponto, você precisava criar dados globais se quisesse compartilhar os dados de uma função para a próxima.
E é assim que c funciona.
Mas o que acontece quando você realmente passa uma
cópia de um endereço. Então, ele pode imitar a passagem por referência.
Mas aqui está um exemplo de como temos usado a passagem por valor, que é muito bem demonstrado aqui.
Essa rotina de troca é um caso de uso frequente para demonstrar a passagem
por valor, porque o que ela faz é criar um temporário e trocar os parâmetros pelos
quais os argumentos estão sendo passados.
Portanto, se temos uma função chamada swap que leva dois
inteiros, criamos uma variável local temp.
Se atribuirmos ao parâmetro x a temp, temp vai ser
igual a x. Se dissermos que x é igual ay,
y agora será igual a x. Se dissermos que y é igual a temp,
y será igual a temp.
Mas ao chamar essa função passando esses valores, x e
y vão ser locais para esta função.
Essas variáveis não serão modificadas fora desta função.
E esta função demonstra que é muito útil porque estamos
atribuindo ay e estamos atribuindo a x.
E x e y são locais para esta função porque estamos passando por valor,
eles não são modificados fora desta função e é por isso que temos que usar variáveis globais.
Mas eles são inteiros. Eles são dois inteiros x e y. Não são endereços,
são inteiros e isso é significativo.
Então, se chamarmos
swap e vermos como é, você vai
notar que os valores não mudam.
Portanto, se criarmos uma variável chamada 100 b
com 200 e imprimirmos as variáveis antes de trocarmos,
100 e 200, então chamarmos swap,
as variáveis não serão alteradas
depois de imprimi-las novamente.
Então, vamos examinar isso em blocos de código e isso demonstrará a passagem por valor.
Então aqui está nosso exemplo. Temos nossa rotina de troca.
Estamos trocando os valores de dos dados que passaram xe y.
Estamos chamando de 100 200.
Vamos imprimir antes de chamar a função.
Vamos imprimir depois.
E o que você notará é que as variáveis aeb nunca mudam.
E a razão para isso é porque estamos fazendo passagem por valor.
Então, se eu executar este programa, podemos
ver que antes da troca, o valor de a era 100, depois que você trocou, o valor de a era
100 e o mesmo para b 200 e 200.
E a razão para isso é porque passamos por valor. Se passarmos por referência,
essas variáveis aeb mudariam após a chamada para swap.
Agora, que tal se empregarmos essa mesma técnica de passagem por valor, mas
copiarmos ponteiros, o que você acha que vai acontecer?
Portanto, ponteiros e funções se dão muito bem juntos.
Você pode passar um ponteiro como um argumento para uma
função e também pode fazer com que uma função retorne um ponteiro como seu resultado.
Também mostraremos como fazer isso.
Mas a passagem por referência copia o endereço de um argumento para o parâmetro formal.
O endereço é usado para acessar o argumento real usado na chamada.
Isso significa que as alterações feitas no parâmetro
dentro da função afetam o argumento que está na chamada.
E mesmo que não seja tecnicamente passado por referência, ele
imita ou simula passagem por referência.
Estamos copiando o endereço. Se você copiar o endereço,
terá um novo endereço e memória para esse valor, o endereço.
Mas se você tentar atribuir a esse endereço, ele atribuirá ao endereço.
Portanto, embora seja passado por valor, ele está copiando.
E se você estiver copiando um número inteiro regular, você não fará alterações. Se você estiver copiando um endereço, ele
estará na memória, portanto, quando você fizer alterações, fará alterações nesse endereço.
Portanto, para passar o valor por
referência, ponteiros de argumento são passados para as funções como qualquer outro valor.
Você precisa declarar os parâmetros da função como tipos de ponteiro com o asterisco.
As alterações dentro da função também são refletidas fora da função.
Você não precisa usar tanto variáveis globais.
Ao contrário de uma chamada por valor, onde as alterações não refletem fora da função.
Ainda estamos copiando ainda estamos passando por valor é apenas o valor de uma cópia do endereço.
Portanto, este é um conceito importante a ser entendido.
Agora, quando criarmos nossa função de
troca, vamos criá-la com a declaração dentro da função
como asterisco, estrela e estrela.
Isso quer dizer que essa função leva ponteiros.
Agora, quando atribuímos dentro desta função, temos que
usar o operador de indireção para desreferenciar porque
queremos atribuir os valores. Vamos atribuir o valor que
x que x aponta para a temperatura.
Vamos atribuir o valor de x pontos para o valor para o
qual y aponta e, em seguida, vamos atribuir o valor para o qual y aponta, para temp.
Temp é um número inteiro, então não precisamos desreferenciá-lo.
Agora, quando o
invocamos com a100 e b200,
passando aeb, aeb não são ponteiros, então temos que passar o
endereço porque a função recebe ponteiros.
Sempre que você tem uma função que leva ponteiros e você não declara um ponteiro,
você deve usar o endereço do operador e é a mesma coisa que usamos o scanf.
Mas agora você vai notar que quando
invocamos isso após a chamada para trocar as variáveis aeb serão
alteradas, elas serão trocadas. A vai ser 200 b vai ser 100.
Portanto, agora vamos olhar para este exemplo com nossa mudança de método de troca para ponteiros em blocos de código.
Estamos em blocos de código. Modificamos a função de troca para obter ponteiros.
Portanto, temos que desreferenciá-los quando os atribuímos.
Agora também passamos os endereços de aeb porque agora são inteiros e
a troca agora leva ponteiros. Então, quando executarmos isso, veremos
que a chamada e depois agora serão alteradas.
Portanto, antes de chamarmos de swap, a é igual a 100 depois de chamarmos de swap é 200.
Antes de chamarmos a troca para b, são 200. Depois disso, são 100.
Portanto, podemos alterar os valores das variáveis dentro da função
porque estamos fazendo uma cópia do endereço. Muito, muito importante e uma das
grandes vantagens dos ponteiros.
Então, vamos resumir a sintaxe. Eu já fiz, mas vamos examinar com mais detalhes.
Você pode comunicar dois tipos de informações sobre uma variável a uma função.
Você pode transmitir o valor de
xe a função deve ser declarada com o mesmo tipo de x.
Nós sabemos isso. Sempre que passamos dados para
uma função, temos que ter os mesmos tipos quando a chamamos.
E aqui temos int function1 para invocá-lo e é definido como um int num.
Portanto, quando transmitimos o valor de x na função, eles devem ser declarados com o mesmo tipo.
E então, se você realmente quiser invocar um endereço de x, é necessário
que a definição da função inclua um ponteiro para o tipo correto.
Portanto, sempre que usar ponteiros, você deve ter certeza de que, se estiver
transmitindo dados e esses dados não forem um ponteiro, você terá que obter o endereço deles.
O que isso está resumindo é que se você definir uma função que não tenha um
ponteiro, você pode apenas chamá-la e passar os dados.
Certifique-se de que não é um ponteiro.
Se você tiver uma função que leva um ponteiro
de asterisco, ao
invocá-la, será necessário invocá-la com o operador add ou sub.
Você tem que passar o endereço.
Essa é a sintaxe.
Agora você também pode qualificar um parâmetro de função usando a palavra-chave constant.
Isso indica que a função tratará o argumento passado para este parâmetro como uma constante.
Só é útil quando o parâmetro é um ponteiro.
Porque se você está passando por valor e não está passando ponteiros, não importa porque isso não
vai afetar a mudança do lado de fora da função então
por que você faria uma constante, quem se importa se você alterá-la .
A única razão pela qual você torna um ponteiro constante é porque você não quer
que os valores da função sejam alterados.
E assim você pode aplicar a palavra-chave constante a um parâmetro que é um
ponteiro para especificar que a função não alterará o valor para o qual o argumento aponta.
Muito importante.
Portanto, neste exemplo, temos uma função de envio de mensagem. Leva um ponteiro de
mensagem p.
Colocamos a palavra-chave constante antes da estrela char.
Portanto, este tipo de parâmetro p mensagem é um ponteiro para um caractere constante.
É o valor do caractere que é constante, não seu endereço.
Portanto, neste caso, o que estamos fazendo é o ponteiro para o qual está sendo apontado não pode mudar.
Podemos alterar o endereço, mas não podemos alterar o que está apontando para o valor.
A sintaxe é semelhante a quando criamos constantes em outros ponteiros.
A palavra-chave constante é a primeira coisa exibida, então você não pode alterar o valor.
Agora você pode especificar que o próprio ponteiro é constante,
mas isso faz pouco sentido porque o endereço é passado por valor.
Você não pode alterar o
ponteiro de renderização na função de chamada. Então, isso é só se você tentar fazer com que o endereço não possa
ser alterado, qual é o ponto porque é passado por valor. Então, na verdade, você
está criando um novo local de
memória para manter esse endereço específico. Então, se você mudar, quem se importa, não vai mudar do lado de fora.
Você só quer ser capaz de tornar o parâmetro
uma constante para não alterar o valor para o qual ele está apontando,
você não pode alterar
o ponteiro original e a função de chamada de qualquer maneira porque é
passado por valor passado por cópia.
O compilador sabe que um argumento que é um ponteiro para dados
constantes será seguro, portanto, há vantagens nisso. Você não pode modificar acidentalmente o que o valor está apontando.
Portanto, você pode fazer isso novamente se não quiser que as alterações sejam refletidas na parte externa da função.
Se você passar um ponteiro para dados constantes como o argumento de
um parâmetro, o parâmetro deve ser usado como acima.
Quando você invoca a função e a passa como constante, então você precisa
que a declaração da função também tome um ponteiro de caractere constante, assim
como outros ponteiros normais que são constantes.
Eles devem ser definidos e os define devem corresponder ao protótipo.
Retornando ponteiros de uma função.
Retornar um ponteiro de uma função é um recurso poderoso em particular.
Ele fornece uma maneira de retornar não apenas um único valor, mas todo um conjunto de valores.
Portanto, novamente, uma das outras vantagens que mencionei sobre
os ponteiros é que agora você pode retornar mais dados de uma função.
No passado, você só podia retornar um valor.
Mas com um ponteiro, porque um ponteiro pode
estar apontando para uma estrutura sobre a qual ainda não falamos ou
até mesmo outras coisas, você
pode retornar vários valores. Portanto, sabemos como passar ponteiros para funções, agora
queremos falar sobre o retorno de ponteiros.
Você teria que declarar uma função retornando um ponteiro. Portanto, a declaração da função deve corresponder.
É muito fácil fazer isso. Você apenas diz usar o asterisco ao
declarar a função ou defini-la e ter o tipo de retorno desse ponteiro para o qual ele está apontando.
Portanto, tenha cuidado, embora haja riscos específicos relacionados ao retorno de um ponteiro.
Use variáveis locais para evitar interferir na variável para a qual o argumento aponta.
É um tanto problemático quando você retorna ponteiros porque se você modificar esse ponteiro dentro
da função e, em seguida, retorná-lo,
pode ser algo que você não pretende fazer.
Portanto, sempre use variáveis locais se não quiser afetar uma variável para a
qual seu argumento aponta.
E então esta aula descreveu como
você pode passar ponteiros para
funções e como você pode retornar ponteiros de funções.
E usando este mecanismo de passar ponteiros para uma
função, ele demonstra ou imita a passagem por referência
porque você está copiando em um endereço.
E quando você copia um endereço, se você escrever para aquele endereço, você
atualizará tudo o que estiver
apontando para aquele endereço, o que efetivamente significa que você está alterando o
valor dos dados dentro da função, eles irão refleti-lo fora da função.
Usamos a rotina de troca para demonstrar isso.
Este é um conceito muito poderoso. E você quer imitar a passagem por referência,
você pode usar ponteiros para fazer isso e você deseja fazer isso quando quiser alterar coisas fora da função.
Obrigada.