[Powered by Google Translate] [Seção 5 - Mais Confortável]
[Rob Bowden - Harvard University]
[Esta é CS50. - CS50.TV]
>> Como eu disse em meu e-mail, há um monte de coisas que você pode usar
além do aparelho de realmente fazer os conjuntos de problemas.
Nós recomendamos que você faça isso no aparelho apenas porque então nós podemos ajudá-lo com mais facilidade
e sabemos como tudo vai funcionar.
Mas, como um exemplo de onde você pode fazer coisas, digamos, se você não tem acesso
para um aparelho ou você quer trabalhar no porão do Centro de Ciências -
que na verdade eles têm o aparelho também -
se você quer trabalhar em qualquer lugar.
Um exemplo é você já viu / ouviu falar de SSH?
O SSH é basicamente como conectar-se a alguma coisa.
Na verdade, agora eu estou SSHed no aparelho.
Eu nunca trabalhar diretamente no aparelho.
Aqui está o aparelho, e se você olhar para baixo aqui você vê este endereço IP.
Eu nunca trabalhar no próprio aparelho;
Eu sempre vinha de uma janela janela de terminal / iTerm2.
Você pode SSH para que o endereço IP, o ssh jharvard@192.168.129.128.
Lembro-me de que esse número muito facilmente porque é um padrão tão agradável.
Mas isso vai me pedir a minha senha, e agora eu estou no aparelho.
Basicamente, neste momento, se você abriu um terminal dentro do próprio aparelho,
esta interface, porém você usá-lo, é exatamente a mesma
como a interface que estou usando aqui, mas agora você está SSHed.
Você não tem que SSH para o aparelho.
Um exemplo de outro lugar você poderia SSH é que eu tenho certeza que você tem por padrão -
Oh. Maior.
Todos vocês devem ter por contas padrão FAS nos servidores da FAS.
Para mim, eu faria o SSH para rbowden@nice.fas.harvard.edu.
Vai pedir-lhe que da primeira vez, e você diz que sim.
Minha senha é apenas vai ser a minha senha FAS.
E agora, eu estou SSHed aos servidores agradável, e eu posso fazer o que eu quiser aqui.
Um monte de classes que podem ser adotadas, como a 124, vai ter de carregar coisas para aqui
para realmente apresentar seus conjuntos de problemas.
Mas digamos que você não tem acesso ao seu aparelho.
Então você pode fazer coisas, como aqui, ele vai dizer -
Este é apenas nossa seção de perguntas.
Ele vai pedir para você fazer isso no aparelho.
Em vez disso eu vou fazê-lo no servidor.
Eu estou indo para descompactar isso.
O problema vai ser que você está acostumado a usar algo como gedit
ou o que quer no interior do aparelho.
Você não vai ter que no servidor FAS.
É tudo só vai ser esta interface textual.
Então, você poderia um ou outro, tentar aprender um editor de texto que eles têm.
Eles têm Nano.
Nano é normalmente muito fácil de usar.
Você pode usar as suas flechas e digite normalmente.
Então, isso não é difícil.
Se você quiser começar realmente a fantasia que você pode usar o Emacs,
que eu provavelmente não deveria ter aberto, porque eu não sei nem como fechar Emacs.
Controle X, Controle C? Sim.
Ou você pode usar o Vim, que é o que eu uso.
E assim essas são suas opções.
Se você não quiser fazer isso, você também pode, se você olhar para manual.cs50.net -
Oh.
Em um PC, você pode SSH usando PuTTY,
que você vai ter que baixar separadamente.
Em um Mac, você pode apenas pelo terminal de uso padrão ou você pode baixar iTerm2,
que é como um terminal, agradável fantasia.
Se você vai para manual.cs50.net você verá um link para o Notepad + +,
que é o que você pode usar em um PC.
Ele permite que você SFTP de Notepad + +, que é basicamente SSH.
O que isso vai deixar você fazer é editar seus arquivos localmente,
e sempre que você quer salvá-los, ele irá salvar a nice.fas,
onde você pode executá-los.
E o equivalente em um Mac vai ser TextWrangler.
Então, ele permite que você faça a mesma coisa.
Ele permite que você edite arquivos localmente e salvá-los em nice.fas,
onde você pode executá-los.
Então, se você está sempre preso sem um aparelho, você tem estas opções
ainda fazer seus conjuntos de problemas.
O único problema vai ser que você não vai ter a biblioteca CS50
porque nice.fas não por padrão tem isso.
Você pode baixar a biblioteca CS50 -
Eu não acho que eu preciso neste momento.
Você pode baixar a biblioteca CS50 e copie-o para nice.fas,
ou eu acho que neste momento nós não usá-lo mais de qualquer maneira.
Ou se nós, você pode, por enquanto, substituí-lo por
as implementações das funções na biblioteca CS50 de qualquer maneira.
De modo que não deve ser muito de uma restrição.
E é isso.
>> Eu vou voltar para o aparelho agora, nós vamos fazer de tudo no aparelho.
Olhando para nossa seção de perguntas, no início, como eu disse no meu e-mail,
temos que falar sobre o curto você deveria assistir.
Temos o redirecionamento & Pipes e essas três perguntas.
>> Para que fluxo que funciona como printf escrever por padrão?
Então córrego. O que é um fluxo?
Um fluxo é basicamente como se fosse apenas alguns -
Não é mesmo uma fonte de 1s e 0s.
O fluxo é pedir aqui é a padrão.
E de modo padrão é um fluxo que quando você escrever para ele,
ele aparece na tela.
A Standard, por stream, isso significa que você acabou de escrever 1s e 0s a ele,
e a outra extremidade de saída padrão apenas lê a partir desse fluxo.
É apenas uma seqüência de 1s e 0s.
Você pode escrever para córregos ou você pode ler a partir de fluxos
dependendo do que o fluxo é realmente.
Os outros dois fluxos padrão são padrão e erro padrão.
Padrão em é sempre que você GetString, está esperando por você para material de entrada.
Por isso, esperando por você, é realmente à espera de padrão em,
o que é realmente o que você começa quando você digitar no teclado.
Você está digitando em padrão dentro
Erro padrão é basicamente equivalente para a saída padrão,
mas é especializada em que quando você imprimir para o erro padrão,
você deveria apenas imprimir mensagens de erro para que
para que você possa distinguir entre mensagens regulares impressos na tela
contra mensagens de erro, dependendo se eles foram para fora padrão ou erro padrão.
Arquivos também.
Fora padrão, padrão, e erro padrão são fluxos apenas especiais,
mas realmente qualquer arquivo, quando você abre um arquivo, ele torna-se um fluxo de bytes
onde você pode apenas ler esse fluxo.
Você, em sua maior parte, pode apenas pensar em um arquivo como um fluxo de bytes.
Então, o que os fluxos que eles escrevem para por padrão? Saída padrão.
>> Qual é a diferença entre> e? >>
Será que alguém assistir ao vídeo antes? Okay.
> Vai ser como você redirecionar em arquivos,
e >> também vai redirecionar a saída em arquivos,
mas é em vez de ir para anexar o arquivo.
Por exemplo, digamos que eu acontecer de ter dict aqui,
e as coisas só dentro de dict é gato, gato, cachorro, peixe, cão.
Um comando que você tem na linha de comando é gato,
que está indo só para imprimir o que está em um arquivo.
Portanto, quando digo dict gato, que vai imprimir gato, cão, gato, peixe, cão. Isso é tudo o gato faz.
Isso significa que ele impressa para a saída padrão, gato, cão, gato, peixe, cão.
Se eu vez quer redirecionar isso para um arquivo, eu posso usar> e redirecioná-lo para o que é o arquivo.
Eu vou chamar o arquivo de arquivo.
Então agora eu ls, eu vou ver eu tenho um novo arquivo chamado arquivo.
E se eu abri-lo, ele vai ter exatamente o gato colocar na linha de comando.
Portanto, agora se eu fizer isso de novo, então ele vai redirecionar a saída em arquivo,
e eu vou ter exatamente a mesma coisa.
Então, tecnicamente, é completamente anulou o que tínhamos.
E vamos ver se eu mudar dict, eu tirei cão.
Agora, se nós gato dict em arquivo novamente, nós vamos ter a nova versão com o cão removido.
Então é completamente substitui-lo.
Em vez disso, se usarmos >>, que vai anexar o arquivo.
Agora, abrir o arquivo, vemos que temos exatamente a mesma coisa duas vezes impressa
porque ali uma vez, em seguida, adicionado ao original.
Então é isso que> >> e fazer.
Será que a próxima pergunta - Não perguntar sobre isso.
>> O outro que temos é <, que se> redireciona saída padrão,
<Vai ser redirecionar padrão dentro
Vamos ver se temos um exemplo.
Eu posso escrever um rápido.
Vamos dar qualquer arquivo, hello.c.
Arquivo relativamente simples.
Eu estou apenas começando uma seqüência e depois imprimir "Olá" qualquer que seja a seqüência de caracteres que acabara de entrar era.
Então faça Olá e depois. / Olá.
Agora ele está me levando para entrar alguma coisa,
o que significa que está esperando as coisas a ser celebrado padrão dentro
Então o que eu quiser entrar em padrão dentro Nós apenas estamos indo para dizer Olá, Rob!
Então está imprimindo para saída padrão Olá, Rob!
Se eu fizer. / Olá e depois redirecionar,
por agora você só pode redirecionar a partir de um arquivo.
Então, se eu colocar em algum arquivo, txt, e eu coloquei Rob,
se eu executar Olá e então redirecionar o arquivo txt em. / Olá, ele vai dizer Olá, Rob! imediatamente.
Quando se chega pela primeira vez GetString e está à espera de padrão em,
padrão em não mais espera no teclado para que os dados se inseridos.
Em vez disso, temos redirecionado padrão para ler o arquivo txt.
E assim ele vai ler o txt, que é apenas o Rob linha,
e, em seguida, ele vai imprimir Olá, Rob!
E se eu quisesse, eu também poderia fazer. / Olá <txt
e então o padrão que ele é impressão, o que é Olá, Rob!,
Eu posso redirecionar que em seu próprio arquivo.
Eu vou chamar o arquivo Olá - não, eu não vou, porque esse é o executável - txt2.
Agora, txt2 vai ter a saída do. / Olá <txt, o que vai ser Olá, Rob!
>> Perguntas?
>> Okay. Então aqui nós temos pipeline.
Pipes são a última unidade de redirecionamento.
>> Oh. Eu acho que mais uma unidade de redirecionamento é se em vez de> você faz 2>,
que está redirecionando erro padrão.
Então, se alguma coisa deu para o erro padrão, não teria se colocado em txt2.
Mas repare se eu fizer 2>, então ele ainda está imprimindo Olá, Rob! para a linha de comando
porque eu estou apenas redirecionando erro padrão, eu não estou redirecionando a saída padrão.
Erro padrão e saída padrão são diferentes.
Se você quisesse realmente escrever para o erro padrão,
então eu poderia mudar isso para ser fprintf para stderr.
Então printf, por padrão, imprime para a saída padrão.
Se eu quiser imprimir para o erro padrão manualmente, então eu tenho que usar fprintf
e especificar o que deseja imprimir.
Se em vez fiz stdout fprintf, em seguida, que é basicamente equivalente a printf.
Mas fprintf para o erro padrão.
Então, agora, se eu redirecionar isso em txt2, Olá, Rob! ainda está sendo impressas na linha de comando
uma vez que está sendo impressas para o erro padrão e eu só estou redirecionando a saída padrão.
Se eu agora redirecionar o erro padrão, agora não se impresso, e txt2 vai ser Olá, Rob!
Então, agora, você pode imprimir seus erros reais para o erro padrão
e imprimir as suas mensagens regulares para a saída padrão.
E assim, quando você executar o programa, você pode executá-lo como. / Olá, este tipo com o 2>
para que o seu programa vai funcionar normalmente,
mas as mensagens de erro que você começa você pode verificar mais tarde, em seu log de erro,
assim os erros, e depois olhar mais tarde e seu arquivo de erros terá quaisquer erros que aconteceram.
>> Perguntas?
>> A última é a tubulação, que você pode pensar em como tirar a saída padrão de um comando
e tornando-se o padrão do comando seguinte.
Um exemplo aqui é o eco é uma coisa de linha de comando
que é só ir para ecoar o que eu coloquei como seu argumento. Não vou colocar aspas.
Eco blá, blá, blá é só ir para imprimir blá, blá, blá.
Antes, quando eu disse que eu tinha que colocar Rob em um arquivo txt
porque eu só posso redirecionar os arquivos txt, em vez disso, / se eu echo Rob
e depois canalizá-lo em. / Olá, que também vai fazer o mesmo tipo de coisa.
Isto está levando a saída deste comando, eco Rob,
e usá-lo como entrada para. / Olá.
Você pode pensar nisso como primeiro redirecionamento eco Rob em um arquivo
e depois de entrada em. / Olá, esse arquivo que acabou de ser emitidas.
Mas é preciso o arquivo temporário fora do quadro.
>> Perguntas sobre isso?
>> A próxima pergunta vai envolver isso.
O gasoduto você poderia usar para encontrar o número de nomes exclusivos em um arquivo chamado names.txt?
Os comandos que vamos querer usar aqui são únicos, por isso uniq, e wc.
Você pode fazer uniq homem para realmente olhar para o que faz,
e ele só vai para filtrar linhas correspondentes adjacentes a partir da entrada.
E o homem wc vai imprimir a palavra, quebra de linha, e bytes para cada arquivo.
E a última que vamos querer usar é tipo,
que está indo apenas para classificar linhas de um arquivo txt.
Se eu fizer algum arquivo txt, names.txt, e é Rob, Tommy, Joseph, Tommy, Joseph, RJ, Rob,
o que eu quero fazer aqui é encontrar o número de nomes únicos neste arquivo.
Então, o que deve ser a resposta? >> [Aluno] 4. Sim >>.
Deve ser 4, pois Rob, Tommy, Joseph, RJ são os nomes originais só neste arquivo.
O primeiro passo, se eu apenas fazer contagem de palavras na names.txt,
isto é, na verdade, me contando tudo.
Esta é realmente a impressão - Vamos ver, o homem wc - novas linhas, palavras e contagem de bytes.
Se eu só me importo com as linhas, então eu posso apenas fazer wc-l names.txt.
Então esse é um passo.
Mas eu não quero wc-l names.txt porque names.txt apenas contém todos os nomes,
e eu quero filtrar todas as não-originais.
Então, se eu fizer names.txt uniq, que não chega a me dar o que eu quero
porque os nomes duplicados ainda estão lá.
Por que isso? Porque é que não uniq fazendo o que eu quero?
[Aluno] O duplicatas não são [inaudível] >> Yeah.
Lembre-se da página de manual para uniq diz filtrar linhas correspondentes adjacentes.
Eles não são adjacentes, por isso não irá filtrá-los.
Se eu classificá-los em primeiro lugar, names.txt tipo vai colocar todas as linhas duplicadas juntos.
Então agora é que tipo names.txt.
Eu vou querer usar isso como a entrada para uniq, que é | uniq.
Isso dá-me José, RJ, Rob, Tommy,
e eu quero usar isso como a entrada para o wc-l,
que vai me dar 4.
Como diz aqui, o gasoduto poderia usar?
Você pode fazer um monte de coisas como usar uma série de comandos
onde você usa a saída de um comando como entrada para o próximo comando.
Você pode fazer um monte de coisas, um monte de coisas inteligentes.
>> Perguntas?
Okay.
Isso é tudo para tubos e redirecionamento.
>> Agora vamos para a coisa real, o material de codificação.
Dentro deste PDF, você verá este comando,
e você deseja executar este comando no seu aparelho.
wget é o comando para apenas pegar alguma coisa da Internet, basicamente,
assim wget e este URL.
Se você foi para este URL no seu navegador, seria baixar o arquivo.
Eu só clicar sobre ele, por isso o download do arquivo para mim.
Mas escrever wget de que a coisa dentro do terminal
é só ir para baixá-lo em seu terminal.
Eu tenho section5.zip, e você deseja descompactar section5.zip,
que vai dar-lhe uma pasta chamada section5,
que vai ter todos os arquivos que vamos estar usando hoje dentro dele.
Como os nomes destes programas de arquivo sugerir, eles são um pouco buggy,
assim que sua missão é descobrir por que usar gdb.
Será que todos eles têm baixado / saber como obtê-los baixado
em seu aparelho? Okay.
>> Correndo ./buggy1, ele vai dizer Falha de segmentação (núcleo),
que a qualquer momento você começa um segfault, é uma coisa ruim.
Sob que circunstâncias você começa um segfault?
[Aluno] desreferenciação um ponteiro nulo. Sim >>. De modo que é um exemplo.
Dereferencing um ponteiro nulo você está indo para obter um segfault.
O que um segfault significa é que você está tocando memória que você não deve tocar.
Então dereferencing um ponteiro nulo é tocar o endereço 0,
e, basicamente, todos os computadores hoje em dia dizer que 0 endereço é a memória que você não deve tocar.
É por isso que uma dereferencing resultados ponteiro nulo em um segfault.
Quando acontecer de você não inicializar um ponteiro, então ele tem um valor de lixo,
e assim, quando você tenta excluir a referência que, com toda a probabilidade que você está tocando memória
que está no meio do nada.
Se acontecer de você ter sorte e do valor de lixo
aconteceu para apontar para algum lugar na pilha ou algo assim,
então quando você dereference que o ponteiro que você não tenha inicializado,
nada vai dar errado.
Mas se ele está apontando para, digamos, em algum lugar entre a pilha eo heap,
ou está apontando apenas para algum lugar que não tem sido utilizado pelo seu programa, no entanto,
então você está tocando memória que você não deve estar tocando e você segfault.
Quando você escrever uma função recursiva e recurses muitas vezes
e sua pilha cresce muito grande e as colide pilha em coisas
que não deve ser colidindo com, você está tocando memória que você não deve tocar,
assim você segfault.
Isso é que é segfault.
>> É também a mesma razão que se você tiver uma seqüência como -
vamos voltar ao programa anterior.
Em hello.c-estou indo só para fazer outra coisa.
char * s = "Olá mundo!";
Se eu usar * s = algo ou s [0] = 'X';
então faça Olá,. / Olá, por que isso segfault?
Por que isso segfault?
O que você espera que aconteça?
Se eu fiz printf ("% s \ n", s), o que você espera para ser impresso?
[Estudante] X Olá. Sim >>.
O problema é que quando você declarar uma seqüência como esta,
s é um ponteiro que está indo para ir para a pilha,
e que s está a destacar é esta corda que está contido na memória só de leitura.
Então, só pela memória o nome, só de leitura, você deve ter a idéia
que se você tentar mudar o que está na memória só de leitura,
você está fazendo algo que você não deveria estar fazendo com a memória e você segfault.
Esta é realmente uma grande diferença entre char * s e s char [].
Então char s [], agora esta cadeia vai ser colocado na pilha,
ea pilha não é somente leitura, o que significa que este deve funcionar perfeitamente bem.
E ele faz.
Lembre-se que quando eu faço char * s = "Olá mundo!", S em si é na pilha
s pontos, mas em outro lugar, em outro lugar e que passa a ser somente leitura.
Mas char s [] é apenas algo sobre a pilha.
Então, isso é um outro exemplo de um segfault acontecendo.
>> Vimos que ./buggy1 resultou em um segfault.
Em teoria, você não deve olhar para buggy1.c imediatamente.
Em vez disso, nós vamos olhar para ele através gdb.
Observe que quando você começa Falha de segmentação (núcleo),
você tem este arquivo sobre o núcleo aqui chamado.
Se ls-l, vamos ver que o núcleo geralmente é um arquivo muito grande.
Este é o número de bytes do arquivo, por isso parece que é 250 e poucos kilobytes.
A razão para isto é que o despejo do núcleo, na verdade, é
é quando suas falhas do programa, o estado da memória de seu programa
apenas é copiado e colado a este arquivo.
Ele é despejada dentro do ficheiro.
Este programa, enquanto ele estava correndo, passou a ter um uso de memória de cerca de 250 kilobytes,
e é isso que tem despejado neste arquivo.
Agora você pode olhar para o arquivo, se fizermos gdb núcleo buggy1.
Podemos apenas fazer o gdb buggy1, e que só vai começar a se gdb regularmente,
usando buggy1 como seu arquivo de entrada.
Mas se você fizer gdb núcleo buggy1, então é especificamente indo para iniciar gdb
olhando o arquivo do núcleo.
E você dizendo buggy1 gdb meio sabe que o arquivo vem de núcleo do programa buggy1.
Então gdb buggy1 núcleo vai trazer-nos imediatamente
para onde o programa passou a terminar.
Vemos aqui programa terminou com sinal de 11, falha de segmentação.
Nós acontecer para ver uma linha de montagem, o que provavelmente não é muito útil.
Mas se você digitar bt ou backtrace, que vai ser a função
que nos dá a lista dos nossos quadros de pilha atuais.
Então backtrace. Parece que só temos dois quadros de pilha.
O primeiro é o nosso quadro de pilha principal,
eo segundo é o quadro de pilha para esta função que acontecer para ser,
que parece que só temos o código de montagem para.
Então, vamos voltar para a nossa função principal,
e para isso nós podemos fazer um quadro, e acho que também pode fazer-se,
mas eu quase nunca para baixo - ou para cima. Sim.
Cima e para baixo. Up leva até um quadro de pilha, para baixo leva a um quadro de pilha.
Tenho tendência a nunca usar isso.
Eu só dizer especificamente o quadro 1, que é voltar para o quadro com o 1.
Quadro 1 vai trazer-nos em quadro de pilha principal,
e ele diz que aqui a linha de código que estejamos menos.
Se quiséssemos linhas mais um par de código, podemos dizer lista,
e que vai nos dar todas as linhas de código ao seu redor.
A linha que foi segfaulted em 6:
if (strcmp ("CS50 rochas", argv [1]) == 0).
Se não é óbvio ainda, você pode obtê-lo diretamente daqui apenas pensando por que segfaulted.
Mas podemos dar um passo mais longe e dizer: "Por que argv [1] segfault?"
Vamos impressão argv [1], e parece que ele é 0x0, que é o ponteiro nulo.
Estamos strcmping CS50 rochas e nulos, e assim que vai segfault.
E por que é argv [1] nulo?
[Aluno] Porque nós não dar quaisquer argumentos de linha de comando.
Sim. Nós não dar quaisquer argumentos de linha de comando.
Então ./buggy1 só vai ter argv [0] ser ./buggy1.
Não vai ter uma argv [1], de modo que vai segfault.
Mas se, em vez disso, eu apenas CS50, ele vai dizer que você começa um D
porque isso é o que é suposto fazer.
Olhando buggy1.c, é suposto para imprimir "Você tem um D" -
Se argv [1] não é "CS50 rochas", "Você tem um D", mais "você ganha um!"
Portanto, se queremos um A, temos isso para comparar como verdadeira,
o que significa que ele compara a 0.
Então, argv [1] precisa ser "CS50 pedras".
Se você quiser fazer isso na linha de comando, você precisa usar \ para escapar do espaço.
Então CS50 \ rochas e você ganha um!
Se você não fizer a barra invertida, porque isto não funciona?
[Estudante] É dois argumentos diferentes. Sim >>.
Argv [1] vai ser CS50, e argv [2] vai ser rochas. Okay.
>> Agora ./buggy2 vai segfault novamente.
Em vez de abri-lo com o seu arquivo de núcleo, vamos abrir buggy2 diretamente,
assim gdb buggy2.
Agora, se nós simplesmente executar o nosso programa, então ele vai dizer Programa recebeu sinal SIGSEGV,
que é o segfault sinal, e este é o lugar onde aconteceu a acontecer.
Olhando para o nosso backtrace, vemos que estávamos no oh_no função,
que foi chamado pelo dinky função, que foi chamado pelo binky função,
que foi chamado por principal.
Nós também podemos ver os argumentos para estas funções.
O argumento para dinky e binky foi 1.
Se listar a função oh_no, vemos que oh_no está apenas fazendo char ** s = NULL;
* S = "BOOM";
Por que isso não?
[Aluno] Você não pode cancelar o ponteiro nulo? Sim >>.
Este é apenas dizer s é NULL, independentemente se isso acontece de ser um char **,
o que, dependendo da forma como o interpretar, que poderia ser um apontador para um apontador para uma cadeia
ou uma matriz de strings.
É s é NULL, então * s é dereferencing um ponteiro nulo,
e assim é que isto vai funcionar.
Esta é uma das formas mais rápidas que você pode possivelmente segfault.
É só declarar um ponteiro nulo e imediatamente segmento padrão.
Isso é o que está fazendo oh_no.
Se formos até um quadro, então vamos entrar na função que chamou oh_no.
Eu preciso fazer isso para baixo.
Se você não digitar um comando e você apenas pressione Enter novamente,
ele vai apenas repetir o comando anterior que você correu.
Estamos no quadro 1.
Listando este quadro, vemos aqui é a nossa função.
Você pode bater lista novamente, ou você pode fazer lista de 20 e ele irá listar mais.
O dinky função diz que se eu for 1, então vá para a função oh_no,
senão vai para a função furtivo.
E nós sabemos que é um i porque aconteceu de ver-se aqui
dinky que foi chamado com o argumento 1.
Ou você pode simplesmente não imprimir i e ele vai dizer que eu é 1.
Estamos atualmente em dinky, e se subir um outro quadro, sabemos que vai acabar em binky.
Up. Agora estamos em binky.
Listando esta função - a lista de antes de meia me cortou -
ele começou como se eu é 0, então vamos chamá-lo oh_no, senão chamar dinky.
Nós sabemos que eu era um, por isso chamado dinky.
E agora estamos de volta no principal, e principal só vai ser int i = rand ()% 3;
Isso só vai lhe dar um número aleatório que é 0, 1 ou 2.
Vai chamar binky com esse número, e ele irá retornar 0.
Olhando para isso,
apenas andando através do programa manualmente, sem executá-lo imediatamente,
você deve definir um ponto de ruptura na principal, o que significa que, quando executar o programa
seu programa é executado até que atinge um ponto de ruptura.
Assim, a execução do programa, ele será executado e depois ele vai bater a função principal e parar de correr.
Agora estamos dentro da principal, e passo ou a próxima vai trazer-nos para a próxima linha de código.
Você pode fazer o passo ou o próximo.
Bater seguinte, agora eu tenha sido definido para rand () de 3%, de modo que pode imprimir o valor de i,
e ele vai dizer que eu é 1.
Agora, não importa se usamos próximo ou passo.
Eu acho que importava no anterior, mas gostaria de usar em seguida.
Se usarmos passo, o passo para a função, o que significa olhar para a coisa real
que está acontecendo dentro de binky.
Se usarmos seguinte, então isso significa passar por cima da função
e vá para a próxima linha de código na nossa função principal.
Aqui nesta linha, eu estava em onde ele disse rand ()% 3;
se eu passo, ele iria para a implementação de rand
e olhar para o que está acontecendo lá, e eu poderia pisar através da função rand.
Mas eu não me importo com a função rand.
Eu só quero ir para a próxima linha de código na principal, então eu uso seguinte.
Mas agora eu me importo com a função binky, assim que eu quero entrar nisso.
Agora estou em binky.
A primeira linha de código é que vai dizer se (i == 0), eu dou um passo,
vemos que acabar em dinky.
Se as coisas nós da lista, vemos que ela é marcada i = 0.
i não é igual a 0, de modo que foi para a condição mais,
que vai chamar dinky (i).
Você pode ficar confuso.
Se você só olhar para essas linhas diretamente, você pode pensar que se (i == 0),
ok, então eu dei um passo e agora estou em dinky (i),
você pode pensar que deve significar i = 0 ou algo assim.
Não. Significa apenas que ele sabe que pode ficar diretamente ao dinky linha (i).
Porque eu não for 0, o próximo passo não vai terminar no outro.
Else não é uma linha que vai parar.
É só passar para ir para a próxima linha pode realmente executar, o que é atraente (i).
Entrando dinky (i), vamos ver se (i == 1).
Sabemos i = 1, então quando passo, sabemos que vamos acabar em oh_no
porque i = 1 chama o oh_no função, que você pode entrar,
que vai definir char ** s = a NULL e imediatamente "BOOM".
E então, na verdade, olhando para a implementação de buggy2,
isso, eu apenas está obtendo um número aleatório - 0, 1, ou 2 - chamado binky,
que se eu for 0 ela chama oh_no, senão ele chama dinky, que vem até aqui.
Se eu for 1, chamada oh_no, senão chamar slinky, que chegando aqui,
se i é 2, chame oh_no.
Eu nem acho que há um caminho -
Alguém vê uma maneira de fazer isso de um programa que não vai segfault?
Porque a menos que eu estou faltando alguma coisa, se eu for 0, você imediatamente segfault,
outra coisa que você ir a uma função que se i é um que você segfault,
outra coisa que você ir para uma função onde se i é 2, você segfault.
Portanto, não importa o que você faça, você segfault.
>> Eu acho que uma maneira de corrigi-lo seria em vez de fazer char ** s = NULL,
você poderia malloc espaço para essa cadeia.
Nós poderíamos fazer malloc (sizeof) - sizeof o que?
[Aluno] (char) * 5? >> Isto parece certo?
Eu estou supondo que isso vai funcionar se eu realmente ele correu, mas não é o que eu estou procurando.
Olhe para o tipo de s. Vamos adicionar * int, então int * x.
Eu faria malloc (sizeof (int)).
Ou se eu queria um conjunto de cinco, eu faria (sizeof (int) * 5);
E se eu tiver um ** int?
O que eu malloc?
[Estudante] Tamanho do ponteiro. Sim >>. (Sizeof (int *));
A mesma coisa aqui.
Eu quero (sizeof (char *));
Isso vai alocar espaço para o ponteiro que aponta para "BOOM".
Eu não preciso alocar espaço para "BOOM" em si
porque este é basicamente equivalente ao que eu disse antes
de char * x = "BOOM".
"BOOM" já existe. Acontece que existe na região de leitura de memória.
Mas ele já existe, o que significa que esta linha de código, se s é um char **,
então * s é um char * e você está definindo este char * para apontar para "BOOM".
Se eu queria copiar "BOOM" em s, então eu teria que alocar espaço para s.
Eu vou fazer * s = malloc (sizeof (char) * 5);
Por 5?
Por que não quatro? Parece que o "boom" é de 4 caracteres. >> [Aluno] O personagem nulo.
Sim. Todas as cordas vão precisar o caractere nulo.
Agora eu posso fazer algo como strcat - Qual é a função para copiar uma string?
[Aluno] cpy? >> Strcpy.
strcpy homem.
Então strcpy ou strncpy.
strncpy é um pouco mais seguro, pois você pode especificar exatamente quantos caracteres,
mas aqui não importa, porque nós sabemos.
Então strcpy e olhar nos argumentos.
O primeiro argumento é o nosso destino.
O segundo argumento é a nossa fonte.
Vamos copiar para o nosso destino * é o ponteiro "BOOM".
Por que você quer fazer isso com um strcpy em vez de apenas o que tínhamos antes
de * s = "BOOM"?
Há uma razão para que você pode querer fazer isso, mas o que é isso?
[Estudante] Se você quer mudar algo em "BOOM". Sim >>.
Agora eu posso fazer algo como s [0] = 'X';
porque os pontos s para a pilha e que o espaço na pilha que está apontando para s
é um ponteiro para mais espaço na pilha, que está armazenando "BOOM".
Portanto, este exemplar de "BOOM" está sendo armazenado na pilha.
Há tecnicamente duas cópias do "boom" no nosso programa.
Não é o primeiro que é apenas dada por esta constante string "BOOM",
ea segunda cópia de "boom", strcpy criou a cópia de "BOOM".
Mas a cópia de "BOOM" está sendo armazenado na pilha, eo heap você está livre para mudar.
A pilha não é somente leitura, o que significa que s [0]
vai deixar você alterar o valor de "BOOM".
Vai deixá-lo mudar esses personagens.
>> Perguntas?
Okay.
>> Passando para buggy3, gdb vamos buggy3.
Nós apenas executá-lo e vemos temos um segfault.
Se backtrace, existem apenas duas funções.
Se subir em nossa função principal, vemos que segfaulted nesta linha.
Então, só de olhar para esta linha, for (int linha = 0; fgets este material faz NULL não é igual;
linha + +).
Nosso quadro anterior foi chamado _IO_fgets.
Você vai ver que um monte de funções built-in C,
que quando você começa a segfault, haverá nomes de funções realmente enigmáticas
como este _IO_fgets.
Mas que vai se relacionar com esta chamada fgets.
Em algum lugar aqui dentro, estamos segmento padrão.
Se olharmos para os argumentos para fgets, podemos imprimir buffer.
Vamos imprimir como um - Oh, não.
Impressão não vai funcionar exatamente como eu quero que ele.
Vamos olhar para o programa real.
Buffer é uma matriz de caracteres. É uma matriz de caracteres de 128 caracteres.
Portanto, quando digo buffer de impressão, que vai imprimir os 128 caracteres,
Eu acho que é o que se espera.
O que eu estava procurando é imprimir o endereço do buffer,
mas que realmente não me diz muito.
Então, quando acontecer de eu dizer aqui tampão x, ele me mostra 0xbffff090,
que, se você se lembrar de mais cedo ou algum ponto, Oxbffff tende a ser uma região pilha-ish.
A pilha tende a começar em algum lugar apenas sob 0xC000.
Só de ver esse endereço, eu sei que o buffer está acontecendo na pilha.
Reiniciando o meu programa, executado, para cima, o buffer que vimos foi essa seqüência de caracteres
que são muito sentido.
Em seguida, imprimir arquivos, o que arquivo se parece?
[Aluno] nulo. Sim >>.
Arquivo é um do tipo FILE *, por isso é um ponteiro,
e o valor de ponteiro que é nula.
Então fgets vai tentar ler de que o ponteiro de forma indireta,
mas, para acessar esse ponteiro, tem que dereference-lo.
Ou, para acessar o que ele deve estar apontando para, ele dereferences ele.
Então é dereferencing um ponteiro nulo e ele segfaults.
Eu poderia ter reiniciado lá.
Se quebrar no nosso ponto principal e correr,
a primeira linha de código é char * filename = "nonexistent.txt";
Isso deve dar uma dica muito grande a respeito de porque este programa falhar.
Digitando próxima traz-me para a próxima linha, onde eu abrir este arquivo,
e então eu imediatamente entrar em nossa linha, onde uma vez eu bati seguinte, que vai segfault.
Alguém quer jogar fora uma razão pela qual pode ser segmento padrão?
[Aluno] O arquivo não existe. Sim >>.
Isto é suposto ser uma dica
que sempre que você está abrindo um arquivo que você precisa para verificar se o arquivo realmente existe.
Então, aqui, "nonexistent.txt";
Quando nome fopen para leitura, então nós precisamos dizer
if (arquivo == NULL) e dizer printf ("O arquivo não existe!"
ou - melhor ainda - filename); return 1;
Então agora vamos verificar para ver se ele é NULL
antes de realmente continuar e tentar ler a partir desse arquivo.
Nós podemos refazê-lo só para ver o que funciona.
Eu pretendia incluir uma nova linha.
Então agora nonexistent.txt não existe.
Você deve sempre verificar se há esse tipo de coisa.
Você deve sempre verificar se fopen retorna NULL.
Você deve sempre verificar para se certificar de que malloc não retorna NULL,
ou então você segfault.
>> Agora buggy4.c.
Execução. Estou descobrindo isso está à espera para entrada ou looping possivelmente infinito.
Sim, é looping infinito.
Então buggy4. Parece que estamos em loop infinito.
Podemos quebrar a principal, executar o nosso programa.
Em gdb, enquanto a abreviatura usada é inequívoca
ou abreviações especiais que eles fornecem para você,
então você pode usar n usar seguinte em vez de ter que digitar lado todo o caminho.
E agora que eu bati n uma vez, posso apenas pressione Enter para continuar próximo
em vez de ter que bater n Enter, n Enter, n Enter.
Parece que eu estou em algum tipo de loop que está assentado matriz [i] a 0.
Parece que eu nunca estou sair deste loop.
Se eu imprimir i, então eu é 2, então eu vou ir.
Vou imprimir i, i é 3, então eu vou ir.
Vou imprimir i e i é 3. Em seguida, imprimir i, i é 4.
Na verdade, a impressão sizeof (array), assim o tamanho da matriz é 20.
Mas parece que há algum comando gdb especial para ir até que algo acontece.
É como definindo uma condição de o valor da variável. Mas eu não me lembro o que é.
Então, se nós continuar -
O que você estava dizendo? O que você traz?
[Aluno] não exibir eu adicionar - >> Yeah. Então exibir eu posso ajudar.
Se nós apenas mostrar i, ele vai colocar-se aqui que o valor de i é
então eu não tenho que imprimi-lo cada vez.
Se só vai manter próximo, vemos 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5.
Alguma coisa está terrivelmente errado, e eu está sendo reposto a 0.
Olhando buggy4.c, vemos tudo o que acontece é matriz int [5];
for (i = 0; i <= sizeof (array); i + +)
array [i] = 0;
O que vemos que está errado aqui?
Como uma dica, quando eu estava fazendo o gdb buggy4 - vamos quebrar principal, run -
Eu tinha impressão sizeof (array) só para ver o que a condição é onde eu deveria finalmente sair.
Onde eu estou? Será que eu corro?
Eu não declarou ainda.
Assim imprimir sizeof (matriz) e que é 20,
que é esperado desde a minha matriz é de tamanho 5 e é de 5 números inteiros,
assim que a coisa toda deve ser de 5 * sizeof (int) bytes, onde sizeof (int) tende a ser 4.
Então, sizeof (array) é 20.
O que deve ser isso?
[Aluno] Dividido por sizeof (int). >> Sim, / sizeof (int).
Parece que ainda há um problema aqui. Eu acho que isso deve ser apenas <
já que é praticamente sempre <nunca e <=.
Agora vamos pensar sobre por que isso foi realmente quebrado.
Alguém tem palpites por que eu redefinir a 0 através de cada iteração do loop?
A única coisa aqui dentro que está acontecendo é que a matriz [i] está sendo definida como 0.
Então, de alguma forma, esta linha de código está causando a nossa int i a ser definido para 0.
[Aluno] Poderia ser porque ele está substituindo a memória dessa parte do i
quando se pensa que é o próximo elemento da matriz? >> [Bowden] Sim.
Quando vamos para além do final de nossa matriz,
espaço que de alguma forma que estamos substituindo está substituindo o valor de i.
E assim, se olharmos para buggy4, quebrar corrida, principal,
vamos imprimir o endereço de i.
Parece que é bffff124.
Agora vamos imprimir o endereço do array [0]. 110.
E sobre [1]? 114.
[2], 118.
11c, 120. array [5] é bfff124.
Então matriz [5] tem o mesmo endereço como eu, o que significa que a matriz [5] é i.
Se eles têm o mesmo endereço, eles são a mesma coisa.
Então, quando vamos definir matriz [5] a 0, estamos estabelecendo i a 0.
E se você pensar sobre isso em termos de pilha,
int i é declarada primeiro, o que significa que fica um pouco de espaço na pilha.
Então matriz [5] é atribuído, de modo que 20 bytes são alocados na pilha.
Então eu é alocada em primeiro lugar, em seguida, esses 20 bytes ficar alocado.
Então eu acontece antes de a matriz,
e por causa da forma, como eu disse na semana passada, quando tecnicamente a pilha cresce para baixo,
quando o índice de você em uma matriz, temos a garantia de que a posição 0 na matriz
sempre acontece antes da primeira posição na matriz.
Este é o tipo de como eu desenhei na semana passada.
Observe que no fundo temos o endereço 0 e no topo temos Max endereço.
A pilha está sempre crescendo para baixo.
Vamos dizer que alocar i.
Alocamos inteiro i, o que significa que vamos apenas dizer que até aqui inteiro i é alocada.
Em seguida, alocamos nosso array de 5 inteiros, o que significa que, por baixo que,
Visto que a pilha está crescendo para baixo, esses cinco inteiros ficam alocados.
Mas por causa de como matrizes funcionar, está garantido que a primeira posição na matriz
sempre tem um endereço de menos do que o segundo aspecto da matriz.
Assim posição 0 matriz sempre tem que acontecer primeiro na memória,
Considerando que a posição matriz 1 tem que acontecer depois disso
e posição da matriz 2 tem que acontecer depois disso,
o que significa que a posição 0 matriz iria acontecer em algum lugar aqui,
posição matriz 1 que aconteceria acima que
porque mover-se significa endereços mais altos desde que o endereço máxima é aqui.
Então array [0] aqui, array [1] até aqui, array [2] até aqui, matriz [3] até aqui.
Observe como antes alocados inteiro i todo o caminho até aqui,
à medida que avançamos mais e mais em nossa matriz, estamos chegando mais perto e mais perto de nosso inteiro i.
O que acontece é que a matriz [5], que é uma posição além da nossa matriz,
é exatamente onde inteiro i passou a ser alocado.
Então esse é o ponto em que acontecerá a ser bater o espaço na pilha
que foi alocada para o inteiro i, e estamos definindo que a 0.
>> É assim que funciona. Perguntas? Sim.
[Aluno] não importa. Okay.
[Aluno] Como evitar este tipo de erros?
Estes tipos de erros? Não use C como linguagem de programação.
Use uma linguagem que tem limites de matriz de verificação.
Enquanto você estiver atento, você só precisa evitar ir além dos limites de sua matriz.
[Aluno] Então aqui quando fomos passado dos limites de sua matriz -
[Bowden] É aí que as coisas começam a dar errado. >> [Aluno] Ah, ok.
Enquanto você ficar dentro da memória alocada para a matriz, você está bem.
Mas C não faz verificação de erros. Se eu fizer matriz [1000], é de bom grado apenas modificar o que acontece -
Vai para o início da matriz, então ela vai posições 1000 e depois define como 0.
Ele não faz nenhuma verificação de que ah, isso não tem realmente mil coisas nele.
1000 é muito além do que eu deveria estar mudando,
enquanto Java ou algo que você vai ter matriz de índice limites
ou índice de exceção limites.
É por isso que um monte de linguagens de alto nível tem essas coisas
onde se você ir além dos limites da matriz, você não
para que você não pode mudar as coisas de debaixo de você
e depois as coisas vão muito pior do que apenas começando uma exceção
dizendo que você foi além do fim da matriz.
[Aluno] E assim teríamos apenas mudou o <= apenas <? >> [Bowden] Yeah.
Deve ser <sizeof (array) / sizeof (int);
desde sizeof (array) é de 20, mas só queremos 5. >> [Aluno] Direito.
Mais perguntas? Okay.
>> [Estudante] Eu tenho uma pergunta. Sim >>.
[Aluno] Qual é a variável matriz real?
[Bowden] Como o que é matriz?
Array em si é um símbolo.
É apenas o endereço do início dos 20 bytes que fazem referência.
Você pode pensar nisso como um ponteiro, mas é um ponteiro constante.
Assim que as coisas são compilados, a matriz variável não existe mais.
[Aluno] Então como é que encontrar o tamanho da matriz?
Tamanho da matriz refere-se ao tamanho do bloco, que se refere a esse símbolo.
Quando eu faço algo como printf ("% p \ n", array);
vamos executá-lo.
O que eu acabei de errado?
'Matriz' matriz declarada aqui.
Ah, aqui em cima.
Clang é inteligente, e isso acontece a perceber que eu declarei a matriz como 5 elementos
mas eu estou a indexação na posição 1000.
Ele pode fazer isso porque estes são apenas constantes.
Ela só pode ir tão longe em perceber que eu estou indo além dos limites da matriz.
Mas aviso antes, quando tivemos i ser incorreta,
ela não pode determinar quantos valores eu poderia assumir,
por isso não pode determinar que eu estava indo para além do fim da matriz.
Isso é apenas Clang ser inteligente.
>> Mas agora fazer buggy4. Então o que mais estou fazendo de errado?
Implicitamente declarando função de biblioteca 'printf'.
Eu vou querer # include.
Okay. Agora correndo buggy4.
Imprimir o valor da matriz, como eu fiz aqui, imprimi-lo como um ponteiro
impressões algo que se parece com isso - bfb8805c - que é algum endereço
que é na região pilha-ish.
Matriz em si é como um ponteiro, mas não é um indicador real,
uma vez que um ponteiro normal podemos mudar.
Matriz é apenas uma constante. Os 20 blocos de memória começam em 0xbfb8805c endereço.
Então bfb8805c através deste endereço +20- ou eu acho -20 -
é toda a memória alocada para essa matriz.
Matriz, a variável em si não é armazenada em qualquer lugar.
Quando você está compilando, o compilador - aceno de mão para ele -
mas o compilador só vai usar sabe onde arranjo a ser.
Ele sabe onde começa essa matriz,
e assim ele pode sempre fazer as coisas em termos de compensações de que começo.
Ela não precisa de uma variável em si para representar matriz.
Mas quando eu faço algo como int * p = array; agora p é um ponteiro que aponta para essa matriz,
e agora p realmente existe na pilha.
Eu sou livre para mudar p. Eu posso fazer p = malloc.
Por isso, inicialmente apontou para matriz, agora aponta para algum espaço na pilha.
Eu não posso fazer malloc = array.
Se Clang é inteligente, ele vai gritar comigo logo de cara.
Na verdade, eu tenho certeza que gcc faria isso também.
Assim, o tipo de matriz 'int [5]' não é transferível.
Você não pode atribuir algo a um tipo de matriz
porque matriz é apenas uma constante.
É um símbolo que faz referência a esses 20 bytes. Eu não posso mudar isso.
>> [Aluno] E onde está o tamanho da matriz armazenada?
[Bowden] Não é armazenada em qualquer lugar. É quando ele está compilando.
Então, onde está o tamanho da matriz armazenada?
Você só pode usar sizeof (matriz) dentro da função que a matriz é declarada em si.
Então, se eu fizer alguma função, foo, e eu faço (int array [])
printf ("% d \ n", sizeof (array));
e aqui eu chamo de foo (matriz);
dentro desta função - vamos executá-lo.
Este é Clang ser inteligente novamente.
Ele está me dizendo que sizeof no parâmetro função de matriz
retornará tamanho de 'int *'.
Isso seria um erro, se não é o que eu queria que acontecesse.
Vamos realmente desligar Werror.
Aviso. Avisos estão bem.
É ainda compilará contanto que tem um aviso.
. / A.out vai imprimir 4.
O aviso que foi gerado é um indicador claro de que deu errado.
Esta matriz int só vai imprimir sizeof (int *).
Mesmo se eu colocar array [5] aqui, ele ainda está indo só para imprimir sizeof (int *).
Assim, logo que você passar para uma função, a distinção entre matrizes e ponteiros
é inexistente.
Isto acontece por ser uma matriz que foi declarado na pilha,
mas assim que passar esse valor, que 0xbf blá, blá, blá para esta função,
então este ponteiro aponta para que a matriz na pilha.
Então isso significa que sizeof só se aplica em função de que a matriz foi declarada,
o que significa que quando você está compilando esta função,
Clang quando passa por esta função, ele vê matriz é um array de int de tamanho 5.
Então ele vê sizeof (array). Bem, isso é 20.
Isso é realmente como sizeof funciona basicamente para quase todos os casos.
Sizeof não é uma função, é um operador.
Você não chama a função sizeof.
Sizeof (int), o compilador só vai traduzir isso para 4.
Entendeu? Okay.
>> [Aluno] Então, qual é a diferença entre sizeof (array) no principal e em foo?
Isso é porque nós estamos dizendo sizeof (array), que é do tipo int *,
enquanto que a matriz aqui em baixo não é do tipo int *, é uma matriz int.
>> [Aluno] Então se você tivesse o parâmetro na matriz [] em vez de matriz int *,
que isso significa que você ainda pode mudar matriz porque agora é um ponteiro?
[Bowden] Gostou? >> [Estudante] Yeah. Você pode mudar matriz dentro da função agora?
[Bowden] Você poderia mudar matriz em ambos os casos.
Em ambos os casos, você é livre para dizer array [4] = 0.
[Aluno] Mas você pode fazer ponto de matriz para algo mais?
[Bowden] Oh. Sim. Em ambos os casos - >> [estudante] Yeah.
[Bowden] A distinção entre array [] e uma matriz int *, não há nenhuma.
Você também pode obter alguns matriz multidimensional aqui
por alguma sintaxe conveniente, mas ainda é apenas um ponteiro.
Isso significa que eu sou livre para fazer o array = malloc (sizeof (int)), e agora apontam para outro lugar.
Mas, assim como a forma como isso funciona sempre e sempre,
mudar essa matriz, tornando-a apontar para algo mais
não mudar essa matriz aqui porque é uma cópia do argumento,
não é um ponteiro para esse argumento.
E, na verdade, apenas como mais uma indicação de que é exatamente o mesmo -
já vimos o que imprime matriz de impressão -
o que se imprime o endereço da matriz ou o endereço do endereço da matriz
a um desses?
Vamos ignorar este.
Okay. Isso é bom. É agora em execução. A.out /.
Matriz de impressão, depois de imprimir o endereço da matriz, são a mesma coisa.
Matriz simplesmente não existe.
Sabe quando você está imprimindo matriz, você está imprimindo o símbolo que se refere a esses 20 bytes.
Imprimir o endereço da matriz, bem, matriz não existe.
Ele não tem um endereço, por isso só imprime o endereço desses 20 bytes.
Assim que você compilar para baixo, como em seu buggy4 compilado. / A.out,
matriz é inexistente.
Ponteiros existe. Matrizes não.
Os blocos de memória que representam a matriz ainda existem,
mas a matriz variável e as variáveis ​​deste tipo não existem.
Aqueles são como as principais diferenças entre matrizes e ponteiros
são assim que fazem chamadas de funções, não há diferença.
Mas dentro da função que a própria matriz é declarada, sizeof funciona de forma diferente
desde que a impressão é o tamanho dos blocos, em vez de o tamanho do tipo,
e você não pode mudar isso, porque é um símbolo.
Impressão da coisa eo endereço da coisa imprime a mesma coisa.
E isso é muito bonito isso.
[Aluno] Poderia dizer que mais uma vez?
Eu poderia ter perdido alguma coisa.
Matriz de impressão e endereço da matriz imprime a mesma coisa,
enquanto que se você imprimir um ponteiro em relação ao endereço do ponteiro,
a única coisa que imprime o endereço do que você está apontando,
o outro imprime o endereço do ponteiro para a pilha.
Você pode alterar um ponteiro, você não pode alterar um símbolo da matriz.
E ponteiro sizeof vai imprimir o tamanho desse tipo de ponteiro.
Então, int * p sizeof (p) vai imprimir 4,
mas int array [5] print sizeof (array) vai imprimir 20.
[Estudante] Então int array [5] irá imprimir 20? Sim >>.
É por isso que dentro de buggy4 quando costumava ser sizeof (array)
este estava fazendo i <20, que não é o que queria.
Queremos i <5. >> [Aluno] Okay.
[Bowden] E então, logo que você começar a passar nas funções,
se fizemos int * p = array;
dentro desta função, podemos basicamente usar p e matriz em exatamente da mesma maneira,
excepto para o problema e sizeof o problema alterando.
Mas p [0] = 1; é o mesmo que dizer array [0] = 1;
E assim como dizemos foo (matriz), ou foo (p);
dentro da função foo, esta é a mesma chamada duas vezes.
Não existe qualquer diferença entre estas duas chamadas.
>> Todos boa sobre isso? Okay.
Temos 10 minutos.
>> Vamos tentar obter através deste programa Typer Hacker,
Neste site, que saiu no ano passado ou algo assim.
É só deveria ser como você digita aleatoriamente e imprime -
Qualquer arquivo que acontece de ter carregado é o que parece que você está digitando.
Parece que algum tipo de código do sistema operacional.
Isso é o que queremos implementar.
Você deve ter um binário executável chamado hacker_typer
que leva em um único argumento, o arquivo para "tipo de hacker."
Executando o executável deve limpar a tela
e depois imprimir um personagem do arquivo passado em cada vez que o usuário pressiona uma tecla.
Então, qualquer tecla pressionada, ele deve jogar fora e em vez imprimir um personagem a partir do arquivo
que é o argumento.
Eu vou muito bem dizer o que as coisas que vamos precisar saber são.
Mas queremos verificar a biblioteca termios.
Eu nunca usei esta biblioteca em toda minha vida, por isso tem propósitos muito mínimas.
Mas isso vai ser a biblioteca que podemos usar para jogar fora o personagem que você bateu
quando você está digitando em padrão dentro
Então hacker_typer.c, e vamos querer # include.
Olhando para a página man para termios - estou supondo que é terminal de OS ou alguma coisa -
Eu não sei como lê-lo.
Olhando para isso, ele diz para incluir esses dois arquivos, então vamos fazer isso.
>> Primeiro de tudo, nós queremos ter em um único argumento, que é o arquivo que deve abrir.
Então, o que eu quero fazer? Como posso verificar para ver que eu tenho um único argumento?
[Estudante] Se argc é igual a ele. >> [Bowden] Yeah.
Então, se (argc = 2!) Printf ("Uso:% s [arquivo para abrir]").
Portanto, agora se eu executar este sem fornecer um segundo argumento - oh, eu preciso da nova linha -
você verá que ele diz de uso:. / hacker_typer,
e, em seguida, o segundo argumento deverá ser o arquivo que eu quero abrir.
Agora, o que eu faço?
Eu quero ler a partir deste arquivo. Como faço para ler um arquivo?
[Aluno] Você abri-lo primeiro. Sim >>.
Então fopen. O que fopen parece?
[Aluno] Filename. >> [Bowden] Espectacular vai ser argv [1].
[Aluno] E então o que você quer fazer com ela, então a - >> [Bowden] Yeah.
Então, se você não se lembra, você só poderia fazer fopen homem,
onde ele vai ser um caminho const char *, onde caminho é nome de arquivo,
modo de const char *.
Se acontecer de você não se lembrar do que modo for, então você pode olhar para o modo.
Dentro de páginas de manual, o caractere de barra é o que você pode usar para pesquisar as coisas.
Então eu digitar / modo para procurar modo.
n e N são o que você pode usar para percorrer os jogos de busca.
Aqui diz que os pontos de modo argumento para uma string
começando com uma das seguintes sequências.
Então r, arquivo de texto aberto para leitura. Isso é o que nós queremos fazer.
Para ler, e eu quero guardar isso.
A coisa vai ser um arquivo *. Agora o que eu quero fazer?
Dê-me um segundo.
Okay. Agora o que eu quero fazer?
[Aluno] Verificar se é NULL. >> [Bowden] Yeah.
Toda vez que você abrir um arquivo, certifique-se que você é sucesso capaz de abri-lo.
>> Agora eu quero fazer essas coisas termios onde eu quero primeiro ler minhas configurações atuais
e salvar aqueles em alguma coisa, então eu quero mudar a minha configuração
jogar fora qualquer personagem que eu escreva,
e então eu quero atualizar essas configurações.
E depois, no final do programa, eu quero mudar de volta para as minhas definições originais.
Assim, a estrutura vai ser de termios tipo, e eu vou querer dois deles.
O primeiro vai ser o meu current_settings,
e então eles vão ser meus hacker_settings.
Primeiro, eu vou querer para salvar minhas configurações atuais,
então eu vou querer atualizar hacker_settings,
e forma, no final do meu programa, eu quero reverter para as configurações atuais.
Então salvar as configurações atuais, da maneira que funciona, nós termios homem.
Vemos que temos esse tcsetattr int, int tcgetattr.
Eu passar em um struct termios pelo seu ponteiro.
A forma como isso vai olhar é - eu já esqueceu o que a função foi chamada.
Copie e cole-o.
Então tcgetattr, então eu quero passar na estrutura que eu estou salvando as informações em,
que vai ser current_settings,
eo primeiro argumento é o descritor de arquivo para a coisa que eu quero salvar os atributos de.
O que o descritor de arquivo é é como qualquer vez que você abrir um arquivo, ele recebe um descritor de arquivo.
Quando eu fopen argv [1], torna-se um descritor de arquivo que você está fazendo referência
sempre que você deseja ler ou escrever para ele.
Esse não é o descritor de arquivo que eu quero usar aqui.
Há três descritores de arquivos que você tem por padrão,
que são padrão, saída padrão e erro padrão.
Por padrão, eu acho que é padrão em é 0, fora padrão é 1, e do erro padrão é 2.
Então, o que eu quero mudar as configurações do?
Eu quero mudar as configurações de sempre que eu bati um personagem,
Eu quero que ele jogue o personagem fora em vez de imprimi-lo na tela.
O fluxo - padrão, saída padrão ou erro padrão -
responde às coisas quando eu digitar no teclado? >> [Estudante] Standard dentro >> Yeah.
Então eu pode fazer 0 ou stdin que eu possa fazer.
Estou recebendo o current_settings de padrão dentro
>> Agora eu quero atualizar essas configurações,
Então, primeiro eu vou copiar em hacker_settings que meus current_settings são.
E como o trabalho de estruturas é que vai apenas copiar.
Isso copia todos os campos, como seria de esperar.
>> Agora eu quero atualizar alguns dos campos.
Olhando termios, você tem que ler um monte de presente
só para ver o que você gostaria de procurar,
mas as bandeiras que você vai querer olhar para são eco,
ECHO para caracteres de entrada de eco.
Primeiro quero definir - eu já esqueceu o que os campos são.
Isto é o que a estrutura parece.
Assim, os modos de entrada eu penso que nós queremos mudar.
Nós vamos olhar para a solução para se certificar de que é o que nós queremos mudar.
Queremos mudar lflag, a fim de evitar a necessidade de olhar através de todos estes.
Queremos mudar os modos locais.
Você teria que ler essa coisa toda para entender onde tudo pertence
que queremos mudar.
Mas é dentro de modos locais onde estamos indo querer mudar isso.
Então hacker_settings.cc_lmode é o que é chamado.
c_lflag.
Isto é onde nós entramos operadores bit a bit.
Nós somos o tipo de fora do tempo, mas vamos passar por isso rápido.
Isto é onde nós entramos operadores bit a bit,
onde eu acho que eu disse um tempo atrás que quando você começa a lidar com bandeiras,
você vai estar usando operador bitwise muito.
Cada bit na bandeira corresponde a algum tipo de comportamento.
Então, aqui, esta bandeira tem um monte de coisas diferentes, onde todos eles significam algo diferente.
Mas o que eu quero fazer é simplesmente desligar o pouco que corresponde ao ECHO.
Assim, para desligar isso eu faço & = ¬ ECHO.
Na verdade, eu acho que é como Techo ou algo assim. Eu só vou verificar novamente.
I pode termios lo. É só ECHO.
ECHO vai ser um único bit.
¬ ECHO vai significar todos os bits são definidos a 1, o que significa que todas as bandeiras são definidas como true
exceto para o pouco ECHO.
Ao acabar com minhas bandeiras locais, com isso, significa que todas as bandeiras que estão atualmente definidos para true
ainda vai ser definido como verdadeiro.
Se a minha bandeira ECHO é definido como verdadeiro, então este é necessariamente definida como false na bandeira ECHO.
Então esta linha de código apenas desliga a bandeira ECHO.
As outras linhas de código, vou copiá-los no interesse de tempo e depois explicá-las.
Na solução, disse ele 0.
Provavelmente é melhor dizer explicitamente stdin.
>> Repare que eu também estou fazendo ECHO | ICANON aqui.
ICANON se refere a algo separado, que significa modo canônico.
O que significa modo canônico é geralmente quando você está digitando a linha de comando,
padrão em não processar nada até que você bata nova linha.
Então, quando você GetString, você digita um monte de coisas, então você atingiu nova linha.
É quando ele é enviado a norma dentro
Esse é o padrão.
Quando eu desligar o modo canônico, agora todos os personagens só você pressionar
é o que é processado, que é geralmente um pouco mal, porque ele é lento para processar essas coisas,
é por isso que é bom para o buffer-lo em linhas inteiras.
Mas eu quero que cada personagem a ser processado
desde que eu não quero isso para me esperar para bater de nova linha
antes de processar todos os personagens que eu estive escrevendo.
Isso transforma o modo canônico.
Este material significa apenas quando ele realmente processa caracteres.
Isto significa processá-los imediatamente; assim que eu estou digitando eles, processá-los.
E esta é a função que está a actualizar as minhas configurações para padrão em,
e meios TCSA fazê-lo agora.
As outras opções são esperar até tudo o que está atualmente no fluxo é processado.
Isso realmente não importa.
Só agora alterar as configurações para ser o que está atualmente em hacker_typer_settings.
Eu acho que o chamou hacker_settings, então vamos mudar isso.
Mudar tudo para hacker_settings.
>> Agora no final do nosso programa vamos querer reverter
para o que existe actualmente no interior de normal_settings,
que vai apenas olhar como e normal_settings.
Repare que eu não mudei nenhum dos meus normal_settings uma vez que originalmente conseguir.
Em seguida, apenas para mudá-los de volta, eu passá-los de volta no final.
Esta foi a atualização. Okay.
>> Agora aqui dentro eu vou explicar o código no interesse de tempo.
Não é que muito código.
Vemos, lemos um personagem a partir do arquivo. Nós o chamamos de f.
Agora você pode fgetc homem, mas como fgetc vai funcionar
é apenas que vai retornar o caractere que você acabou de ler ou EOF,
que corresponde ao fim do ficheiro de algum acontecimento ou de erro.
Estamos looping, continuando a ler um único caractere do arquivo,
até que nós funcionamos fora de caracteres para ler.
E enquanto nós estamos fazendo isso, esperamos em um único caractere de padrão dentro
Cada vez que você digitar algo na linha de comando,
que está lendo em um personagem de padrão dentro
Então putchar só vai colocar o char lemos aqui em cima do arquivo para a saída padrão.
Você pode putchar homem, mas é só colocar para a saída padrão, é a impressão que o personagem.
Você também pode apenas fazer printf ("% c", c); mesma idéia.
Isso vai fazer a maior parte do nosso trabalho.
>> A última coisa que você vai querer fazer é apenas fclose nosso arquivo.
Se você não fclose, que é um vazamento de memória.
Queremos fclose o arquivo que originalmente aberto, e eu acho que é isso.
Se fizermos isso, eu já tenho problemas.
Vamos ver.
O que foi reclamar?
Esperado 'int' mas o argumento é do tipo 'struct _IO_FILE *'.
Vamos ver se isso funciona.
Apenas permitido em C99. Augh. Ok, fazer hacker_typer.
Agora temos descrições mais úteis.
Portanto, use de identificador não declarado "normal_settings '.
Eu não chamá-lo normal_settings. Chamei-current_settings.
Então, vamos mudar tudo isso.
Agora passando argumento.
Eu vou fazer isso 0 para agora.
Okay. . / Hacker_typer cp.c.
Eu também não limpar a tela no começo.
Mas você pode olhar para trás para o conjunto de problemas última para ver como você limpar a tela.
É só imprimir alguns caracteres
enquanto isso está fazendo o que eu quero fazer.
Okay.
E pensando sobre por que isso precisava ser 0 em vez de stdin,
que deve ser # define 0,
este se queixa de que -
Antes, quando eu disse que não há descritores de arquivos, mas então você também tem o seu FILE *,
um descritor de arquivo é apenas um único número inteiro,
enquanto que um FILE * tem um monte de coisas associado a ele.
A razão que nós precisamos dizer 0 em vez de stdin
é que stdin é um FILE * que aponta para a coisa que é referência descritor de arquivo 0.
Assim, mesmo aqui, quando eu faço fopen (argv [1], eu estou ficando um * arquivo de volta.
Mas em algum lugar que * FILE é uma coisa correspondente ao descritor de arquivo para o arquivo.
Se você olhar para a página do manual para abrir, então eu acho que você vai ter que fazer o homem aberto 3 - nope -
homem aberto 2 - sim.
Se você olhar para a página para aberto, aberto é como um fopen de nível inferior,
e ele está retornando o descritor de arquivo real.
fopen faz um monte de coisas em cima de aberto,
que ao invés de retornar apenas descritor de arquivo que retorna um arquivo inteiro ponteiro *
dentro do que é o nosso descritor de arquivo pequeno.
Então padrão em se refere à coisa * FILE,
Considerando 0 refere-se apenas um padrão descritor do arquivo em si.
>> Perguntas?
[Risos] Blew por isso.
Tudo bem. Estamos a fazer. [Risos]
>> [CS50.TV]