Olá, seja bem-vindo de volta.
Nesta aula, quero falar sobre o caso
de uso de ponteiros quando vamos usá-los como vamos usá-los. Sabemos como criá-los.
declará-los e defini-los.
Sabemos como atribuir o endereço de uma variável existente a eles usando o endereço do operador.
E sabemos como des-referenciá-los, como obter
o valor para o qual o ponteiro está apontando.
Agora, como vamos começar a usá-los.
Portanto, c oferece várias operações básicas que você pode executar em ponteiros.
Você pode atribuir um endereço a um ponteiro
sobre o qual já aprendemos.
Esta é uma variável existente onde a variável tem um endereço.
E você apenas olha para o endereço dessa variável usando o endereço do operador,
e temos um ponto nisso. Também podemos atribuir um ponteiro existente a outro ponteiro.
São duas maneiras de atribuir um endereço a um ponteiro.
Portanto, pode ser um nome de array por si só.
Ainda não falamos sobre arrays, mas quando você atribui um aumento a um ponteiro, tudo
o que você precisa fazer é especificar o nome. Você nem precisa usar o endereço da operadora.
Até agora, apenas nos concentramos em usar uma variável existente
e passar o endereço da própria variável.
Mas as 3 maneiras de fazer isso são mencionadas aqui.
Você também pode cancelar a referência de um ponteiro. Acabamos de saber que é onde você usa, o
asterisco, o operador de indireção.
Isso fornecerá o valor armazenado no ponteiro para. O ponteiro é o valor do
ponteiro, que é um endereço para o qual o ponto
está apontando, basta usá-lo. Você também pode obter um endereço de ponteiro.
Este é o endereço real do ponteiro quando você cria o
ponteiro onde o próprio ponteiro é armazenado.
Você também pode usar o operador de endereço que tenho nesse ponteiro.
E você também pode realizar aritmética de ponteiro. Teremos uma palestra separada sobre isso.
É aqui que você pode usar os operadores
mais e menos para adicionar inteiros a um ponteiro ou subtrair inteiros de um ponteiro.
E isso permite que você tenha uma notação específica e permite
que você pule pela memória mais rápido, especialmente se você estiver usando matrizes.
Então, o que você normalmente faz é adicionar um inteiro
a um ponteiro usando o
operador mais, isso
apenas definirá o endereço desse ponteiro em tantos bytes.
Se você subtrair
um número de um ponteiro, ele
definirá aquele endereço menos um certo número de bytes.
Você também pode aumentar o decréscimo em
um, e verá que isso é muito comum quando o usamos em matrizes.
Portanto, quando você está em uma matriz e deseja mover para o próximo elemento em vez de usar o
índice, pode usar um ponteiro e apenas incrementá-lo em um.
Se você quiser mover para o elemento anterior em vez de usar um índice,
você pode apenas diminuí-lo apontando em um.
Então, vamos falar sobre aritmética de ponteiros exclusivamente, mas isso é
algo que você pode fazer com ponteiros.
E, novamente, é uma notação particular que permite que você se mova
rapidamente na memória, seja
mais eficiente.
Você também pode encontrar a diferença entre 2 ponteiros.
Portanto, se você tiver 2 ponteiros e os elementos estiverem no mesmo array,
talvez você queira descobrir a que distância
eles estão, os elementos, você pode retirá-los.
Essa seria a diferença de
2 ponteiros e. Então, se você está no índice 5
e tem um ponto de ponteiro no índice 5, você tem outro ponto
apontando para o índice 15, você
poderia subtraí-los e
isso lhe daria quantos elementos estão ausentes, porque seria o endereço hexadecimal
menos o outro endereço hexadecimal assumindo o array na
memória contínua que é, ele mostrará a distância entre eles.
Você pode usar operadores relacionais para comparar os valores de 2 ponteiros.
Os ponteiros devem ser do mesmo tipo. Portanto, se ambos estão apontando para números inteiros, os dois
devem ser do mesmo tipo.
Isso apenas dirá se os endereços são maiores ou menores que.
Novamente, você pode identificar em qual parte do array você está.
Existem também 2 formas de subtração.
Acabei de falar sobre subtração, onde
você pode pegar um ponteiro e subtraí-lo de outro que você
obterá um
ponteiro ou pode subtrair um ponteiro de um
inteiro, o que lhe dará um ponteiro.
Portanto, subtraia um ponteiro de outro para obter um inteiro ou subtraia
um inteiro de um ponteiro para obter um ponteiro. Esses são os 2 casos.
Portanto, teremos em mente que veremos mais alguns exemplos.
Tenha cuidado ao usar o incremento ou decremento de ponteiros e fazer com
que o array saia dos limites. Portanto, a maioria das coisas que você faz
com a aritmética de ponteiros é quando você está operando em matrizes.
Porque, novamente, permite que você se mova rapidamente, é uma sintaxe simples.
Mas lembre-se de que as matrizes apresentam um erro de fora dos limites.
Portanto, se você incrementar muito, você sairá
dos limites do array. Se você diminuir demais, você
terá um under bounds, você entrará em um número negativo.
O próprio computador não controlará se um ponteiro ainda aponta para um elemento do array.
Portanto, usando o ponto da aritmética em matrizes, é preciso ter cuidado.
Se quiser ponteiros usados em expressões, você também pode fazer isso.
Falamos sobre isso um pouco antes, desreferenciamos o ponteiro e adicionamos 15
a ele. Acho que temos 20.
Portanto, o valor do ponteiro de pi pode ser usado em expressões aritméticas.
Portanto, se uma variável é definida para ser do tipo ponteiro para inteiro,
ela é avaliada usando as regras da aritmética de inteiros.
E mostramos isso com a desreferência. Portanto, se você
tiver uma variável e tiver um ponteiro, inicialize-o como nulo e, em seguida,
assine o endereço dessa variável no ponteiro. Você pode então dizer estrela pnumber mais é igual a 25.
Lembre-se de que a estrela pnumber está obtendo o valor para o qual aponta.
Então, o que isso significa é que o valor de pnumber
é igual ao valor pnumber mais 25.
Portanto, o valor será igual a 35.
Isso não está mudando o que está apontando porque estamos usando asterisco, então não está mudando o
endereço para o qual estamos apontando, não está
mudando o endereço em si porque não temos o endereço do operador.
Estamos mudando o valor para o qual está apontando.
Portanto, incrementando o valor da variável numérica em 25, a estrela indica que você está
perguntando o conteúdo para o qual a variável chamada pnumber está apontando.
Se um ponteiro apontar para a variável x, esse
ponteiro foi definido para ser um ponteiro para o mesmo tipo de dados que x.
Portanto, se você criar um ponteiro final, um ponteiro flutuante, só
poderá ter um endereço de
um tipo de dados que seja flutuante ou int, dependendo de qual deles você declarou.
Portanto, o uso do operador de indireção em
uma expressão é idêntico ao uso de x na mesma expressão.
E isso é tudo o que está dizendo é que se você usar um float ou
tiver um int e usar o operador de indireção, você desreferenciou, você pode fazer
o que puder se tiver um float nele, para o que quer que esteja apontando.
Uma variável definida como um ponteiro para um int pode armazenar o endereço
de qualquer variável do tipo in, sabemos disso.
Portanto, aqui o valor int é igual a 999, podemos atribuir esse int a pnumber,
desde que pnumber seja um ponteiro para um int, e então
podemos adicionar a ele.
A instrução operará com o novo valor da variável
e o novo valor do conteúdo será 1024.
Pnumber está apontando para o valor,
o endereço do valor. Portanto, se você alterar o ponteiro para
o valor que ele aponta, você alterará o valor desse valor de variável.
Portanto, lembre-se de que este é o conceito de alterar uma variável em um
ponto do ponteiro e você está alterando-o em várias variáveis.
Um ponteiro pode conter o endereço de qualquer variável do tipo apropriado.
Portanto, você pode usar uma variável de ponteiro para alterar os valores de muitas variáveis
diferentes se houver ints, doubles ou floats. Contanto que sejam do tipo compatível com o tipo
de ponteiro, você pode alterar esse valor. Você não pode alterar o
valor de pnumbers se pnumber estiver apontando para um float e o
valor for um inteiro. Você não seria capaz de fazer isso. Você teria um erro de compilação.
Outro exemplo aqui. Aqui vamos mostrar como
obter o endereço de algumas variáveis. Então, aqui neste principal, estamos fazendo
algumas aritméticas de
ponteiros nas expressões reais.
Portanto, não estamos fazendo nada no array, portanto, não estamos incrementando ou diminuindo
os ponteiros nele. Portanto, estamos diminuindo os valores. Portanto, temos 2 tipos
longos num1 e num2 e, em seguida, temos um ponteiro do tipo long, o inicializamos como nulo.
Atribuímos o endereço de num1 ao nosso ponteiro pnum.
E então atribuímos o valor de pnum a L,
o que significa que num1 agora será 2l.
Se incrementarmos num2, agora num2 ainda não fizemos
nada.
Portanto, o valor de pnum é 2l, o que significa que o valor de num1 é 2l.
L é apenas o sufixo para significar que é longo.
Se agora incrementarmos o valor longo num2,
será 0l, será 1l.
Se dissermos num2 mais igual a
estrela pnum, pnum é 2l, então será 1l
mais 2l, num2 agora será igual a 3l.
E então, se atribuirmos o endereço de num2 a pnum,
pnum agora será igual ao endereço de num2.
Portanto, se incrementarmos pnum para
o que estamos apontando, agora teremos 4l.
E vamos olhar para isso dentro de blocos frios,
mas estou explicando com você. Num1 vai ser igual a 2l.
Num2 vai ser igual a apontar
para pnum.
Pnum é 3l, então acho que vai ser 3l.
Pnum vai ser 3l, e pneum mais num2 vai
ser 6l.
Então, vamos ver se estou certo, analise isso em blocos de código.
Então, aqui estamos em blocos de código, tentamos fazer isso manualmente, mas
agora queremos ver se estamos certos. Vamos usar o depurador.
Vamos colocar isso aqui e veremos, à medida que avançamos no programa, como os valores estão mudando.
Então, vamos construir isso e depurá-lo.
Isso vai iniciar e vai parar na linha 10.
E se abrirmos nossa janela de observação, o
que podemos fazer depurando nos relógios do Windows, relógios, podemos ver os
valores conforme eles estão indo. Então agora num1
é zero, num2 é zero, pnum está apontando para nulo.
É por isso que é 0
x 0, porque sabemos que nulo representa 0 em pnumber.
E podemos usar isso quando comparamos em declarações if.
Então, depois de executarmos a linha 10, pnum agora deve ter o endereço de num1.
Então, vamos dar um passo adiante.
Agora pnum tem um endereço real 0x60ff08.
Depois de executar a linha 11, diremos que o valor de pnum é igual a 2l e não veremos realmente
nada na janela de observação para isso. Mas vamos executá-lo, pnum ainda
tem o mesmo endereço.
Num1 agora foi alterado porque pnum tinha o endereço
de num1, e nós o alteramos.
Portanto, agora num1 é 2. Agora, nas linhas 12, vamos incrementar num2.
Então num2 vai se tornar 1l.
Vai ser 1. O endereço de pnum ainda é o mesmo.
Agora vamos adicionar a num2 o que quer
que pnum fosse. Pnum lembre-se de
apontar para num1, então num2 deve ser 3.
Em seguida, vamos para 14, num2 é 3, num1 ainda é 2.
Agora na linha 15, vamos atribuir
a pnum um novo endereço de num2. Depois disso,
não seremos mais 0x60ff08.
Vamos ser 060ff04.
Portanto, agora, se incrementarmos o valor para o qual pnum está apontando, que é o número
2 e o número 2 é 3, o número 2 agora deve ser 4.
E vemos que são 4.
Agora vamos imprimir todos esses valores e você verá que o valor de num1
deve ser 2.
O valor de num2 deve ser 4.
Pnum está apontando para num2, então deve ser 4 ainda.
E então vamos dizer que o valor de pnum que é 4 mais,
num2 que é 4 deveria ser 8.
Então, eu posso realmente ter saído com 6.
Portanto, agora não poderemos ver isso na janela do relógio. Então, vamos apenas jogar
e ver a saída como instruções printf. Devemos ver 2448.
Então, vamos executar isso agora no modo sem
depuração e veremos 2 4 4 8.
Então vamos lá. Espero que tenha feito sentido.
Vamos voltar e continuar nossa discussão.
Então, quando recebermos informações, como podemos usar ponteiros dessa forma.
Até agora, quando usamos scanf para inserir valores, temos que usar o operador E comercial sempre que
tivermos um tipo de dados inter ou float. A única vez que temos é quando temos uma
matriz de caracteres e a razão para isso é porque o caractere a
é na verdade um ponteiro de caractere sobre o qual falaremos.
Mas se usarmos scanf para inserir valores e os usarmos em tipos de
dados normais que não são matrizes de caracteres, sempre tivemos que usar este operador add ou sub.
E a razão para isso é que o segundo argumento de scanf é um ponteiro.
Portanto, temos que enviar um endereço. Os ponteiros têm apenas valores de endereços.
Portanto, quando você tem um ponteiro que já contém um endereço, pode usar o nome do ponteiro como um argumento para scanf.
Portanto, agora você não precisa usar o operador E comercial se realmente criou um
ponteiro, porque o segundo argumento para o scanf é um ponteiro e, portanto, você
pode apenas dizer o nome do ponteiro.
No passado, se quiséssemos armazenar um valor em valor,
diríamos o valor do
e comercial no segundo argumento scanf.
Mas se tivermos um ponteiro que criamos e ele já aponta
para um endereço como faz neste exemplo,
então quando usamos scanf, tudo o que temos a dizer é o nome do valor,
o nome do ponteiro.
Portanto, aqui está um caso em que temos um inteiro, mas
não precisamos usar o E comercial porque temos um ponteiro para um inteiro.
Dizemos apenas valor percentual d, p.
Portanto, há outras ocasiões em que você pode passar nomes
regulares de variáveis para scanf, além de matrizes de caracteres, se você
tiver um ponteiro.
Você também pode imprimir o que eles inseriram depois e você notará
que o valor será realmente alterado para o que eles
inseriram porque estamos apontando para aquele endereço.
Testando para nulo.
Existe uma regra que você deve gravar em sua memória. Isto é muito, muito importante.
Falamos sobre desreferenciar, mas o que acontece se você desreferenciar um ponteiro
não intencionalizado, um ponteiro que aponta para nada ou
um ponteiro que não tem memória alocada para ele.
E daí se fizermos algo assim. Declaramos um ponteiro,
mas nunca o inicializamos de fato, e então tentamos atribuí-lo.
Nós des-referenciamos e atribuímos a ele, isso é um grande erro.
Portanto, a segunda linha basicamente significa que você
deseja armazenar o valor 5 no local para o
qual pt aponta, mas pt não aponta para nada porque não foi inicializado.
Portanto, pt agora terá um valor aleatório.
Portanto, não há como saber onde 5 será colocado, ele pode ser colocado em qualquer lugar da memória.
Isso pode ser inofensivo. Isso pode superestimar os
dados existentes ou as variáveis de estado existentes em seu programa.
Isso pode causar o travamento do programa. É muito problemático.
Portanto, quando você cria um ponteiro e aloca memória para armazenar o próprio ponteiro, ele
não aloca memória para armazenar os dados.
Então, dizendo instar pt, isso
alocou memória para o ponteiro, mas não
alocou memória para o valor do
ponteiro, o endereço. Lembre-se de que os ponteiros contêm endereços.
Ele criou o ponteiro, mas não criou nenhuma memória para o que está apontando.
Portanto, você não pode desreferenciá-lo.
Portanto, ele não aloca memória para armazenar os dados.
Antes de usar um ponteiro, deve ser atribuído um local de memória que já foi alocado
ou você deve usar a alocação de memória dinâmica usando malloc, sobre o qual falaremos.
Portanto, você pode atribuir o endereço de uma variável existente ao ponteiro
ou usar malloc que é a alocação de memória dinâmica.
Mas tenha isso em mente sempre que você usar um ponteiro se você o
criou e ele for nulo, você deseja verificar se há
nulo antes, falaremos sobre isso em um segundo. Mas se você criou um
ponteiro que não foi inicializado, não atribua nada a
ele, a menos que esteja apontando para outra variável inicializada dessa maneira ou tendo memória alocada.
Então é aqui que null entra em jogo.
Você pode realmente testar o valor nulo para ter certeza
de que não há um erro de desreferência. Certifique-se de que seu programa não trave.
Portanto, já sabemos que nulo basicamente aponta para
nada e devemos inicializar todas as nossas variáveis, nossos
ponteiros para nulo.
Portanto, devemos sempre fazer algo assim.
Nulo é um símbolo especial em c que representa o ponteiro equivalente
a zero com números comuns.
A seguir também define um ponteiro para nulo usando zero.
Então, podemos também, se não quisermos passar ou atribuir a null, podemos apenas dizer 0 e
isso é o equivalente a não apontar para nada.
Portanto, como nulo é equivalente a 0, se
você quiser testar se o valor de p é nulo, você pode fazer
algo como usar testes explícitos. Você pode dizer que o ponteiro é igual a nulo.
E se o ponteiro for igual a nulo, o que significa
que é zero, você pode realmente fazer algo.
Portanto, se não for zero ou não for nulo, é um valor verdadeiro ou qualquer
outro, essa é uma forma de verificar se há travamentos.
Antes de tentar remover a referência de algo, certifique-se
de que isso lhe dirá se há memória alocada ou não.
Portanto, você deseja verificar se há nulo antes de cancelar a referência de um ponteiro.
E você costuma fazer isso em
funções porque as funções pegam ponteiros como parâmetros.
E se o
chamador estiver passando dados inválidos ou passando um ponteiro sem memória alocada,
isso pode travar seu código. Portanto, o que você deseja fazer é certificar-se
de que o chamador tem um endereço válido para que possa verificar se há nulo.
Basicamente, o que isso significa ter sempre esses if verifica
para dizer não valor p ou não ponteiro.
E se não for um ponteiro, você pode desreferenciá-lo.
Estamos falando muito sobre ponteiros. Discutimos um pouco.
Tenho certeza que está ficando um pouco opressor. Você pode querer fazer uma
pausa e passar pelas aulas anteriores para tentar realmente sincronizar esse conceito em sua cabeça.
Os ponteiros contêm apenas endereços.
Mas há três partes, então pode se tornar problemático.
Mas fique positivo. Continue comigo e eu sei
que é confuso.
Já falamos sobre como trabalhar com endereços.
Falamos sobre como trabalhar com valores. Já falamos sobre como trabalhar com o
próprio ponteiro e sobre como trabalhar com variáveis que apontam para o ponteiro.
Portanto, há todas essas coisas de
que você precisa se lembrar, e pode ser muito opressor e frustrante.
E é sempre difícil tentar descobrir o que está acontecendo e vai exigir muita prática.
A melhor maneira de entender esse conceito e tudo o que
falamos até agora é
praticar, escrever programas curtos que usem todos os conceitos que mencionei.
Portanto, escreva um programa que obtenha
os valores usando um ponteiro, talvez usando o operador
de indireção, desreferenciamento. Escreva um programa que altera os valores usando ponteiros.
Então isso seria desreferenciamento, obter os valores usando ponteiros seria apenas o
nome do ponteiro que seria o endereço. Imprimindo endereços, examinamos toda essa sintaxe.
Pratique essas coisas inúmeras vezes, é assim que você vai ficar bom nisso.
Você tem que aumentar sua confiança e estudar esses conceitos
em detalhes para dominá-los.
Ainda temos muito o que falar sobre dicas, mas este pode ser o momento de apenas se afastar
e repassar o que conversamos até agora para que fique gravado em sua cabeça.
Obrigada.