[Powered by Google Translate] [SEÇÃO 5: menos confortável]
[Nate Hardison, Harvard University]
[Esta é CS50.] [CS50.TV]
Então bem-vindo de volta, rapazes.
Bem-vindo à secção 5.
Neste ponto, tendo completado questionário 0 e tendo visto como você fez,
espero que você se sentir realmente bem, porque eu estava muito impressionado com a pontuação nesta seção.
Para os nossos espectadores on-line, nós tivemos um par de perguntas
sobre os dois últimos problemas no conjunto de problemas - ou no questionário, em vez.
Então, nós estamos indo ir sobre os muito rapidamente, de modo que toda a gente vê o que aconteceu
e como ir através da solução real e não apenas visualizar a solução em si.
Nós vamos passar por cima o último par de problemas muito rapidamente, 32 e 33.
Assim, mais uma vez, para que os espectadores online pode ver isso.
>> Se você ligar para o seu problema 32, que está na página 13,
13 de 16, problema 32 é tudo sobre swaps.
Era tudo sobre a troca de dois inteiros.
É o problema que tinha ido ao longo de um par de vezes em palestra.
E aqui, o que estávamos pedindo para você fazer é um traço de memória rápida.
Para preencher os valores das variáveis ​​que estão na pilha
como o código passa por esta função swap.
Em particular, o que nós estamos olhando - eu vou colocar esse iPad para baixo -
em particular, o que estamos vendo é esta linha numerada 6 aqui.
E é numerada 6 por apenas contiguidade com o problema anterior.
O que queremos fazer é mostrar ou rotular o estado da memória
como é na altura em que se executa este número da linha 6,
que é efetivamente um retorno de nossa função swap aqui.
Se rolar aqui, vimos que os endereços de tudo na memória foi fornecido por nós.
Isso é muito fundamental, nós vamos voltar a ele em apenas um momento.
E então, aqui em baixo, na parte inferior, tivemos um diagrama de pouca memória que vamos referir.
Eu realmente fiz isso no meu iPad.
Então, eu estou indo para alternar rapidamente entre o iPad e este código apenas para referência.
>> Vamos começar. Primeiro, vamos nos concentrar no primeiro par de linhas de principal aqui.
Para começar, vamos inicializar x e y a 1 para 2.
Portanto, temos duas variáveis ​​inteiras, ambos estão indo para ser colocado na pilha.
Nós vamos colocar um 1 e um 2 neles.
Então, se eu virar para o meu iPad, esperamos, vamos ver -
A Apple espelhamento de TV, e lá vamos nós. Okay.
Então, se eu virar para o meu iPad,
Eu quero inicializar x e y a 1 para 2.
Fazemos isso simplesmente escrevendo um 1 na caixa marcada x
e um 2 na caixa marcada y. Bastante simples.
Então agora vamos voltar para o laptop, ver o que acontece.
Então esta linha seguinte é onde as coisas ficam complicadas.
Nós passar o endereço de x e y o endereço de como os parâmetros a e b para a função de troca.
O endereço de x e o endereço de y são coisas que não podemos calcular
sem se referir a estes bala aponta para a direita aqui.
E, felizmente, os dois primeiros pontos de bala nos dizer exatamente quais são as respostas.
O endereço de memória de x é 10, e o endereço na memória de y é 14.
Então esses são os valores que são passados ​​como a e b em cima da nossa função swap.
Então, novamente, retornar para o nosso diagrama, eu posso escrever um 10 em um
e 14 um em b.
Agora, este ponto é onde nós prosseguir com a troca.
Então, lançando de volta para o laptop de novo,
vemos que a forma como o swap funciona é que eu dereference primeiro uma loja e o resultado em tmp.
Assim, o operador dereference diz, "Hey. Tratar o conteúdo de uma variável como um endereço.
Vá para o que está armazenado naquele endereço, e carregá-lo. "
O que você carrega para fora da variável que vai ser armazenado em nossa variável tmp.
Lançando de volta para o iPad.
Se formos para tratar 10, sabemos que é o endereço de 10 x varible
porque foi dito pelo nosso ponto de bala que o endereço de x em memória é 10.
Então, nós podemos ir lá, pegar o valor do mesmo, o que é um, como podemos ver em nosso iPad,
e carregar isso em tmp.
Novamente, isto não é o conteúdo final.
Nós vamos caminhar e nós vamos chegar ao nosso estado final do programa no final.
Mas, agora, temos o valor armazenado em um tmp.
>> E há uma pergunta rápida aqui.
[Alexander] é o operador dereference - que é apenas o direito de estrela na frente da variável?
Sim >>. Assim, o operador dereference, como virar de volta para o nosso laptop, mais uma vez,
é esta estrela em frente.
Nesse sentido, ele é - você compará-lo com o operador de multiplicação
que requer duas coisas: o operador dereference é um operador unário.
Apenas aplicada a um valor, por oposição a um operador binário,
onde você aplica a dois valores diferentes.
Então, isso é o que acontece nesta linha.
Nós carregamos o valor 1 e é armazenado em nosso variável temporária inteiro.
A próxima linha, armazenamos o conteúdo de b em -
ou, em vez disso, armazenar o conteúdo que b está apontando para o lugar onde um está apontando.
Se analisarmos este da direita para a esquerda, vamos b dereference,
vamos abordar 14, vamos pegar o número inteiro que está lá,
e então nós estamos indo para ir para o endereço 10,
e vamos jogar o resultado da nossa dereference de b em que o espaço.
Lançando de volta ao nosso iPad, onde podemos fazer isso um pouco mais concreto,
que poderia ajudar se eu escrever números em todos os endereços aqui.
Então, nós sabemos que, y, estamos no endereço 14, x está no endereço 10.
Quando começamos a b, b dereference, vamos pegar o valor 2.
Nós vamos pegar esse valor porque esse é o valor que vive no endereço 14.
E nós vamos colocá-lo na variável que vive no endereço 10,
que está ali, correspondente à nossa variável x.
Assim, podemos fazer um pouco de sobrescrever aqui
onde vamos nos livrar de nosso 1 e em vez disso, escreva uma 2.
Então está tudo bem e bom no mundo, mesmo que tenhamos x sobrescritos agora.
Temos armazenados valor antigo de x em nossa variável tmp.
Assim, podemos completar a troca com a linha seguinte.
Lançando de volta para o nosso laptop.
Agora tudo o que resta é levar o conteúdo de nossa variável temporária inteiro
e armazená-los na variável que vive no endereço que b está segurando.
Então, nós vamos efetivamente b dereference para obter acesso à variável
isto é, no endereço que b tem nele,
e nós vamos encher o valor que tmp está segurando para ele.
Lançando de volta para o iPad mais uma vez.
Eu posso apagar este valor aqui, 2,
e em vez disso vamos copiar o direito um para ele.
Em seguida, a próxima linha que executa, é claro -
se jogarmos de volta para o laptop - é este o ponto 6,
que é o ponto em que nós queríamos ter o nosso diagrama completamente preenchido.
Então, lançando de volta para o iPad, uma vez mais, só assim você pode ver o diagrama completo,
você pode ver que temos um 10 em um, 14 em um b, a 1 em tmp, um 2 em x, e um 1 em y.
Há alguma dúvida sobre isso?
Será que isto faz mais sentido, tendo orientado por ela?
Fazer menos sentido? Esperemos que não. Okay.
>> Os ponteiros são um assunto muito complicado.
Um dos caras que trabalham com tem um ditado muito comum:
"Para entender os ponteiros, você deve primeiro entender os ponteiros".
Que eu acho que é muito verdadeiro. Ele faz demorar um pouco para se acostumar com isso.
Sorteio de fotos, sorteio de diagramas de memória como esta são muito útil,
e depois de andar por exemplo após exemplo após exemplo,
ele vai começar a fazer sentido um pouco mais e sentido um pouco mais e um pouco mais de sentido.
Finalmente, um dia, você vai ter tudo completamente dominado.
Quaisquer dúvidas, antes de passar para o próximo problema? Tudo bem.
Então virar para trás para o laptop.
O próximo problema que temos é o problema número 33 no arquivo de I / O.
Zoom sobre isso um pouco.
Problema 33 - Sim?
>> [Daniel] Eu só tinha uma pergunta rápida. Esta estrela, ou o asterisco,
ele é chamado dereferencing quando você usa um asterisco antes.
Como se chama quando você usa o comercial antes?
>> O comercial antes é o endereço do operador.
Então, vamos rolar de volta.
Oops. Eu estou no modo de zoom, então não posso realmente rolagem.
Se olharmos para esse código realmente rápido aqui,
novamente, a mesma coisa acontecendo.
Se olharmos para este código aqui, nesta linha onde fazemos a chamada para trocar,
o comercial é apenas dizer "obter o endereço em que vive a variável x."
Quando o compilador compila seu código,
tem que fisicamente marcar um lugar na memória de todos os seus variáveis ​​para viver.
E assim o que o compilador pode então fazer uma vez que é compilado tudo,
ele sabe, "Oh, eu coloquei no endereço x 10. coloquei y no endereço 14."
Ele pode, então, preencher estes valores para você.
Assim, você pode então - ele pode passar este e passe & y no bem.
Esses caras obter o endereço, mas também, quando você passá-los para a função swap,
este tipo de informação, este int * aqui, diz o compilador,
"Ok, nós vamos estar interpretando esse endereço como um endereço de uma variável inteira."
Como um endereço de um inteiro, que é diferente do endereço de uma variável de caracteres
porque um int toma-se, em uma máquina de 32 bits, ocupa 4 bytes de espaço,
enquanto um único personagem ocupa 1 byte de espaço.
Por isso, é importante saber também o que é - o que vive, que tipo de valor
está a viver no endereço que foi passado dentro
Ou o endereço que você está lidando.
Dessa forma, você sabe quantos bytes de informações para realmente carregar de sua memória RAM.
E então, sim, este operador dereference, como se estivesse perguntando:
vai e acessa informações em um endereço particular.
Por isso, diz, com esta variável um aqui, tratar o conteúdo de um como endereço,
ir para esse endereço, e puxar, carregar no processador, a carga em um registrador
os valores reais ou os conteúdos que vivem naquele endereço.
Mais alguma pergunta? Essas são boas perguntas.
É um monte de nova terminologia também.
É também uma espécie de funk, vendo & * e em lugares diferentes.
>> Tudo bem.
Então, de volta para o problema de 33, arquivo I / O.
Este foi um daqueles problemas que eu acho que um par de coisas aconteceu.
Um, é um tema relativamente novo.
Ele foi apresentado logo antes do teste,
e então eu acho que foi mais ou menos como um daqueles problemas de palavras em matemática
onde eles dão-lhe um monte de informações, mas você realmente não acabar tendo que usar uma tonelada do mesmo.
A primeira parte deste problema está descrevendo o que é um arquivo CSV.
Agora, um arquivo CSV, de acordo com a descrição, é um arquivo separado por vírgulas valores.
A razão, são em tudo interessante, ea razão pela qual você nunca usá-los,
é, pois, como muitos de vocês já usou o material como o Excel?
Figura maioria de vocês, provavelmente, ou vai usar em algum momento de sua vida.
Você vai usar algo como o Excel.
A fim de obter os dados de uma planilha do Excel ou fazer qualquer tipo de tratamento com ele,
se você queria escrever um programa em C ou Python programa, o programa Java,
para lidar com os dados armazenados lá,
uma das formas mais comuns para tirá-lo está em um arquivo CSV.
E você pode abrir o Excel e quando você vai para a 'Salvar como' o diálogo,
você pode sair um arquivo CSV real.
>> Útil para saber como lidar com essas coisas.
O modo como funciona é que ele é semelhante a - Quero dizer, ele é essencialmente imitando uma planilha,
onde, como vemos aqui, na peça muito mais à esquerda,
temos todos os últimos nomes.
Assim, temos Malan, então Hardison, e, em seguida, Bowden, MacWilliam, e, em seguida, Chan.
Todos os sobrenomes. E então uma vírgula separa os sobrenomes dos primeiros nomes.
David, Nate, Rob, Tommy, e Zamyla.
Eu sempre misturar Robby e Tom.
E, em seguida, finalmente, a terceira coluna é os endereços de e-mail.
Depois de entender isso, o resto do programa é bastante simples de implementar.
O que temos feito, a fim de imitar essa mesma estrutura em nosso programa C
é que usei uma estrutura.
Vamos começar a jogar com estes mais um pouco também.
Vimo-los para o primeiro bit pouco no conjunto de problemas 3, quando estávamos lidando com os dicionários.
Mas essa estrutura pessoal armazena um último nome, um primeiro nome e um e-mail.
Assim como o nosso arquivo CSV foi guardar.
Portanto, esta é apenas a conversão de um formato para outro.
Temos de converter, neste caso, uma estrutura pessoal para uma linha,
uma linha separada por vírgulas, apenas como aquele.
Isso faz sentido? Vocês têm todos levados ao questionário,
então eu imagino que você tenha pelo menos teve algum tempo para pensar sobre isso.
>> Na função de aluguer, o problema nos pede para tomar - zoom em we'll sobre isso um pouco -
tomar em uma estrutura de pessoal, uma estrutura de pessoal, com o nome de s,
e anexar o seu conteúdo para o nosso arquivo staff.csv.
Acontece que isso é bastante simples de usar.
Vamos tipo de brincar com essas funções hoje um pouco mais.
Mas, neste caso, a função fprintf é realmente a chave.
Assim, com fprintf, podemos imprimir, assim como vocês têm vindo a utilizar este termo printf todo.
Você pode printf uma linha de um arquivo.
Então, em vez de apenas fazer a chamada de costume printf onde você dá a ele a cadeia de formato
e depois de substituir todas as variáveis ​​com os seguintes argumentos,
com fprintf, seu argumento é muito primeira vez o arquivo que você deseja gravar.
Se estivéssemos a olhar para isso no aparelho, por exemplo, o homem fprintf,
podemos ver a diferença entre o printf e fprintf.
Eu vou ampliar um pouco aqui.
Assim, com printf, nós dar-lhe uma seqüência de formato, e depois os argumentos subseqüentes
são todas as variáveis ​​para reposição ou substituição em nossa cadeia de formato.
Considerando que, com fprintf, o primeiro argumento é de fato esse * arquivo chamado um córrego.
>> Voltando aqui para a nossa contratação,
nós já temos o nosso fluxo * arquivo aberto para nós.
Isso é o que faz esta primeira linha, que abre o arquivo staff.csv,
ele abre em modo incremental, e tudo o que resta para nós é
escrever a estrutura de pessoal para o arquivo.
E, vamos ver, eu quero usar o iPad?
Vou usar o iPad. Temos vazio - vamos colocar isso na mesa para que eu possa escrever um pouco melhor -
anular a contratação e que leva, em um argumento, uma estrutura de equipe chamado s.
Temos os nossos aparelhos, temos a nossa * arquivo chamado arquivo,
nós temos nossa linha fopen dada a nós,
e eu vou escrevê-lo como pontos, uma vez que já está no pedia.
E então, em nossa próxima linha, vamos fazer uma chamada para fprintf
e vamos passar o arquivo que deseja imprimir,
e então nossa cadeia de formato, que -
Eu vou deixar vocês me dizer o que parece.
E você, Stella? Você sabe o que a primeira parte da cadeia de formato parece?
[Stella] Eu não tenho certeza. Sinta-se livre para >> pedir Jimmy.
Sabe, Jimmy?
[Jimmy] Seria apenas ser o último? Eu não sei. Eu não estou totalmente certo.
Ok >>. Que tal, será que alguém receber este correta no exame?
Não. Tudo bem.
Acontece que aqui tudo o que temos a fazer é que queremos cada parte de nosso quadro de pessoal
para ser impresso como uma string em nosso arquivo.
Nós só usar o personagem substituição corda três vezes diferentes, porque temos um sobrenome
seguido por vírgula, um primeiro nome seguido de uma vírgula,
e, finalmente, o endereço de e-mail que é seguido - o que não é
encaixando na minha tela - mas é seguido por um caractere de nova linha.
Então eu vou escrever isso só lá em baixo.
E então, após a nossa seqüência de formato,
só temos as substituições, que temos acesso usando a notação de ponto
que vimos no conjunto de problemas 3.
Podemos usar s.last, s.first e s.email
para substituir nos três valores em nossa cadeia de formato.
Então, como foi isso? Faz sentido?
Sim? Não? Possivelmente? Okay.
>> A última coisa que fazemos depois que nós impresso e depois abrimos o nosso arquivo:
sempre que você abriu um arquivo, temos sempre que lembrar de fechá-lo.
Porque, do contrário vamos acabar vazando a memória,
utilizando-se descritores de arquivos.
Então, para fechá-lo, qual a função que vamos usar? Daniel?
[Daniel] fclose? >> Fclose, exatamente.
Assim, a última parte deste problema era fechar corretamente o arquivo, usando a função fclose,
que apenas se parece com isso.
Não é muito louco.
Cool.
Então, isso é problema de 33 no questionário.
Nós vamos ter de arquivo definitivamente mais I / O chegando.
Vamos fazer um pouco mais em palestra hoje, ou na seção de hoje,
porque é isso que vai formar a maior parte deste pset próximo.
Vamos seguir em frente a partir do questionário neste momento. Sim?
>> [Charlotte]] Por que fclose (arquivo) em vez de fclose (staff.csv)?
Ah >>. Porque acontece que - para a questão, que é um grande,
é por isso que, quando escrevemos fclose, estamos escrevendo fclose estrela variável (arquivo)
em oposição ao nome do arquivo, staff.csv? Isso está correto? Sim.
Então, vamos dar uma olhada. Se eu voltar para o meu laptop,
e vamos olhar para a função fclose.
Assim, a função fclose fecha um córrego e leva no ponteiro para o fluxo que deseja fechar
em oposição ao nome real do arquivo que deseja fechar.
E isto porque, nos bastidores, quando você faz uma chamada para fopen,
quando você abrir um arquivo, você está, na verdade, a alocação de memória para armazenar informações sobre o arquivo.
Então você tem um ponteiro de arquivo que contém informações sobre o arquivo,
como ele é aberto, seu tamanho, onde está atualmente no arquivo,
de modo que você pode fazer chamadas de leitura e escrita para aquele lugar especial dentro do arquivo.
Você acaba de fechar o ponteiro em vez de fechar o nome do arquivo.
>> Sim? [Daniel] Portanto, a fim de usar de aluguer, você diria que - como se obter a entrada do usuário?
O fprintf agir como GetString no sentido de que ele só vai esperar a entrada do usuário
e pedir-lhe para escrever isso - ou esperar para você digitar essas três coisas em?
Ou você precisa de usar algo para implementar aluguer?
Sim >>. Então, nós não somos - a questão foi, como é que vamos chegar a entrada do usuário
a fim de implementar aluguer? E o que temos aqui é o chamador de aluguer,
passaram neste struct pessoal com todos os dados armazenados na estrutura já.
Então fprintf é capaz de escrever apenas que os dados diretamente para o arquivo.
Não há espera para a entrada do usuário.
O usuário já está dada a entrada corretamente por colocá-lo nessa estrutura de pessoal.
E as coisas, é claro, iria quebrar se algum desses ponteiros foram nulos,
para que rolar para trás até aqui e olhamos para a nossa estrutura.
Temos string string ultimo primeiro, e-mail string.
Agora sabemos que todos aqueles realmente, sob o capô, são variáveis ​​char *.
Isso pode ou não pode estar apontando para nulo.
Eles podem estar apontando para a memória na pilha,
talvez memória na pilha.
Nós realmente não sei, mas se qualquer um desses ponteiros são nulos ou inválidos,
que isso definitivamente vai falhar a nossa função de aluguer.
Isso era algo que estava meio fora do escopo do exame.
Nós não está se preocupando com isso.
Grande. Okay. Então passar do quiz.
>> Vamos fechar esse cara, e nós estamos indo olhar pset 4.
Então, se vocês olhar para a especificação pset, uma vez que você pode acessá-lo, cs50.net/quizzes,
vamos passar por alguns dos problemas de hoje da seção.
Estou a rolagem para baixo - seção de perguntas começa na terceira página da especificação pset.
E a primeira parte pede-lhe para ir e assistir o curta em redirecionando e canos.
Que era uma espécie de curto legal, mostra-lhe alguns truques novos, frescos comando de linha que você pode usar.
E então nós temos algumas perguntas para você também.
Esta primeira pergunta sobre fluxos, para que printf escreve por padrão,
que tipo de tocada em apenas um pouco de um momento atrás.
Este fprintf que nós estávamos discutindo leva em um fluxo de arquivo * como seu argumento.
fclose leva em um fluxo de arquivo *, bem como,
eo valor de retorno de fopen lhe dá um fluxo de arquivo * também.
A razão que nós não vimos antes, quando aqueles que lidou com printf
é porque printf tem um fluxo de padrão.
E o fluxo padrão para o qual ele escreve
você vai descobrir mais sobre a curto.
Então, definitivamente, dar uma olhada.
>> Na seção de hoje, vamos falar um pouco sobre o GDB,
pois quanto mais você está familiarizado com ele, mais prática você começa com ele,
o mais capaz você será realmente descobrir os erros no seu próprio código.
Isso acelera o processo de depuração-se tremendamente.
Então, usando printf, cada vez que você faz que você tem que recompilar seu código,
você tem que executá-lo novamente, às vezes você tem que mover a chamada printf redor,
comente o código, ele só leva um tempo.
Nosso objetivo é tentar convencê-lo de que, com o GDB, você pode essencialmente
nada printf em qualquer ponto do seu código e você nunca tem que recompilá-lo.
Você nunca tem que iniciar e manter adivinhar onde printf seguinte.
A primeira coisa a fazer é copiar essa linha e obter o código da seção fora da web.
Estou copiando esta linha de código que diz: "http://cdn.cs50.net wget".
Eu estou indo para copiá-lo.
Eu estou indo para ir para o meu aparelho, zoom out para que você possa ver o que eu estou fazendo,
colá-lo lá, e quando eu aperte Enter, esse comando wget é literalmente uma teia obter.
Vai puxar para baixo este arquivo fora da Internet,
e ele vai salvá-lo para o diretório atual.
Agora, se eu listar meu diretório atual, você pode ver que eu tenho esse arquivo section5.zip bem ali.
A maneira de lidar com esse cara é descompactá-lo,
que você pode fazer na linha de comando, assim como este.
Section5.zip.
Isso vai descompactá-lo, criar a pasta para mim,
inflar todo o conteúdo, colocá-los lá dentro.
Então agora eu posso ir para o meu diretório seção 5 usando o comando cd.
Limpar a tela usando claro. Então, limpar a tela.
Agora eu tenho um terminal limpo agradável de lidar.
>> Agora, se eu listar todos os arquivos que eu vejo neste diretório,
você vê que eu tenho quatro arquivos: buggy1, buggy2, buggy3 e buggy4.
Eu também tenho os arquivos correspondentes. C.
Nós não vamos olhar para os arquivos. C por agora.
Em vez disso, vamos usá-los quando abrimos GDB.
Mantivemos-los em torno de modo que temos acesso ao código fonte real quando estamos usando GDB,
mas o objetivo desta parte da seção é de mexer volta com o GDB
e ver como podemos usá-lo para descobrir o que está acontecendo de errado com cada um dos quatro programas de buggy.
Então, estamos apenas indo ao redor da sala muito rapidamente,
e eu vou pedir a alguém para executar um dos programas de buggy,
e depois vamos em grupo através de GDB, e vamos ver o que podemos fazer para corrigir esses programas,
ou, pelo menos, identificar o que está acontecendo de errado em cada um deles.
Vamos começar aqui com Daniel. Você vai correr buggy1? Vamos ver o que acontece.
[Daniel] Ele diz que há uma falha de aplicação. Sim >>. Exatamente.
Então, se eu executar buggy1, eu recebo uma falha de seg.
Neste ponto, eu poderia ir e abrir buggy1.c,
tentar descobrir o que está acontecendo de errado,
mas uma das coisas mais irritantes sobre este erro de falha de seg
é que ele não diz em que linha das coisas programa realmente deu errado e quebrou.
Você meio que tem que olhar para o código
e descobrir usando palpite e marque ou printf para ver o que está acontecendo de errado.
Uma das coisas mais legais sobre o GDB é que ele é muito, muito fácil
para descobrir a linha em que trava o programa.
É totalmente vale a pena usá-lo, mesmo que apenas por isso.
Então, para arrancar GDB, eu digito GDB, e então eu dar-lhe o caminho para o executável que eu quero correr.
Aqui eu estou escrevendo gdb ./buggy1.
Pressione Enter. Me dá toda essa informação de direitos autorais,
e aqui você vai ver esta linha que diz: "a leitura de símbolos / home /
jharvard/section5/buggy1 ".
E, se tudo correr bem, você vai ver que imprimir uma mensagem parecida com esta.
Ele vai ler símbolos, ele vai dizer: "Eu estou lendo símbolos de seu arquivo executável",
e então ele vai ter esse "feito" mensagem aqui.
Se você ver alguma outra variação deste, ou você ver que não conseguia encontrar os símbolos
ou algo assim, o que isso significa é que você simplesmente não ter compilado o executável corretamente.
Quando compilar programas para uso com o GDB, temos que usar a bandeira especial-g,
e isso é feito por padrão, se você compilar seus programas, apenas digitando fazer
ou fazer buggy ou fazer recuperar, qualquer um desses.
Mas se você está compilando manualmente com Clang, então você vai ter que ir e que incluem flag-g.
>> Neste ponto, agora que temos o nosso GDB prompt,
é muito simples de executar o programa.
Nós pode digitar run, ou podemos simplesmente digitar r.
A maioria dos comandos do GDB pode ser abreviado.
Normalmente, para apenas um ou dois cartas, o que é bastante agradável.
Então Saad, se você digitar r e pressione Enter, o que acontece?
[Saad] Eu tenho SIGSEGV, falha de segmentação, e então todo este jargão.
Sim >>.
Como estamos vendo na tela, agora, e como Saad disse,
quando digitamos corrida ou R e aperte Enter, nós ainda recebemos a falha seg mesmo.
Então, usando o GDB não resolve o nosso problema.
Mas dá-nos alguns gobbledygook, e verifica-se que este gobbledygook
na verdade, diz-nos onde ele está acontecendo.
Para analisar isso um pouco, este primeiro bit é a função em que tudo está acontecendo de errado.
Há essa __ strcmp_sse4_2, e diz-nos que está acontecendo neste arquivo
chamado sysdeps/i386, tudo isso, de novo, uma espécie de confusão - mas a linha 254.
Isso é meio difícil de analisar. Normalmente, quando você vê coisas como esta,
o que significa que ele está seg falha de uma das bibliotecas de sistema.
Então, alguma coisa a ver com strcmp. Vocês têm visto strcmp antes.
Não é muito louco, mas isso significa que strcmp está quebrado ou que há um problema com strcmp?
O que você acha, Alexandre?
[Alexander] É que - é de 254 a linha? E o - não binário, mas não é os seus tectos,
e depois há uma outra língua para cada função. É que 254 em que a função, ou -?
É >> linha 254. Parece que neste arquivo s., Por isso, provavelmente código de montagem.
>> Mas, eu acho que a coisa mais urgente é, porque nós temos tido uma falha de seg,
e parece que ele está vindo a função strcmp,
isso implica, então, que strcmp está quebrado?
Não deveria, espero. Então, só porque você tem uma falha de segmentação
em uma das funções do sistema, normalmente isso significa que você simplesmente não tê-lo chamado corretamente.
O mais rápido coisa a fazer para descobrir o que está realmente acontecendo
quando você vê algo louco como esse, sempre que você vê uma falha seg,
especialmente se você tem um programa que está usando mais do que apenas principal,
é a utilização de um registo de chamadas.
I abreviar backtrace escrevendo bt, em oposição à palavra backtrace completo.
Mas Charlotte, o que acontece quando você digita bt e pressione Enter?
[Charlotte] Ele me mostra duas linhas, a linha 0 e linha 1.
Sim >>. Assim linha 0 e linha 1.
Estes são os quadros de pilha reais que estavam atualmente no jogo quando o programa deixou de funcionar.
A partir do quadro superior, quadro 0, e indo para o fundo mais, que é uma armação.
Nosso quadro de nível superior é o quadro strcmp.
Você pode pensar nisso como semelhante ao problema que só estavam fazendo no questionário com os ponteiros,
onde tínhamos trocar quadro de pilha em cima de quadro de pilha principal,
e tivemos as variáveis ​​que troca estava usando em cima das variáveis ​​que principal estava usando.
Aqui a nossa queda aconteceu em nossa função strcmp, que foi chamado pela nossa função principal,
backtrace e está nos dando não apenas as funções em que as coisas falharam,
mas também está nos dizendo que tudo foi chamado.
Então, se eu rolar um pouco mais para a direita,
podemos ver que, sim, nós estávamos na linha 254 do arquivo strcmp-sse4.s.
Mas a chamada foi feita em buggy1.c, linha 6.
Então isso significa que podemos fazer - é que podemos apenas ir verificar e ver o que estava acontecendo
em buggy1.c, linha 6.
Novamente, existem algumas maneiras de fazer isso. Um deles é para sair do GDB
ou ter seu código aberto em outra janela e referência cruzada.
Isso, por si só, é bastante útil, porque agora se você está em horário de expediente
e você tem uma falha seg e seu TF está se perguntando onde tudo estava quebrando,
você pode apenas dizer: "Oh, linha 6. que eu não sei o que está acontecendo,
mas algo sobre a linha 6 está causando o meu programa para quebrar ".
A outra maneira de fazer isso é que você pode usar este comando chamada lista em GDB.
Você também pode abreviar com l.
Então, se nós batemos l, o que temos aqui?
Nós temos um monte de coisas estranhas.
Este é o código de montagem real
que está em strcmp_sse4_2.
Isso parece tipo de funky,
ea razão que estamos recebendo é porque agora,
GDB nos tem em quadro 0.
>> Então, quando nós olhamos para as variáveis, toda vez que olhar para o código-fonte,
nós estamos olhando para o código-fonte que pertence ao quadro de pilha que estamos atualmente dentro
Portanto, a fim de obter alguma coisa significativa, temos que
mover-se para um quadro de pilha que faz mais sentido.
Neste caso, o quadro de pilha principal faria sentido um pouco mais,
porque esse era realmente o código que escreveu.
Não o código strcmp.
A maneira como você pode mover-se entre os quadros, neste caso, porque temos dois,
temos 0 e 1,
você faz isso com o para cima e para baixo comandos.
Se eu subir um quadro,
agora eu estou no quadro de pilha principal.
Eu posso mover para baixo para voltar para onde eu estava,
ir para cima de novo, ir para baixo de novo, e ir para cima de novo.
Se você sempre faz o programa no GDB, você tem uma queda, você começa a backtrace,
e você vê que ele está em algum arquivo que você não sabe o que está acontecendo.
Você tenta lista, o código não parece familiar para você,
dar uma olhada em seus quadros e descobrir onde você está.
Você provavelmente está no quadro de pilha errada.
Ou, pelo menos você está em um quadro de pilha que não é aquele que você pode realmente depurar.
Agora que estamos no quadro de pilha adequada, estamos em principal,
agora podemos usar o comando lista para descobrir o que a linha estava.
E você pode vê-lo, imprimi-lo para nós aqui.
Mas podemos acertar listar todos o mesmo, e lista nos dá esta impressão agradável
do código-fonte real que está acontecendo aqui.
>> Em particular, podemos olhar para a linha 6.
Nós podemos ver o que está acontecendo aqui.
E parece que nós estamos fazendo uma comparação de string
entre a string "CS50 pedras" e argv [1].
Alguma coisa sobre isso foi bater.
Então Missy, você tem alguma opinião sobre o que pode estar acontecendo aqui?
[Missy] Eu não sei por que ele está falhando. >> Você não sabe por que está batendo?
Jimmy, todos os pensamentos?
[Jimmy] eu não estou totalmente certo, mas a última vez que usou cadeia de comparar,
ou strcmp, tivemos algo como três casos diferentes sob ele.
Nós não temos um ==, eu não acho que, mesmo em que a primeira linha.
Em vez disso, foi separada em três, e uma era == 0,
um foi <0, eu acho, e foi um> 0.
Então talvez algo assim? Sim >>. Então há essa questão
de que estamos fazendo a comparação corretamente?
Stella? Todos os pensamentos?
[Stella] Eu não tenho certeza. >> Não tenho certeza. Daniel? Pensamentos? Okay.
Acontece que o que está acontecendo aqui é quando executamos o programa
e temos a culpa seg, quando o programa foi executado pela primeira vez, Daniel,
você deu a ela quaisquer argumentos de linha de comando?
[Daniel] Não. Não. >> Nesse caso, o que é o valor de argv [1]?
>> Não há nenhum valor. >> Direito.
Bem, não há valor de seqüência apropriada.
Mas há algum valor. Qual é o valor que fica armazenado ali?
Um valor >> lixo? >> É tanto um valor de lixo, ou, neste caso,
o final da matriz argv sempre é terminada com null.
Então, o que realmente foi armazenado lá é nulo.
A outra maneira de resolver isso, em vez de pensar nisso,
é tentar mostrá-lo.
Este é o lugar onde eu estava dizendo que o uso de GDB é grande,
porque você pode imprimir todas as variáveis, todos os valores que você deseja
usando este prático-dandy comando p.
Então, se eu digitar p e depois digite o valor de uma variável ou o nome de uma variável,
dizer, argc, vejo que argc é 1.
Se eu quiser imprimir argv [0], eu posso fazê-lo assim.
E, como vimos, argv [0] é sempre o nome do seu programa,
sempre o nome do executável.
Aqui você vê que tem o nome completo do caminho.
Eu também pode imprimir argv [1] e ver o que acontece.
>> Aqui temos este tipo de valor místico.
Temos este 0x0.
Lembre-se, no início do mandato, quando nós falamos sobre números hexadecimais?
Ou que pequena pergunta ao final de pset 0 sobre como representar 50 em hexadecimal?
A nossa forma de escrever números hexadecimais em CS, apenas para não confundir-nos
com números decimais, é sempre prefixo-los com 0x.
Portanto, este prefixo 0x sempre significa apenas interpretar o seguinte número como um número hexadecimal,
não como uma string, não como um número decimal, não como um número binário.
Uma vez que o número de 5-0 é um número válido em hexadecimal.
E é um número decimal, 50.
Portanto, este é apenas como nós disambiguate.
Então 0x0 meios 0 hexadecimal, que também é decimal, binário 0 0.
É apenas o valor 0.
Acontece que isso é o que nulo é, na verdade, na memória.
Nulo é apenas 0.
Aqui, o elemento armazenado em argv [1] é nulo.
Então, estamos tentando comparar o nosso "CS50 rochas" string para uma string nula.
Então dereferencing nulo, tentando acessar coisas ao nulo,
aqueles são normalmente vai causar algum tipo de falha de segmentação ou de outras coisas ruins aconteçam.
E acontece que strcmp não verifique
ou não que você passou em um valor que é nulo.
Em vez disso, ele simplesmente vai em frente, tenta fazer a sua coisa,
e se seg defeitos, ela seg falhas, e é o seu problema. Você tem que ir consertar.
Muito rapidamente, como podemos resolver este problema? Charlotte?
[Charlotte] Você pode verificar se usando.
Então, se argv [1] é nulo, == 0, em seguida, retornar 1, ou alguma coisa [ininteligível].
Sim >>. Então essa é uma ótima maneira de fazê-lo, como se pode verificar,
o valor que estamos prestes a passar para o strcmp, argv [1], não é nulo?
Se é nulo, então podemos dizer bem, abortar.
>> A maneira mais comum de fazer isso é usar o valor argc.
Você pode ver aqui no início do principal,
omitimos que primeiro teste que nós normalmente fazemos quando usar argumentos de linha de comando,
que é para testar se ou não o nosso valor argc é o que esperamos.
Neste caso, estamos esperando pelo menos dois argumentos,
o nome do programa mais um outro.
Porque nós estamos prestes a usar o segundo argumento aqui.
Então, ter algum tipo de teste com antecedência, antes de nossa chamada strcmp
que os testes ou não argv é pelo menos duas, também faria o mesmo tipo de coisa.
Podemos ver se isso funciona, executando o programa novamente.
Você sempre pode reiniciar o programa dentro do GDB, que é muito bom.
Você pode correr, e quando você passar argumentos para o seu programa,
você passá-los quando você chamar executar, não quando você arrancar GDB.
Dessa forma, você pode manter invocando o seu programa com argumentos diferentes a cada vez.
Então corra, ou ainda, eu posso digitar r, e vamos ver o que acontece se digitar "Olá".
Será sempre perguntar se você deseja iniciá-lo desde o início novamente.
Normalmente, você quer começar desde o início novamente.
E, neste ponto, ele reinicia-lo novamente, ele imprime
o programa que nós estamos correndo, buggy1, com o argumento Olá,
e imprime isso padrão, ele diz, "Você tem um D," face triste.
Mas nós não seg culpa. Ele disse que o processo saiu normalmente.
De modo que parece muito bom.
Nenhuma falha mais seg, conseguimos passado,
então parece que esse era realmente o bug culpa seg que estávamos recebendo.
Infelizmente, ele nos diz que nós estamos começando um D.
>> Nós podemos ir para trás e olhar para o código e ver o que estava acontecendo lá
para descobrir o que era - por isso que estava nos dizendo que temos um D.
Vamos ver, aqui foi este printf dizendo que você tem um D.
Se tipo de lista, como você manter a lista de digitar, ele mantém a iteração através de seu programa,
por isso vai mostrar-lhe as primeiras linhas do seu programa.
Em seguida, ele vai mostrar-lhe as próximas linhas, eo pedaço seguinte eo próximo pedaço.
E vai continuar tentando ir para baixo.
E agora nós vamos começar a "linha de número 16 está fora de alcance."
Porque só tem 15 linhas.
Se você chegar a este ponto e seu perguntando: "O que eu faço?" você pode usar o comando de ajuda.
Use a ajuda e, em seguida, dar-lhe o nome de um comando.
E você vê o GDB nos dá todo este tipo de coisa.
Ela diz: "Sem argumento, lista dez linhas mais após ou em torno da listagem anterior.
Lista - lista as 10 linhas antes - "
Então, vamos tentar usar menos lista.
E que lista as 10 linhas anterior, você pode brincar com uma lista pouco.
Você pode fazer a lista, lista -, você pode até dar listar um número, como a lista de 8,
e ele vai listar as 10 linhas em torno da linha 8.
E você pode ver o que está acontecendo aqui é que você tem um simples, se outra coisa.
Se você digitar CS50 rochas, ela mostra "Você tem um A."
Caso contrário, ele imprime "Você tem um D."
Cidade chatice. Tudo bem. Sim?
>> [Daniel] Então, quando eu tentei fazer CS50 rochas sem as aspas,
ele diz "Você tem um D."
Eu precisava das aspas para fazê-lo funcionar, por que isso?
Sim >>. Acontece que quando - este é outro petisco pouco de diversão -
quando você executar o programa, se executá-lo e digitar CS50 rochas,
apenas como Daniel estava dizendo que ele fez, e você aperte Enter,
ele ainda diz que obter um D.
E a pergunta é, por que é isso?
E verifica-se que o terminal nosso e GDB analisar estes como dois argumentos distintos.
Porque quando há um espaço, que está implícito como
o primeiro argumento terminou, o próximo argumento está prestes a começar.
A maneira de combinar os em dois, ou muito, em um argumento,
é usar as aspas.
Então, agora, se colocá-lo entre aspas e executá-lo novamente, temos um A.
Então, só para recapitular, sem aspas, CS50 e rochas são analisados ​​como dois argumentos distintos.
Com aspas, é analisado como um argumento completamente.
>> Podemos ver isso com um ponto de interrupção.
Até agora temos sido executado o nosso programa, e está sendo executado
até que ela seg falhas ou acertos um erro
ou até que ele saiu e tudo tem sido totalmente bem.
Isso não é necessariamente a coisa mais útil, porque às vezes
você tem um erro no seu programa, mas ele não está causando uma falha de segmentação.
Ele não está causando o seu programa para parar ou qualquer coisa assim.
O caminho para chegar GDB para pausar o seu programa em um determinado ponto
é definir um ponto de interrupção.
Você pode fazer isso através da criação de um ponto de interrupção em um nome de função
ou você pode definir um ponto de interrupção em uma determinada linha de código.
Eu gosto de definir pontos de interrupção em nomes de função, porque - fácil de lembrar,
e se você realmente entrar e alterar seu código fonte se um pouco,
então o seu ponto de interrupção vai realmente ficar no mesmo lugar dentro de seu código.
Considerando que se você estiver usando números de linha, e os números de linha mudar
porque você adicionar ou excluir algum código, em seguida, os pontos de interrupção estão totalmente asneira.
Uma das coisas mais comuns que fazer é definir um ponto de interrupção na função principal.
Muitas vezes, eu vou arrancar o GDB, eu vou digitar b principal, aperte Enter, e que vai definir um ponto de interrupção
sobre a função principal, que apenas diz: "Pause o programa assim que você começar a correr",
e de que maneira, quando eu executar o meu programa com, digamos, CS50 rochas como dois argumentos
e pressione Enter, ele fica com a função principal e pára na primeira linha,
direita antes que avalia a função strcmp.
>> Desde que eu estou uma pausa, agora posso começar a perder tempo e ver o que está acontecendo
com todas as diferentes variáveis ​​que são passados ​​para o meu programa.
Aqui eu posso imprimir argc e ver o que está acontecendo.
Veja que argc é 3, porque tem três valores diferentes nela.
Tem o nome do programa, ele tem o primeiro argumento eo segundo argumento.
Nós podemos imprimir os para fora, olhando para argv [0], argv [1], e argv [2].
Então, agora você também pode ver por esta chamada strcmp vai falhar,
porque você vê que o fez dividir o CS50 e as rochas em dois argumentos distintos.
Neste ponto, uma vez que você tenha atingido um ponto de interrupção, você pode continuar a percorrer o seu programa
linha por linha, em vez de começar o programa novamente.
Então, se você não quer começar o seu programa novamente e apenas continuar a partir daqui,
você pode usar o comando continue e continuar o programa será executado até o fim.
Assim como ele fez aqui.
No entanto, se eu reiniciar o programa, CS50 rochas, que bate no meu ponto de interrupção de novo,
e desta vez, se eu não quero apenas ir até o fim com o restante do programa,
Eu posso usar o comando seguinte, o que eu também abreviar com n.
E isso vai percorrer o programa linha a linha.
Assim, você pode ver como as coisas executar, como mudança de variáveis, como as coisas se atualizado.
O que é muito bom.
A outra coisa legal é, em vez de repetir o mesmo comando mais e mais e mais uma vez,
se você apenas pressione Enter - por isso aqui você vê que eu não ter digitado nada -
se eu apenas aperte Enter, ele vai repetir o comando anterior,
ou o comando GDB anterior que eu só colocar dentro
Eu posso continuar batendo Enter e ele vai manter percorrendo meu código linha por linha.
Gostaria de incentivar vocês para ir verificar os programas de buggy outros também.
Não temos tempo para passar por todos eles hoje em seção.
O código fonte está lá, então você pode tipo de ver o que está acontecendo
nos bastidores se você ficar muito preso,
mas, no mínimo, apenas a prática de arrancar GDB,
a execução do programa até que se rompe em você,
recebendo o backtrace, descobrir qual a função do acidente estava,
que linha era em, imprimindo alguns valores variáveis,
só para você ter uma idéia para ele, porque isso vai realmente ajudá-lo daqui para frente.
Neste ponto, nós estamos indo para sair fora do GDB, que você parar de usar ou apenas q.
Se o seu programa está no meio da execução, ainda assim, e não foi encerrado,
ele sempre perguntar: "Você tem certeza de que você realmente deseja sair?"
Você pode apenas bater sim.
>> Agora vamos olhar para o próximo problema que temos, que é o programa de gato.
Se você assistir o curta em redirecionando e tubos, você vai ver que Tommy usa este programa
que, basicamente, imprime toda a saída de um arquivo para a tela.
Então, se eu executar gato, este é realmente um programa embutido no aparelho,
e se você tem Macs você pode fazer isso no seu Mac também, se você abrir o terminal.
E nós - gato, vamos dizer, cp.c, e pressione Enter.
O que este fez, se rolar um pouco e ver onde nós corremos a linha,
ou em que executou o comando gato, literalmente impressa o conteúdo do cp.c a nossa tela.
Nós podemos correr de novo e você pode colocar em vários arquivos juntos.
Assim você pode fazer cp.c gato, e então também podemos concatenar o arquivo cat.c,
que é o programa que estamos prestes a escrever,
e ele vai imprimir ambos os arquivos de volta para fazer a nossa tela.
Então, se rolar um pouco, vemos que, quando rodamos este cp.c gato, cat.c,
primeiro-lo impresso o arquivo cp, e depois abaixo, ele imprimiu o arquivo cat.c bem aqui.
Nós vamos usar isso para apenas molhar os pés.
Brinque com impressão simples ao terminal, ver como é que funciona.
Se vocês se abrir com gedit cat.c, pressione Enter,
você pode ver o programa que estamos prestes a escrever.
Nós incluímos esta placa de caldeira nice, portanto, não tem que gastar tempo digitando tudo isso.
Nós também verificar o número de argumentos passado dentro
Nós imprimir uma mensagem de uso agradável.
>> Este é o tipo de coisa que, mais uma vez, como temos vindo a falar,
é quase como se a memória muscular.
Apenas lembre-se de continuar a fazer o mesmo tipo de material
e sempre imprimir algum tipo de mensagem útil
para que as pessoas saibam como executar o seu programa.
Com o gato, é muito simples, nós só vamos passar por todos os diferentes argumentos
que foram passadas para o nosso programa, e nós estamos indo para imprimir
o seu conteúdo para fora para o ecrã de um de cada vez.
Para imprimir arquivos para a tela, vamos fazer algo muito semelhante
ao que fizemos no final do questionário.
Ao final do questionário, que contratem programa, tivemos que abrir um arquivo,
e depois tivemos de imprimir nela.
Neste caso, vamos abrir um arquivo, e vamos lê-lo em seu lugar.
Então nós vamos para imprimir, em vez de um arquivo, vamos imprimir na tela.
Assim, a impressão para a tela que todos fizemos antes com printf.
Então, isso não é muito louco.
Mas a leitura de um arquivo é meio estranho.
Nós vamos passar por isso um pouco de cada vez.
Se vocês voltar a esse último problema em seu questionário, problema 33,
a primeira linha que nós vamos fazer aqui, abrir o arquivo, é muito semelhante ao que fizemos lá.
Então, Stella, o que faz aquele olhar linha como, quando abrir um arquivo?
[Stella] FILE * Capital, arquivo - >> Okay. >> - É igual a fopen. >> Yup.
Que neste caso é? É no comentário.
>> É no comentário? argv [i] e r?
>> Exatamente. Certo. Então, Stella é totalmente certo.
Isto é o que a linha parece.
Nós estamos indo para obter uma variável fluxo de arquivo, armazená-lo em um FILE *, então todas as tampas,
FILE, *, eo nome desta variável será de arquivo.
Poderíamos chamá-lo de tudo o que gosta. Nós poderíamos chamar isso first_file, ou file_i, tudo o que gostaria.
E então o nome do arquivo foi passado na linha de comando para este programa.
Então, ele é armazenado em argv [i] e, em seguida, vamos abrir este arquivo em modo de leitura.
Agora que abriu o arquivo, o que é a coisa que nós sempre temos que lembrar de fazer
sempre que você abriu um arquivo? Fechá-lo.
Então Missy, como é que vamos fechar um arquivo?
[Missy] fclose (arquivo) >> fclose (arquivo). Exatamente.
Grande. Okay. Se olharmos para este fazer comentário aqui,
ele diz, "Open argv [i] e imprimir o seu conteúdo para stdout."
>> Saída padrão é um nome estranho. Stdout é apenas uma maneira de dizer
queremos imprimir ao terminal, queremos imprimi-lo para o fluxo de saída padrão.
Podemos realmente se livrar deste comentário aqui.
Eu estou indo para copiá-lo e colá-lo, já que é o que nós fizemos.
Neste ponto, agora temos que ler o arquivo de pouco a pouco.
Nós discutimos algumas formas de leitura de arquivos.
Quais são os seus favoritos até agora?
Que forma você já viu ou não se lembra, para ler arquivos?
[Daniel] fread? Fread >>? Então fread é um deles. Jimmy, você conhece algum outro?
[Jimmy] Não. >> Okay. Nope. Charlotte? Alexander? Quaisquer outros? Okay.
Assim, os outros são fgetc, é um que nós vamos usar muito.
Há também fscanf; vocês ver um padrão aqui?
Todos eles começam com f. Nada a ver com um arquivo.
Há fread, fgetc, fscanf. Estas são todas as funções de leitura.
Para escrever temos fwrite, temos fputc vez de fgetc.
Temos também fprintf como vimos no questionário.
Uma vez que este é um problema que envolve a leitura de um ficheiro,
vamos usar uma dessas três funções.
Nós não estamos indo para usar estas funções aqui.
Estas funções são encontradas na biblioteca de I / O padrão.
Então, se você olhar para o topo do programa,
você pode ver que nós já incluído o arquivo de cabeçalho para a biblioteca de I / O padrão.
Se quisermos descobrir qual queremos usar,
podemos sempre abrir as páginas man.
Assim, podemos digitar stdio homem
e ler tudo sobre a entrada stdio e funções de saída em C.
E já podemos ver oh, olha. É mencionar fgetc, é mencionar fputc.
Assim, você pode se aprofundar um pouco e olhar para, digamos, fgetc
e olhar para sua página do homem.
Você pode ver que ele vai junto com um monte de outras funções:
fgetc, fgets, getc, getchar, fica, ungetc, e sua entrada de caracteres e strings.
Portanto, esta é a maneira como lemos em caracteres e strings de arquivos de entrada padrão,
que é, essencialmente, a partir do utilizador.
E é assim que fazemos em C. real
Portanto, este não está usando o GetString e funções getchar
que foi utilizada a partir da biblioteca de CS50.
Nós vamos fazer esse problema em um par de formas
de modo que você pode ver duas maneiras diferentes de fazê-lo.
Tanto a função fread que Daniel mencionado e fgetc são boas maneiras de fazê-lo.
Eu acho que fgetc é um pouco mais fácil, porque ele só tem, como se vê,
um argumento, o FILE * que estamos tentando ler o personagem,
e seu valor de retorno é um int.
E isso é um pouco confuso, né?
>> Porque nós estamos começando um personagem, então por que não faz isso um retorno char?
Vocês têm alguma idéia sobre por que isso não pode retornar um char?
[Respostas Missy, ininteligível] >> sim. Então, Missy é totalmente certo.
Se for ASCII, então este inteiro poderia ser mapeado para um caractere real.
Poderia ser um caractere ASCII, e isso é certo.
Isso é exatamente o que está acontecendo.
Estamos usando um int simplesmente porque tem mais bits.
É maior do que um char, o nosso carvão tem apenas 8 bits, 1 byte que em nossas máquinas de 32 bits.
E um int tem valor todos os 4 bytes 'do espaço.
E acontece que a maneira fgetc funciona,
se deslocar para baixo na nossa sinopse nesta página do manual um pouco,
percorrer todo o caminho para baixo. Acontece que eles usam esse valor especial chamado EOF.
É uma constante especial como o valor de retorno da função fgetc
sempre que você atingiu no final do arquivo ou se você receber um erro.
E verifica-se que para fazer estas comparações com EOF corretamente,
você quer ter essa quantidade extra de informações que você tem em um int
em vez de usar uma variável char.
Mesmo que fgetc é efetivamente começar um personagem de um arquivo,
você quer se lembrar que ele está retornando algo que é do tipo int para você.
Dito isso, é bastante fácil de usar.
Vai nos dar um caráter, por isso tudo o que temos a fazer é continuar fazendo o arquivo,
"Dê-me o próximo personagem, dá-me o próximo personagem, dá-me o próximo personagem",
até chegarmos ao final do arquivo.
E que irá puxar um personagem em um momento do nosso arquivo,
e então nós podemos fazer o que quiser com ele.
Nós podemos armazená-lo, podemos adicioná-lo a uma corda, que pode imprimi-lo.
Fazer nada disso.
>> Zoom de volta e voltar ao nosso programa cat.c,
se vamos usar fgetc,
como podemos abordar esta próxima linha de código?
Nós vamos usar - fread vai fazer alguma coisa um pouco diferente.
E, desta vez, estamos indo só para usar fgetc para obter um personagem de cada vez.
Para processar um arquivo inteiro, o que poderia nós temos que fazer?
Quantos personagens estão lá em um arquivo?
Há uma grande quantidade. Então você provavelmente quer obter um
e depois pegar outro e começar outro e começar outro.
Que tipo de algoritmo que você acha que pode ter que usar aqui?
Que tipo de -? [Alexander] Um loop for? >> Exatamente.
Algum tipo de laço.
Um laço for é realmente grande, neste caso.
E como você estava dizendo, parece que você quer um laço sobre o arquivo inteiro,
obtenção de um personagem de cada vez.
Sugestões sobre o que pode parecer?
[Alexander, ininteligível]
>> Ok, diga-me em Inglês o que você está tentando fazer? [Alexander, ininteligível]
Portanto, neste caso, parece que estamos apenas tentando varrer o arquivo inteiro.
[Alexander] Então i > O tamanho de -?
Eu acho que o tamanho do arquivo, certo? O tamanho - nós apenas escrever assim.
Tamanho do arquivo para o momento, i + +.
Assim, verifica-se que a maneira de fazer isso usando fgetc, e isso é novo,
é que não há nenhuma maneira fácil de obter apenas o tamanho de um arquivo
com este "sizeof" tipo de construção que você já viu antes.
Quando usamos essa função fgetc, estamos introduzindo algum tipo de
sintaxe, novo funky para este loop, onde em vez de usar apenas um contador básico
ir caractere por caractere, vamos puxar um personagem de cada vez,
um personagem de cada vez, e da maneira que sabemos que estamos no fim
não é quando temos contado um determinado número de caracteres,
mas quando o personagem que retire é esse fim específico do arquivo de caracteres.
Assim, podemos fazer isso - Eu chamo isso de pc, e nós estamos indo para o inicializar
com a nossa primeira chamada para obter o primeiro caractere fora do arquivo.
Então, essa parte aqui, isso vai obter um personagem fora do arquivo
e armazená-lo no ch variável.
Nós vamos continuar fazendo isso até chegarmos ao final do arquivo,
o que fazemos por meio de testes para o personagem não ser igual ao que o personagem EOF especial.
E, em seguida, em vez de fazer o ch + +, o que apenas aumentar o valor,
por isso, se lemos um fora A do arquivo, uma capital de um, digamos,
ch + + nos daria b, e então teríamos c e d.
Isso claramente não é o que queremos. O que nós queremos aqui
neste último bit é que queremos obter o próximo personagem a partir do arquivo.
>> Então, como podemos obter o próximo caractere do arquivo?
Como podemos obter o primeiro caractere do arquivo?
[Estudante] fgetfile? Fgetc >>, ou, desculpe, você estava totalmente certo.
Eu grafada aí. Então, sim.
Aqui, em vez de fazer ch + +,
nós apenas estamos indo para chamar fgetc (arquivo) novamente
e armazenar o resultado em nossa variável ch mesmo.
[Pergunta do estudante, ininteligível]
>> Este é onde esses caras arquivo * são especiais.
A forma de trabalhar é que - quando você abre pela primeira vez - quando você fazer essa chamada fopen,
o FILE * eficazmente serve como um ponteiro para o início do processo.
E então, cada vez que você chamar fgetc, move-se um personagem através do arquivo.
Assim, sempre que você chama isso, você está incrementando o ponteiro de arquivo por um caractere.
E quando você fgetc novamente, você está movendo-outro personagem
e outro personagem e outro personagem e outro personagem.
[Pergunta do estudante, ininteligível] >> E isto - sim.
É uma espécie de esta magia debaixo do capô.
Você simplesmente continua incrementando através.
Neste ponto, você é capaz de realmente trabalhar com um personagem.
Então, como podemos imprimir isso para a tela, agora?
Podemos usar a mesma coisa printf que usamos antes.
Que temos vindo a utilizar todo o semestre.
Podemos chamar printf,
e podemos passar o personagem assim.
Outra maneira de fazer isso é em vez de usar printf e ter que fazer essa seqüência de formato,
também podemos usar uma das outras funções.
Podemos usar fputc, que imprime um caráter para a tela,
exceto se olharmos para fputc - deixe-me afastar um pouco.
Nós vemos o que é bom é que leva no caráter que lemos, utilizando fgetc,
mas então temos de dar-lhe um fluxo para imprimir.
Podemos também utilizar a função putchar, que irá colocar diretamente para saída padrão.
Portanto, há um monte de opções diferentes que podemos usar para impressão.
Eles estão todos na biblioteca de I / O padrão.
Sempre que você deseja imprimir - assim printf, por padrão, irá imprimir à norma especial saída de fluxo,
que é a saída padrão.
Assim, podemos apenas se referem a ele como uma espécie de magia este valor, stdout aqui.
Oops. Coloque o ponto e vírgula fora.
>> Esta é uma grande quantidade de informação nova, funk aqui.
Uma grande parte destas é muito idiomática, no sentido de que este é o código
que é escrito desta forma só porque é limpo de ler, fácil de ler.
Há muitas maneiras diferentes de fazer isso, muitas funções diferentes que você pode usar,
mas nós só tendem a seguir estes mesmos padrões mais e mais.
Portanto, não se surpreenda se você ver um código como este vindo de novo e de novo.
Tudo bem. Neste ponto, é preciso quebrar para o dia.
Obrigado por vir. Obrigado por assistir, se você estiver online. E nós vamos vê-lo na próxima semana.
[CS50.TV]