Olá, seja bem-vindo de volta.
Nesta palestra, quero falar sobre o uso de ponteiros e estruturas,
como eles estão relacionados.
Podemos fazer com que os elementos dentro de uma
estrutura sejam um ponteiro e também podemos fazer com que nossas próprias estruturas sejam ponteiros.
Dois conceitos muito úteis.
Portanto, a estrutura é importante aqui c permite ponteiros para duas estruturas.
Portanto, quando criamos uma variável de estrutura de tipo, podemos
criá-la como um ponteiro.
Os ponteiros para estruturas são mais fáceis de manipular do que as próprias estruturas.
Novamente, é uma espécie de notação, torna
as coisas muito mais fáceis e também é mais eficiente.
Em algumas implementações mais antigas da linguagem
C, uma estrutura não
pode ser passada como um argumento para uma função,
mas sabemos que um ponteiro pode. Portanto, esta é outra razão pela
qual você deseja que uma estrutura seja um ponteiro ao
declará-la, porque então você pode passá-la em versões mais antigas de
c e passá-la como um argumento para uma função.
Mesmo se você puder passar uma estrutura como um argumento,
passar um ponteiro é mais eficiente.
Sabemos que isso é mais eficiente. Já falamos sobre isso no passado,
como passar de importante pode ser mais eficiente.
Nesse caso, é ainda mais eficiente
porque se você está passando uma estrutura inteira, se a estrutura
é grande e contém muitos elementos, você está
copiando todos os elementos da estrutura para aquele parâmetro.
Se você está passando um ponteiro apenas copiando o endereço.
Portanto, é ainda mais importante com
ponteiros como argumentos estruturados porque as estruturas podem ser grandes.
Muitas representações de dados usam estruturas, contendo ponteiros para
outras estruturas.
Você verá muito isso em todas as estruturas
de seus programas em C que são ponteiros e pelos motivos que mencionamos.
O fato de você poder passá-los como parâmetros em versões anteriores para
ver e também é mais eficiente porque você faz
tudo bypass por valor que é copiar muito mais
eficiente para copiar um endereço do
que copiar todos os elementos dessa grande estrutura.
Portanto, declarar uma estrutura como um ponteiro é bastante simples.
Quando você define uma variável para ser um ponteiro a ser atingido, você apenas usa esse asterisco
após o tipo dos dados.
E dissemos que as estruturas são realmente um novo tipo.
Nesse caso, nossa estrutura de data sempre importante.
Dizemos apenas struct date e usamos o ponteiro de data de
asterisco, e você provavelmente vai querer inicializá-lo como nulo.
Porque lembre-se, quando você diz struct date pointer data struct
data ponteiro, que não aloca nenhuma memória para,
ele cria memória para armazenar o ponteiro real.
Não está criando memória para os dados dentro dele.
Portanto, você pode
atribuir a este ponto de data ou o endereço de outra
data de estrutura ou pode alocar uma memória para ele
em outro lugar ou pode inicializá-lo
com alguns dados dessa maneira também.
Portanto, o ponteiro de data variável pode ser atribuído como outros ponteiros,
você pode configurá-lo para apontar para a data de
hoje com a instrução de atribuição, se já tivermos uma declaração da data de hoje.
Usaríamos apenas o e comercial para obter o endereço dele.
Portanto, criamos uma estrutura de data normal e é a data de hoje.
Isso criaria memória para os três elementos.
E então criamos nosso ponteiro e apenas atribuímos a
ele o endereço da outra data.
Bem direto. Fizemos isso com tipos de dados regulares,
não é diferente quando você faz isso com estruturas.
Em seguida, você pode acessar indiretamente qualquer um dos membros da estrutura de dados apontados pelo ponteiro de data.
Portanto, precisaríamos fazer algo assim.
Se você quiser acessar o elemento da
estrutura do ponteiro do dia, terá que retirá-lo da referência primeiro.
E provavelmente seria útil colocá-lo entre parênteses para que seja bem simples.
Você tem que remover a referência do ponteiro de data para obter o
valor para o qual a data de
hoje está apontando, o valor do endereço da data de hoje para
o qual está apontando, a fim de definir valores específicos.
Então, você iria retirá-lo da referência
e, em seguida, fazer dot day igual a 21 e isso atribuiria 21 ao
ponteiro do dia.
Agora, o acima está apenas dizendo para qualquer data que esteja apontando,
queremos acessar esse valor e definir o dia como 21.
Nesse caso, os parênteses são necessários porque o ponto tem
uma precedência mais alta do que o asterisco.
Se você não colocou os parênteses aqui, fica confuso porque tentaria fazer o ponto
primeiro antes de realmente colocar o ponteiro no ponteiro, para
apontá-lo para os dados.
Portanto, ele tentaria acessar os dados com um ponteiro nulo essencialmente.
E assim, ao retirar a referência, você está obtendo o valor.
Agora, quando usamos structs um ponteiros.
Se quiséssemos testar o valor dos meses armazenados na estrutura de data apontada pelo ponteiro de
data, poderíamos fazer algo assim.
Removemos a referência do ponteiro, colocamos entre parênteses, de modo que tenha precedência sobre o ponto e, em
seguida, dizemos que ponto mês é igual a 12.
É uma sintaxe perfeitamente válida.
Mas é um pouco complicado.
Portanto, c tem sua própria sintaxe, então você não precisa remover a
referência de tudo e colocá-lo entre parênteses para estruturas.
Quando você deseja acessar o que a estrutura está apontando, não é
necessário cancelar a referência dessa forma.
Existe um operador especial que existe em c para estruturas de ponteiros, e
é o traço maior que.
É um traço seguido pelo sinal de maior.
E isso permite que você não precise remover a referência do ponteiro.
Então, em vez de fazer estrela x entre parênteses e depois ponto y,
você pode apenas dizer x traço maior do que y.
Como x é um ponteiro, você não precisa referenciá-lo e deseja acessar o
elemento dentro dessa estrutura para onde quer que esteja apontando.
Então, você verá essa sintaxe em todo lugar. Você não verá
os parênteses e a referência d. É perfeitamente válido, mas você está digitando mais caracteres.
É muito mais eficiente dizer apenas x traço maior do que y.
Portanto, a instrução if anterior aqui em cima, em vez de des-referenciar dentro dos parênteses, tudo que você precisa fazer
é dizer se o traço do ponteiro da data é maior que o mês.
E é um tanto intuitivo porque o traço maior do que indica que está apontando para algo.
E assim o traço de data maior que o mês
E então o ponteiro de data é um ponteiro e é por isso que você usa o traço maior que.
Agora, se essa fosse uma estrutura aninhada, você
faria o traço maior do que a estrutura e então você teria
que fazer um ponto depois disso para acessar os elementos da estrutura aninhada.
Mas como o ponteiro do dia é um ponteiro, você deve usar o traço igual ou
o traço maior que.
Então aqui está um exemplo, estamos criando nossa data de estrutura,
temos três elementos, em seguida, estamos criando nosso ponteiro de data e estamos criando apenas
uma variável regular hoje. Atribuímos ao nosso ponteiro
o endereço de hoje e, em seguida, para atribuir os
elementos reais da estrutura, apenas usamos aquele traço maior que e então podemos imprimi-lo.
Então, vamos examinar isso em blocos de código para ver como funciona.
Vou colocar este exemplo aqui.
E, normalmente, quando você define estruturas, você
as define fora de uma função, porque então você pode usá-las em qualquer função.
É uma espécie de global. Mas lembre-se de
que ao definir a estrutura, você não está criando nenhuma memória.
Portanto, você ainda cria as variáveis como variáveis locais ou ponteiros, mas
a definição real está fora da função.
Portanto, se você quiser usá-lo, poderá usá-lo em qualquer função.
Então, aqui no topo, criamos uma nova estrutura do tipo data
ou nome até a data e ela contém três inteiros, terminamos com um ponto-e-vírgula.
Em seguida, criamos algumas variáveis dentro do principal.
Um que é apenas uma data de estrutura de variável normal
hoje e outro que é um ponteiro. Sabemos que é um ponteiro porque tem o asterisco antes dele.
Sabemos que um ponteiro, quando você cria um ponteiro, ele apenas cria aloca memória
para armazenar aquele endereço para aquele valor daquele ponteiro.
Portanto, teríamos que atribuir a ele um endereço que fazemos com o e comercial hoje,
porque isso realmente cria um relatório de memória.
Portanto, agora o ponteiro de data vai apontar para o que quer
que seja hoje com o endereço de hoje.
Podemos então começar a atribuir dados a ele usando este traço maior do que muito útil e
apenas atribuir os dados como qualquer outra pessoa, basta usar o operador de atribuição.
E então, quando o
imprimirmos, você perceberá que não precisamos desreferenciá-lo para imprimi-lo.
Para imprimi-lo, podemos
apenas dizer os tipos de dados para os especificadores de formato aqui, por cento mais alto,
por cento d mesmo para inteiros, e então usamos apenas o nivelador de traços
porque isso está realmente fazendo a des-referência, lembre-se.
Agora poderíamos mudar isso e isso seria válido.
Temos que colocar entre parênteses e então temos que dizer o mês do ponto.
Qualquer uma das formas está bem, mas acostume-se com o traço e depois com o maior porque é a melhor sintaxe.
E agora, quando construirmos isso,
você verá que ele imprime os valores dos ponteiros de
data e eles estão todos configurados para 9 25 e 2015.
Portanto, diz que a data de hoje é 9 25 15. Foi formatado para ser impresso dessa forma.
Coisas bem diretas.
Lembre-se de que o traço maior que, é uma
nova sintaxe para ponteiros para estruturas.
Mas tudo o mais aqui é normal.
Sabemos que os ponteiros precisam ter endereços de armazenamento, então
sabemos que temos que alocar memória dinâmica ou atribuir a ela uma estrutura existente.
Vamos voltar e falar mais sobre structs e ponteiros.
Agora vamos falar sobre estruturas que contêm ponteiros.
Sabemos como criar um ponteiro para uma estrutura.
Poderíamos alocar memória para esse ponteiro se não quisermos atribuir
um endereço existente de um ponteiro existente.
Mas você também pode ter elementos dentro de uma estrutura
que é um ponteiro em si. Portanto, um ponteiro também pode ser membro de uma estrutura.
Não precisamos usar esse array de caracteres.
Então poderíamos dizer struct,
poderíamos nomeá-lo em ponteiros e então poderíamos usar aqui neste exemplo, temos dois inteiros,
dois ponteiros inteiros p1 e p2, sintaxe perfeitamente válida para fazer isso.
Portanto, uma estrutura chamada em ponteiros é definida para conter dois ponteiros inteiros.
O primeiro é denominado p1 e o segundo é denominado p2.
Você pode definir uma variável do tipo struct em ponteiros, como
normal, struct, int pointers, ponteiros.
Os ponteiros de variáveis agora podem ser usados como outras estruturas.
Ponteiros não são ponteiros.
São ponteiros int. Ele contém dois elementos dentro de seu struck
que são ponteiros, mas os ponteiros em si não são um ponteiro real.
Portanto, preste atenção a isso. É uma variável de estrutura que tem dois ponteiros como
membros, isso é diferente.
Então, se quisermos acessar os elementos dentro da variável
de ponteiros que está em ponteiros, não
usamos o traço maior do que porque ponteiros não são ponteiros, é uma estrutura.
Usaríamos o ponto
para acessar p1 e acessar p2.
Portanto, neste exemplo, criamos nossa estrutura,
criamos uma variável. Não é um ponteiro chamado ponteiros.
E então criamos dois inteiros, i1
igual a 100 e i2 que não é inicializado.
Agora p1 e p2 são ponteiros.
Portanto, eles devem conter endereços de memória válidos onde os endereços
foram alocados na memória.
E é isso que está fazendo em i1 e i2.
Quando você declara as variáveis inteiras, a memória está sendo alocada para esses inteiros.
E assim, atribuindo os endereços i1 e i2 a p1 e p2, está perfeitamente correto porque
i1 e i2 têm memória alocada porque eles são inteiros.
Agora, se os ponteiros de inteiros não fossem ponteiros de inteiros, mas fossem ponteiros de
caracteres, você teria que alocar memória dinâmica.
Agora, estamos apenas usando inteiros, então atribuímos os endereços dos dois inteiros a p1 e p2.
Agora podemos dizer ponteiros de estrelas. p2 é igual a 97,
97 negativo.
Por que temos que des-referenciá-lo
lá porque ponteiros. p2 é o que estamos referenciando.
Ponteiros. pt p2 é um ponteiro.
Não estamos acessando um elemento de uma estrutura.
Estamos desreferenciando todas as dicas. p2.
E vamos atribuir a isso o valor de tudo o que está apontando para 97 negativo.
Portanto, observe a diferença aí.
Não estamos criando um ponteiro de estrutura.
Estamos criando um ponteiro de variável regular, mas
ele contém dois elementos que são ponteiros.
Então, se quisermos atribuir a esses dois elementos dentro da estrutura, temos que
desreferenciá-los usando o operador de indireção, e é isso
que fazemos lá.
E então em nossas duas declarações de impressão.
Novamente, queremos imprimir os valores para os quais p1 está apontando,
portanto, des-referenciamos o mesmo com p2
e, em seguida, queremos também imprimir
novamente os valores para os quais os ponteiros estão apontando.
Então, naquele primeiro, estamos dizendo que i1 é igual a
por cento I ponteiros de vírgula. p1 é igual a por
cento I.
Portanto, o primeiro dentro das aspas duplas é apenas imprimir o texto real,
não é remover a referência de nada, depois da vírgula é onde estamos fazendo a remoção da referência.
Primeiro imprimimos i1, que é 100, e depois
imprimimos os ponteiros p1, que seria 100.
Na verdade, I2 será
97 negativo depois de atribuí-lo a p2.
E quanto a matrizes de caracteres ou ponteiros de caracteres. Na aula anterior, falamos sobre como
você pode criar uma matriz de caracteres como um elemento
dentro de um ponteiro. Mas e quanto aos ponteiros de
caracteres, devemos fazer algo
como o primeiro e o último têm
matrizes de caracteres ou devemos apenas fazer algo como o primeiro e o
último com ponteiros de caracteres como elementos. Qual é a melhor opção.
Bem, veremos. É realmente novamente uma coisa notacional, mas há vantagens em usar os ponteiros.
Então, quando criamos ponteiros para personagens dentro de nossos elementos, geralmente
é um pouco melhor.
Mas você precisa entender o que está acontecendo aqui quando você
cria o elemento do array char array como um elemento ou um ponteiro como um elemento,
ambos são válidos. Mas há uma grande diferença.
Portanto, se dissermos nomes de struct veep
ou vp e dissermos Talia e Summers e, em seguida, diremos
nome do ponteiro de struct. Portanto, lembre-se de que os nomes das estruturas contêm as matrizes de caracteres.
Os nomes dos ponteiros de estrutura contêm os ponteiros.
E aqui estamos inicializando os ponteiros iguais a Brad e Fallingjaw.
E então nós os imprimimos vamos imprimir o primeiro elemento, que
é um caractere a e vp e então
o primeiro elemento de treas, que é um ponteiro para o primeiro.
Portanto, o struct nomeia a variável veep.
O que está acontecendo aqui é que as strings são armazenadas dentro da estrutura.
A estrutura alocou um total de 40 bytes para conter os dois nomes, eles
são matrizes de caracteres.
Isso é o que acontece quando criamos matrizes de caracteres dentro de um ponteiro.
Estamos alocando 40 bytes para conter esses dois
nomes, 20 e 20.
Para a variável de nomes de ponteiros, se temos dois ponteiros primeiro e último, essas são
strings que são armazenadas onde quer que o compilador armazene constantes de string.
A estrutura contém os dois
endereços porque são ponteiros. Os ponteiros contêm, quais, valores que são endereços.
Portanto, tudo o que vai fazer é alocar dois ponteiros dentro dessa estrutura.
Isso é 16 bytes para a maioria dos sistemas, ponteiros geralmente 8 bytes ou 4 bytes.
A estrutura de nome de struct p não aloca espaço para armazenar as
strings porque só aloca espaço para os próprios ponteiros.
Ponteiros têm valores que são endereços.
O endereço para o qual está apontando não há memória alocada para isso.
Portanto, quando você cria esses dois ponteiros, não há memória alocada para eles.
Ele não aloca espaço.
Portanto, ele pode ser usado apenas com cadeias de caracteres que tiveram espaço alocado para elas em outro lugar.
Sempre que houver ponteiros dentro de uma estrutura, você
deve usar malloc ou calloc.
Caso contrário, eles podem ser armazenados onde quer que constantes de string sejam armazenadas.
E essa é a grande diferença. Portanto, os ponteiros e a estrutura de
nomes p devem ser usados apenas para gerenciar strings que foram criadas e alocadas em outro lugar no programa.
Você não deseja atribuir
dados a isso, a menos que os tenha alocado dinamicamente ou que tenham
sido alocados em outro lugar, talvez
por uma estrutura existente ou algo assim. Mas, normalmente, você vai querer malhar essas dicas.
Novamente, muito diferente.
Você está alocando 40 bytes para essas duas matrizes de caracteres dentro da estrutura de nome.
Para a estrutura de nome p, você tem dois ponteiros.
Não há memória alocada para isso quando você cria uma variável desse tipo.
Ele está apenas criando memória para os ponteiros de dois caracteres, cada um com oito bytes.
Para realmente alocar memória para o que primeiro e o último estão apontando, você tem que fazer um malloc.
Portanto, uma instância em que faz sentido usar um ponteiro e uma estrutura
para manipular strings
é se você estiver alocando dinamicamente essa memória.
Geralmente é quando você o
usa, porque outros tipos de dados permitidos vão alocar automaticamente a memória para você.
Portanto, você deseja usar um ponteiro para armazenar o endereço.
A vantagem disso é que você pode pedir a malloc para alocar apenas a quantidade
de espaço necessária para uma string. Falamos sobre as vantagens da alocação dinâmica de memória.
Quando você cria coisas estaticamente em tempo de compilação, você
cria apenas um número específico de bytes.
O que acontece se você precisar de mais bytes, está ferrado, seu programa não vai funcionar.
Você tem que voltar e recompilar. Com strings alocadas dinamicamente, você
pode reajustar, você pode criar tanta memória quanto você precisa, o que o
torna muito mais eficiente no programa.
Portanto, você usará malloc em quaisquer ponteiros criados dentro de uma estrutura.
Portanto, neste caso, você tem uma estrutura chamada name count.
Você tem dois ponteiros, ponteiros de caractere fname e sobrenome.
E então você tem um int, cartas.
As duas strings aqui não são armazenadas na estrutura.
Nós apenas criamos memória para os ponteiros, que contêm endereços.
Queremos criar memória para fname e lname para o que os ponteiros estão apontando.
Então, queremos armazenar isso em um bloco gerenciado por localização de memória dinâmica, que está usando malloc.
O endereço das duas strings é o que está armazenado, não para o que elas estão apontando.
Portanto, endereços são com os quais as funções de manuseio de strings normalmente trabalham, o que
é bom e é por isso que você deseja usar ponteiros lá,
porque então você não precisa fazer isso, então você pode simplesmente distribuí-los com muito mais facilidade.
Então aqui está um exemplo. Temos uma função chamada get info.
Leva um ponteiro.
E podemos passar esse ponteiro facilmente.
Agora também vamos criar um outro ou
para nosso array de ponteiros que está
sendo passado para ele, vamos alocar
memória para ele.
Você verá que criamos um caractere chamado temporário
e armazenamos alguns dados nesse temporário.
Pedimos ao usuário para obter algo. Não estamos familiarizados com os sgets, mas é o mesmo que o scanf é salvo um pouco.
Em seguida, alocamos memória para fname dentro desse ponteiro e fazemos isso da mesma forma
que alocamos memória para qualquer caractere. Chamamos malloc, passamos
o número de bytes.
Nesse caso, o comprimento de str de 10 mais 1 para o terminador nulo.
E então lembre-se, malloc vira uma estrela vazia, então nós a transformamos em uma estrela de personagem.
Mas isso é fundamental aqui. Essa chamada malloc é necessária para qualquer estrutura que contenha ponteiros.
Agora, passamos esse ponteiro e o ponteiro nunca teve uma memória alocada
para ele.
Então, estamos alocando memória aqui. Podemos então fazer nossa cópia com muita facilidade.
Podemos copiar temp para fname de nossa estrutura pst que foi
passada como parâmetro.
E então podemos fazer com que eles insiram seu sobrenome, podemos alocar mais memória para
o sobrenome e então armazená-los na variável de estrutura do sobrenome.
Isso é o que você vai precisar fazer quando tiver ponteiros para personagens
dentro de suas estruturas, você tem que fazer o malloc.
Porque, novamente, esses ponteiros armazenam apenas o endereço.
Eles alocam memória suficiente para armazenar um endereço, mas não
memória suficiente para armazenar uma string.
Então, qualquer que seja o endereço que está apontando, você tem que alocar memória para
isso e você faz isso normalmente com o
malloc, a única diferença é aqui você usa o traço maior do que o
sinal para acessar o elemento real dentro dessa estrutura, que é um ponteiro de personagem, então você
pode atribuí-lo a ele e isso está fazendo a des-referência.
Então você vai querer brincar com essas coisas. Isso é muito comum
para ponteiros de caracteres dentro de structs.
Teremos um desafio em que você precisará fazer isso. Mas brinque com este código, vá
dentro dos blocos de código, comece a alocar memória para strings
ou ponteiros de caracteres dentro de structs e veja
como você pode usar str copy para realmente colocar dados lá, mas primeiro
você precisa alocar a memória antes de atribuir .
Se você está fazendo constantes como no exemplo anterior, quem sabe onde essa memória está alocada.
Então, novamente, muitos conceitos importantes apresentados nesta palestra.
Essa ideia de uma estrutura que é um ponteiro nesta
nova sintaxe onde
você pode usar o traço maior do que em vez de usar
o operador de indireção, muito legal.
E então ter elementos como ponteiros e como você
deve alocar memória para ponteiros de caracteres. Obrigada.