DAVID MALAN: Tudo bem, bem-vindo de volta. Isto é CS50. Este é o início da semana de sete. Por isso, já faz um tempo, então pensei que tinha fazer um passeio turbilhão de onde nós parou e para onde estamos indo agora. Então, essa coisa aqui pode ter causou alguma angústia em primeiro lugar. Mas espero que, você está começando a aclimatar com o que isso denota aqui - estrela que representa um ponteiro, o qual é o que, em termos mais leigos? Portanto, é um endereço. Portanto, é o endereço de algo em memória. E começamos a descascar as camadas um par de semanas atrás, coisas como GetString e outras funções tais todo esse tempo foram retornando endereços de coisas na memória, como o endereço do primeiro caractere alguma seqüência. Então, nós também introduziu valgrind, que você vai começar a usar para este problema definido, em particular para a próxima conjunto de problemas também. E valgrind faz o que para nós? Ele verifica se há vazamentos de memória, e também verifica a existência de abuso de memória. É possível, com alguma probabilidade, se detectar seu código vai tocar de memória que simplesmente não deveria. Então, não necessariamente uma fuga, mas se você ir além das fronteiras de alguns array, e você realmente executar valgrind e induzir a que o comportamento enquanto valgrind está sendo executado em seu programa rodando dentro dele, você vai ter mensagens como esta - "inválido de escrever tamanho 4 ", que, lembre-se de um par de semanas atrás significava que eu tinha acidentalmente procura de um int demasiado além dos limites de um array. E assim tamanho 4 significa aqui o tamanho de que int particular. Então dê confiança no fato de que saída do valgrind, o formato do mesmo, é simplesmente atroz. É realmente difícil de ver através da bagunça pela informação interessante. Então, o que temos feito aqui é apenas trecho alguns dos pares de mais linhas interessantes. Mas percebemos que 80% dos valgrind de saída vai ser um pouco de distração. Basta olhar para os padrões como estes - inválido direita, inválido ler, 40 bytes e um certo número de blocos são definitivamente perdido, palavras-chave como essa. E o que você espera ver alguma tipo de vestígio de que a função do erro é realmente dentro Neste caso, aqui, no qual a linha de meu código era aparentemente o erro? 26 em um arquivo chamado memory.c, que era o exemplo que estavam brincando com no momento. Portanto, não é, provavelmente, em malloc. Foi provavelmente no meu código em seu lugar. Então, vamos ver isso de novo e novamente em pouco tempo. Então scanf, esta surgiu em um algumas formas até agora. Vimos sscanf brevemente. Era algo que uma série de você mergulhou em sua preparativos para o teste. E scanf é realmente o que o CS50 biblioteca está usando por baixo da capa por algum tempo, a fim para obter a entrada do usuário. Por exemplo, se eu passar para o CS50 aparelho aqui, deixe-me abrir uma exemplo, hoje, que é chamado de scanf-0.C E é super simples. É apenas algumas linhas de código. Mas ele demonstra realmente como getInt vem trabalhando todo esse tempo. Neste programa aqui, na linha 16 , Repare que eu declarar um int. Assim, não ponteiros, nada de mágico lá, apenas um int. Em seguida, na linha 17, que solicitará ao usuário para um número, por favor. Então, no final de 18, eu uso o scanf aqui. E eu especifiquei, tipo como printf, que eu estou esperando Citação unquote por cento i. Assim por cento de i, é claro, denota um int. Mas note que a segunda argumento de scanf é. Como você descreveria o segundo argumento depois da vírgula? O que é isso? É o endereço de x. Então, isso é útil porque fornecendo scanf com o endereço de x, o que faz que capacitam essa função para fazer? Não é só ir lá, mas também fazer o quê? Fazer uma mudança para ela. Porque você pode ir lá, é uma espécie de como um mapa para a localização na memória. E assim, desde que você forneça scanf, ou qualquer função com um tal mapa, isto função pode ir lá, e não só olhar para o valor, mas também pode alterar esse valor, o que é útil quando a propósito na vida de scanf é digitalizar a entrada do utilizador, especificamente a partir do teclado. E o f denota formatado, assim como printf, o f denota uma formatado string que você deseja imprimir. Assim, em breve, essa linha 18 simplesmente diz: tente ler um int do usuário do teclado e armazená-lo dentro de x, em qualquer endereço x acontece a viver em. E então, finalmente, a linha 19 apenas diz: obrigado pela int, neste caso. Então deixe-me ir em frente e fazer isso. Então faça scanf 0. Deixe me ir em frente e zoom in Vou correr com esta pontos cortar scanf 0. Número, por favor? 50. Obrigado pela 50. Por isso, é muito simples. Agora, o que é que não está fazendo? Ele não está fazendo um grupo inteiro de verificação de erros. Por exemplo, se eu não cooperar, e eu não digitar um número, mas em vez disso, escrever algo como "Olá", isso é apenas uma espécie de estranho. E assim, uma das coisas que o CS50 biblioteca tem vindo a fazer para nós por algum tempo é que reprompting e reprompting. A nova tentativa frase foi retirada em cs50.c, e essa é a razão que getInt em a biblioteca CS50 é realmente um conjunto bando de longas filas, porque nós somos verificação de coisas estúpidas como esta. Será que o usuário não dar nós, na verdade, um int? Será que ele ou ela nos dar algo como uma letra alfabética? Se assim for, queremos detectar isso e gritar com eles. Mas as coisas ficam mais interessantes Neste próximo exemplo. Se eu for para scanf-1.c, que é a única coisa que mudou fundamentalmente em Neste próximo exemplo? Estou usando char *, é claro, em vez de int. Então, isso é interessante, porque char *, recordo, é realmente apenas o mesma coisa que a string. Então, parece que talvez isso seja um super implementação simples de GetString. Mas eu desmascarado a camada da biblioteca CS50, por isso estou chamando este char * agora. Então vamos ver onde, se em qualquer lugar, nós erramos. Linha 17 - Volto a dizer, por favor, me dar alguma coisa, neste caso, uma cadeia de caracteres. E, em seguida, na linha seguinte, eu chamo scanf, mais uma vez, dando-lhe um código de formato, mas desta vez por cento s. E, em seguida, desta vez, eu sou dando-lhe tampão. Agora note, eu não estou usando o comercial. Mas por que é que, provavelmente, OK aqui? Porque o que é já tampão? Já é um ponteiro. Já é um endereço. E vamos a palavra "confundir", deixe-me basta chamá-lo s, por exemplo, para simplicidade. Mas eu chamei-tampão porque em geral, na programação, se você tiver um pedaço de memória, que uma seqüência realmente apenas é, você pode chamá-lo de um buffer. É um lugar para armazenar informações. Semelhante a coisas como o YouTube, quando eles estão de tamponamento, por assim dizer, que significa apenas que ele está baixando pedaços de a internet e armazená-los em um array local, um pedaço locais de memória para que você pode vê-lo mais tarde, sem ele pular ou pendurado você durante a reprodução. Portanto, há um problema aqui, porém, porque eu estou dizendo scanf, esperar um corda do usuário. Aqui está o endereço do um pedaço de memória. Coloque a corda lá. Por que é que bound dar nos problemas, embora? O que é isso? Tenho permissão para acessar que parte da memória? Você sabe, eu não sei. Porque tem tampão foi inicializado para alguma coisa? Não é verdade. E por isso é que temos vindo a chamar um valor de lixo, que não é uma palavra formal. Significa apenas que nós não temos nenhuma idéia do que pedaços estão dentro das quatro bytes Tenho alocado como buffer. Eu não chamei malloc. Eu definitivamente não é chamado GetString. Então, quem sabe o que é realmente dentro de tampão de? E ainda dizendo scanf cegamente, vai lá e colocar o que o usuário digitou. Então, o que é susceptível de causar em nosso código, se executá-lo? Provavelmente um segfault. Talvez não, mas provavelmente um segfault. E eu digo talvez não, porque às vezes você faz, às vezes você não tem um segfault. Às vezes você apenas ter sorte, mas é mesmo assim vai ser um bug no nosso programa. Então deixe-me ir em frente e compilar isso. Eu vou fazer do jeito old school. Então bumbum traço 0, scanf-1, scanf-1.c, Enter. Opa, escola muito antiga. Vamos ver. Onde é que eu vou? Oh, buffer de char *. Oh, thank you - Salvar, OK - escola muito antiga. Tudo bem, isso já faz um tempo. Então, eu acabei de salvar o arquivo depois fazendo que temporária mudar de um momento atrás. E agora eu compilei ele manualmente com Clang. E agora eu estou indo para ir em frente e executar scanf-1, Enter. Cordas favor. Eu vou digitar "Olá". E agora, aqui está em que, francamente, printf pode é um pouco irritante. Não está indo realmente para segfault neste caso. Printf é um pouco especial, porque é tão super comumente utilizado, que essencialmente printf está fazendo -nos um favor e perceber, isso não é um ponteiro válido. Deixe-me levá-la a mim apenas para imprimir em parênteses nulo, mesmo embora não seja necessariamente o que que nos esperava. Portanto, não podemos muito facilmente induzir um segfault com isso, mas claramente este não é o comportamento que eu queria. Então, qual é a solução mais simples? Bem, em scanf-2, deixe-me propor que em vez de verdade, apenas a atribuição de uma char *, deixe-me ser um pouco mais esperto sobre isso, e deixe-me alocar buffer como uma seqüência de 16 caracteres. Então, eu posso fazer isso de duas maneiras. Eu absolutamente poderia usar malloc. Mas eu posso voltar a duas semanas, quando Eu só precisava de um monte de caracteres. Isso é apenas uma matriz. Então me deixe em vez redefinir tampão a ser uma matriz de 16 caracteres. E agora, quando eu passar tampão in - e isso é algo que não fez falar em duas semanas - mas você pode tratar uma variedade de mas é um endereço. Tecnicamente, como vimos, eles são um pouco diferente. Mas scanf não vai se importar se você passá-lo o nome de um array, porque o que Clang fará por nós é essencialmente tratar o nome do array como o endereço do bloco de 16 bytes. Portanto, este é melhor. Isto significa que agora que eu espero que possa faça o seguinte. Deixe-me afastar por um momento e fazem scanf-2, compilada OK. Agora, deixe-me que tenho barra scanf-2. Cordas favor. "Olá". E parecia estar a trabalhar neste momento. Mas alguém pode propor um cenário em que não pode ainda funciona? Sim? Algo mais do que 16 caracteres. E, na verdade, podemos ser um pouco mais precisa. Algo mais então 15 personagens, porque realmente precisamos ter em mente que precisamos que barra invertida de zero implicitamente no final da cadeia, que é um aparte scanf irá tipicamente cuidar para nós. Então deixe-me fazer algo assim - Às vezes, podemos apenas deixá-lo assim. OK, então nós agora induzido nossa falha de segmentação. Por quê? Porque eu digitei a mais de 15 caracteres, e por isso nós realmente memória tocado que eu realmente não deveria ter. Então, qual é realmente a solução aqui? Bem, e se precisamos de uma seqüência mais longa? Bem, talvez torná-lo 32 bytes. Bem, e se isso não é o suficiente? Como cerca de 64 bytes? E se isso não é o suficiente? Como cerca de 128 bytes ou 200? O que é realmente a solução aqui no caso geral, se não sabemos em avançar o que o usuário vai digitar? É apenas uma espécie de grande dor na bunda, para ser honesto, é por isso que o Biblioteca CS50 tem algumas dezenas de linhas de código que implementar coletivamente GetString cordas de uma forma que nós não tem que saber com antecedência o que o de usuário vai digitar. Em particular, se você olhar para trás, cs50.c de duas semanas atrás, você vai ver GetString que realmente faz Não utilizar scanf desta maneira. Em vez disso, ele lê um caractere de cada vez. Porque a única coisa agradável sobre leitura de um personagem é o que pudermos nós garantimos para sempre ter pelo menos um caractere. Eu só posso declarar um char, e em seguida, tomar estes passos verdadeiramente bebê para apenas leia um personagem em um tempo a partir do teclado. E então, o que você vai ver GetString faz é cada vez ficar sem, digamos, 16 bytes de memória, ele usa malloc, ou um seu primo, a alocar mais memória, copiando o antigo memória para o novo, e, em seguida, rastejando junto, obtendo um personagem de cada vez, e quando ele é executado fora do que pedaço de memória, joga fora, agarra uma fatia maior de memória, copia velho em novos e repete. E é realmente uma dor de verdade implementar algo tão simples como se a entrada de um usuário. Então você pode usar scanf. Você pode usar outras funções semelhantes. E um monte de livros e on-line exemplos fazer, mas todos eles são vulnerável a problemas como este. E finalmente, se um segfault é meio chato. Não é bom para o usuário. Mas, na pior das hipóteses, o que faz fundamentalmente coloque o codificar em risco de? Algum tipo de ataque, potencialmente. Nós conversamos sobre um tal ataque - transbordamento da pilha. Mas, em geral, se você está autorizado a estourar um buffer, como nós fizemos um Algumas semanas atrás, com apenas escrever mais do que "Olá" na pilha, você pode realmente assumir, potencialmente, um computador, ou pelo menos começar a dados não pertence a você. Então, em suma, é por isso que temos essas rodinhas. Mas, agora, começamos a tirá-las, como os nossos programas não precisa mais, necessariamente, a entrada do utilizador. Mas, no caso de problema de definir seis, sua contribuição virá de uma enorme arquivo de dicionário com 150 alguns ímpares mil palavras. Assim, você não terá que se preocupar com entrada arbitrária do utilizador. Nós vamos dar-lhe algumas suposições sobre esse arquivo. Qualquer dúvida sobre ponteiros ou scanf ou a entrada do usuário em geral? Tudo bem, então um olhar rápido, em seguida, em um fuga tópico de duas semanas atrás. E que era essa noção de um struct. Não que - esta noção de um struct, o que era o quê? O que struct fazer por nós? Definir - arrependido? Definir um tipo de variável. Então, mais ou menos. Na verdade, estamos combinando dois tópicos. Assim, com typedef, lembre-se que podemos declarar um tipo de nossa própria, como um sinônimo, como string para char *. Mas o uso de typedef e struct, podemos realmente criar nossas próprias estruturas de dados. Por exemplo, se eu voltar para o gedit aqui só por um momento, e eu ir em frente e fazer algo assim, deixe-me salvar isso como, digamos, structs.c temporariamente, eu só vou para ir em frente e incluem standardio.h, int void main. E então, aqui, vamos supor que eu quero para escrever um programa que armazena vários alunos de vários casas, por exemplo. Então, é como um registrarial banco de dados de algum tipo. Então, se eu preciso do nome de um estudante, eu poderia fazer algo parecido com o nome de char *, e eu vou fazer algo parecido - na verdade, vamos usar a biblioteca CS50 por apenas um momento para fazer deste um pouco mais simples, para que possamos emprestar essas dezenas de linhas de código. E vamos mantê-lo simples. Vamos mantê-lo string, e agora GetString. Então eu afirmo agora que eu tenho guardado o nome de algum aluno, ea casa de algum aluno, simplesmente usando variáveis como fizemos e em uma semana. Mas suponho que agora querem apoiar vários alunos. Tudo bem, então meus instintos estão a fazer corda nome2, fica GetString, corda house2 fica GetString. E então o nosso terceiro estudante, vamos fazer name3 GetString. Certo, então isso é sorte impressionante lo como uma espécie de idiota, porque este processo é realmente nunca vai acabar, e ela só vai fazer o meu código parece pior e pior e pior. Mas nós resolvemos isso também em duas semanas. Qual foi a nossa solução relativamente limpa quando tivemos múltiplas variáveis ​​do mesmo tipo de dados que estão relacionados, mas nós não queremos essa confusão atroz de variáveis ​​de nome semelhante? O que vamos fazer em vez disso? Então, eu acho que eu ouvi alguns lugares. Tivemos uma matriz. Se você quiser várias instâncias do alguma coisa, por que não vamos limpar isso tudo e apenas dizer, me dar array chamado nomes? E, por enquanto, vamos 3 código rígido. E então me dar outro array chamados casas, e deixe-me por agora rígido código 3. E eu maciçamente limpou o bagunça que acabou de criar. Agora, eu ainda codificado 3, mas mesmo o 3 poderia vir dinamicamente a partir do utilizador, ou argv, ou semelhantes. Então, isso já é mais limpo. Mas o que é irritante sobre isso é que agora, mesmo que um nome é de alguma forma fundamentalmente ligada à casa de um aluno - é um estudante que eu realmente deseja representar - Agora tenho duas matrizes que são paralelas no sentido de que eles são os mesmo tamanho, e os nomes de suporte 0 presumivelmente mapas para casas suporte 0, e os nomes de Suporte 1 mapas às casas suporte 1. Em outras palavras, que o aluno vive em aquela casa, e que outro aluno que vive em outra casa. Mas, certamente, isso poderia ser feito ainda mais limpa. Bem, isso pode, de fato. E deixe-me ir em frente e abrir se structs.h, e você vai veja essa idéia aqui. Repare que eu usei typedef, como você aludiu há pouco para declarar o nosso tipo de dados próprio. Mas eu também estou usando essa outra palavra-chave chamado struct que me dá uma nova estrutura de dados. E essa estrutura de dados que afirmam que vai ter duas coisas no interior da - uma string chamada nome, e uma série chamada casa. E o nome que eu vou dar para esta estrutura de dados que vai a ser chamado de estudante. Eu poderia chamá-lo de qualquer coisa que eu quero, mas semanticamente fazer sentido para mim em minha mente. Então, agora, se eu abrir uma versão melhor do programa comecei a escrever lá, deixe-me ir até o topo. E há mais algumas linhas de código aqui, mas deixe-me concentrar para o momento em um. Eu já declarou uma constante chamada estudantes e codificado 3 por enquanto. Mas agora, observe como limpo meu código começa a ficar. Na linha 22, declaro conjunto de alunos. E notem que o aluno é, aparentemente, agora um tipo de dados. Porque no início deste arquivo, observe Eu incluí o arquivo de cabeçalho que eu puxei apenas um momento atrás. E esse arquivo de cabeçalho simplesmente tinha esta definição de um estudante. Então, agora, eu criei meus próprios dados personalizados tipo que os autores do C anos atrás não pensar com antecedência. Mas não há problema. Eu posso fazer isso sozinho. Portanto, esta é uma matriz chamada estudantes, cada um dos membros cujos é uma estrutura do estudante. E eu quero que três desses na matriz. E agora, o que faz o resto deste programa faz? Eu precisava de algo um pouco arbitrária. Assim, a partir online 24 em diante, Eu iterar de 0 a 3. Eu, então, pedir ao usuário para o nome do aluno. E então eu uso GetString como antes. Então eu pergunto para a casa do aluno, e eu uso GetString como antes. Mas aviso - um pouco nova pedaço de sintaxe - Ainda posso índice para o i-th estudante, mas como faço para obter os dados específicos campo dentro da estrutura? Bem, o que é, aparentemente, a nova peça de sintaxe? É apenas o operador ponto. Nós não temos realmente visto isso antes. Você já vi isso em pset cinco, se você tem mergulhou já com arquivos de bitmap. Mas o ponto significa apenas dentro deste struct ou vários campos, dar ponto nome, ou me dar dot casa. Isso significa ir para dentro da estrutura e obter os campos específicos. O que faz o resto deste programa faz? Não é tudo o que sexy. Observe que uma iteração de 0 a 3, novamente, e eu simplesmente criar um Inglês frase como fulano de tal está em tal e tal casa, passando em nome de ponto de o i-th aluno e sua casa também. E então, finalmente, agora vamos começar a ter anal com isso, agora que estamos familiarizado com o que malloc e outras funções tenham sido fazendo todo esse tempo. Por que eu tenho de libertar tanto o nome ea casa, apesar de eu não chamar malloc? GetString fez. E esse foi o pequeno segredo sujo para várias semanas, mas tem GetString sido vazamento de memória em todo o colocar todo o semestre até o momento. E Valgrand vai finalmente revelar isso para nós. Mas não é um grande negócio, porque eu sei que eu posso simplesmente liberar o nome ea casa, apesar de, tecnicamente, a ser super, super seguro, eu deveria ser fazer alguma verificação de erros aqui. Quais são os seus instintos lhe dizendo? O que devo estar verificando antes de liberar o que é um string, aka que um char *? Eu realmente deveria estar verificando se os alunos suporte i nome dot não igual nulo. Em seguida, ele vai ficar bem para ir em frente e livre esse ponteiro, e o mesmo ou outro um bem. Se os alunos suporte i dot casa não é igual a zero, isso agora vai proteger contra o canto caso em que GetString retorna algo como nulo. E vimos há pouco, printf vai proteger-nos aqui apenas dizendo nulo, o que vai ficar estranho. Mas pelo menos ele não vai segfault, como já vimos. Bem, deixe me fazer uma outra coisa aqui. structs-0 é um tipo de programa estúpido porque eu entro todos esses dados e, em seguida, ele perdeu uma vez o programa termina. Mas deixe-me ir em frente e fazer isso. Deixe-me fazer o terminal janela um pouco maior. Deixe-me fazer-estruturas 1, que é uma nova versão deste. Vou ampliar um pouco. E agora deixe-me correr dot reduzir structs-1. Nome do aluno - David Mather, vamos fazer Rob Kirkland, vamos fazer Lauren Leverett. O que é interessante agora é prévio - e eu só sei disso porque Eu escrevi o programa - há um arquivo agora na minha atual diretório chamado students.csv. Alguns de vocês podem ter visto estas no mundo real. O que é um arquivo CSV? Valores separados por vírgulas. É como uma espécie de homem pobre versão de um arquivo Excel. É uma tabela de linhas e colunas que você pode abrir em um programa como o Excel, ou os números em um Mac. E se eu abrir esse arquivo aqui no gedit, prévio - e os números não estão lá. Isso é apenas gedit dizendo me os números das linhas. Repare na primeira linha deste arquivo é David e Mather. A próxima linha é Rob vírgula Kirkland. E a terceira linha é Lauren vírgula Leverett. Então, o que eu criei? Eu já escrevi um programa em C que efetivamente pode gerar planilhas que pode ser aberto numa programa como o Excel. Nem tudo o que obrigar um conjunto de dados, mas se você tem pedaços muito maiores de dados que você realmente quer manipular e fazer gráficos de e gosto, este é talvez um maneira de criar esses dados. Além disso, CSVs são realmente super-comum apenas para armazenar dados simples - Yahoo Finance, por exemplo, se você receber cotações de ações através de seu chamado API, o serviço gratuito que permite que você esteja em stock, up-to-the-data atual citações para as empresas, que dar os dados de volta no Super formato CSV simples. Então, como vamos fazer isso? Bem notar, a maior parte deste programa de quase a mesma. Mas note-se aqui, ao invés de impressão os alunos para fora, na linha 35 em diante, eu afirmo que estou guardando o estudantes em disco, para salvar um arquivo. Então, repare que eu estou declarando um FILE * - Agora, isso é uma espécie de anomalia no C. Por alguma razão, o FILE é todas as tampas, o que não é como a maioria dos outros tipos de dados em C. Mas este é um built-in tipo de dados, FILE *. E eu estou declarando um ponteiro para um arquivo, é como você pode pensar isso. fopen significa arquivo aberto. O arquivo que você quer abrir? Eu quero abrir um arquivo que eu vou arbitrariamente chamar students.csv. Eu poderia chamar isso de qualquer coisa que eu quiser. E, em seguida, dar um palpite. O que faz o segundo argumento para fopen provavelmente significa? Certo, w para escrever, poderia ser r para leitura. Há uma para anexar se deseja adicionar linhas e não substituir a coisa toda. Mas eu só quero criar este arquivo uma vez, por isso vou usar citação unquote w. E eu sei que só de ter lido a documentação, ou a página de manual. Se o arquivo não é nulo - em outras palavras, Se nada deu errado lá - deixe-me repetir ao longo do os alunos de 0 a 3. E agora percebe que há algo sempre assim ligeiramente diferente sobre a linha 41 aqui. Não é printf. É fprintf para o arquivo printf. Então, ele vai gravar no arquivo. Que arquivo? Aquele cujo ponteiro que você especifique como o primeiro argumento. Então nós especificar uma seqüência de formato. Então nós especificamos o que queremos cadeia plug-in para o primeiro por cento s, e em seguida, uma outra variável ou o segundo por cento s. Depois, feche o arquivo com fclose. Que eu liberar a memória como antes, embora Eu deveria voltar e adicionar algumas verificações para null. E é isso. fopen, fprintf, fclose me dá a capacidade de criar arquivos de texto. Agora, você vai ver problema em conjunto de cinco, que envolve imagens, que você vai usar arquivos binários vez. Mas, fundamentalmente, a idéia é a mesma, mesmo que as funções que você vai ver é um pouco diferente. Então rápido tour, mas você vai ter muito familiarizado com o arquivo I/O-- entrada e saída - com pset cinco. E qualquer dúvida sobre a fundamentos iniciais aqui? Sim? E se você tentar libertar um valor nulo? Eu acredito que, a menos que tenha obtido a livre pouco mais user-friendly, você pode potencialmente segfault. Passando-o nulo é ruim porque eu não acreditam livre preocupa em verificar para você, porque seria potencialmente um desperdício de tempo para que ele faça a si mesmo para todos no mundo. Boa pergunta, no entanto. Tudo bem, então esse tipo de fica nos a um tema interessante. O tema do conjunto de problemas cinco é forense. Pelo menos essa é a parte do conjunto de problemas. Forensics geralmente se refere à recuperação de informações que podem ou não pode ter sido excluído deliberadamente. E então eu pensei que eu iria dar-lhe um rápido gosto do que realmente está acontecendo em todos os desta vez sob o capa de seu computador. Por exemplo, se você tem dentro do seu laptop ou computador de mesa a disco rígido, ou é um mecânico dispositivo que realmente gira - há coisas circulares chamados platters que parece muito com o que eu só tinha acima na tela aqui, embora esta é a escola cada vez mais velho. Esta é uma de três e meia polegadas disco rígido. E três centímetros e meio refere-se de com a coisa quando você instalá-lo em um computador. Muitos de vocês em seus laptops agora têm drives de estado sólido, ou SSD, que não têm partes móveis. Eles são mais como RAM e menos como estes dispositivos mecânicos. Mas as idéias ainda são os mesmos, certamente como eles se relacionam para o problema de definir cinco. E se você pensar agora um disco rígido representa ser um círculo, que Eu vou desenhar como este aqui. Quando você cria um arquivo no seu computador, se é um SSD, ou em Neste caso, uma antiga escola de disco rígido, esse arquivo é composto por vários bits. Vamos dizer que é este 0 e 1, um monte de 0s e 1s. Portanto, este é o meu disco rígido inteiro. Isto é, aparentemente, um arquivo muito grande. E ele está usando a 0s e 1s em que porção do prato física. Bem, o que é a parte física? Bem, acontece que em um disco rígido, pelo menos deste tipo, não há essas minúsculas partículas magnéticas. E eles têm essencialmente norte e pólos sul para eles, de modo que se você transformar uma dessas partículas magnéticas Desta forma, pode-se dizer que é representando um 1. E se está de cabeça para baixo para o sul para norte, você poderia dizer que é representa um 0. Assim, no mundo físico real, que é como você pode representar algo em estado binário de 0 a 1 e um. Então, isso é tudo que um arquivo é. Há um monte de magnético partículas que são deste modo ou sua Desta forma, a criação de padrões de 0s e 1s. Mas acontece quando você salvar um arquivo, algumas informações são salvas separadamente. Portanto, esta é uma pequena mesa, um diretório, por assim dizer. E eu vou chamar este nome de coluna e Vou ligar para este local coluna. E eu vou dizer, suponha este é o meu currículo. Meu RESUME.DOC é armazenado a localização, vamos dizer 123. Eu sempre vou para esse número. Mas basta dizer que, assim como na memória RAM, você pode levar um disco rígido isso é um gigabyte ou 200 gigabytes ou um terabyte, e você pode número de todos os bytes. Você pode enumerar todos os blocos de 8 bits. Então, vamos dizer que este é a localização 123. Portanto, este diretório dentro da minha operação sistema lembra que o meu currículo é no local 123. Mas fica interessante quando você apaga um arquivo. Assim, por exemplo - e, felizmente, a maior parte do mundo tem peguei para isso - o que acontece quando você arrastar um arquivo para seu sistema operacional Mac Trash ou a sua Lixeira do Windows? Qual é a finalidade de fazer isso? É, obviamente, para livrar-se do arquivo, mas o que faz o ato de arrastar e cair em seu lixo ou o seu Lixeira fazer em um computador? Absolutamente nada, na verdade. É como uma pasta. É uma pasta especial, para ter certeza. Mas será que isso realmente excluir o arquivo? Bem, não, porque alguns de vocês provavelmente ter sido, oh caramba, você não fez quero fazer isso. Assim que você clicar duas vezes o Trash ou Lixeira. Você já cutucou ao redor e você recuperou o arquivo apenas arrastando-o de lá. Então, claramente, não é necessariamente excluí-lo. OK, você é mais esperto do que isso. Você sabe que apenas arrastando-o para o Trash ou Lixeira não significa você esvaziar a lixeira. Então você vai até o menu, e você diz Esvaziar Lixeira ou Esvaziar Lixeira. Então o que acontece? Sim, por isso é eliminada mais. Mas tudo o que acontece é o seguinte. O computador se esquece de onde RESUME.DOC era. Mas o que não mudou, aparentemente, na foto? Os bits, os 0s e 1s que eu afirmo é no local de algum aspecto físico o hardware. Eles ainda estão lá. É apenas o computador tem esqueceu o que eles são. Portanto, é essencialmente livre do arquivo bits de modo que eles podem ser reutilizados. Mas não até que você crie mais arquivos, e mais arquivos e mais arquivos serão probabilisticamente, os 0s e 1s, aquelas partículas magnéticas, são reutilizados, de cabeça ou do lado direito para cima, para outros arquivos, 0s e 1s. Então você tem essa janela de tempo. E não é de previsível comprimento, realmente. Depende do tamanho do seu disco unidade e quantos arquivos você tem e o quão rápido você fazer novos. Mas há esta janela de tempo durante o que esse arquivo ainda é perfeitamente recuperável. Então, se você nunca usar programas como o McAfee ou Norton para tentar recuperar de dados, tudo o que estamos fazendo é tentando recuperar este chamado diretório para descobrir onde o arquivo foi. E às vezes Norton e dirá: do arquivo é de 93% recuperável. Bem, o que isso significa? Isso só significa que algum outro arquivo coincidentemente acabou usando, por exemplo, esses bits fora do seu arquivo original. Então, o que está realmente envolvido na recuperação de dados? Bem, se você não tem algo parecido Norton pré-instalado em seu computador, o melhor que você às vezes pode fazer é olhar em todo o disco rígido à procura de padrões de bits. E um dos temas do conjunto de problemas cinco é que você vai pesquisar a equivalente a um disco rígido, um forense imagem de um cartão de memória flash compacto a partir de um câmera digital, buscando a 0s 1s e que normalmente, com elevada probabilidade, representam o início de uma imagem JPEG. E vocês podem recuperar aquelas imagens por supondo que, se eu vejo esse padrão de bits na imagem forense, com elevada probabilidade, que marca o início de um JPEG. E se eu ver o mesmo padrão de novo, que provavelmente marca o início de outro JPEG, e outro JPEG, e outro JPEG. E este é tipicamente como recuperação de dados vai funcionar. O que é agradável sobre JPEGs é mesmo o formato de arquivo em si é um pouco complexo, no início de cada tal arquivo é realmente bastante identificável e simples, como você vai ver, Se você não tiver já. Então, vamos dar uma olhada embaixo o capô como a exatamente o que tem sido acontecendo, e que estes 0s e 1s são, para lhe dar um pouco mais de um contexto para este desafio particular. [REPRODUÇÃO] -Onde seu PC armazena mais dos seus dados permanentes. Para isso, os dados viajam de RAM juntamente com os sinais de software que contam o disco rígido como armazenar esses dados. Os circuitos do disco rígido traduzir esses sinais em tensão flutuações. Estes, por sua vez, controlam o disco rígido do partes móveis, alguns dos poucos partes móveis deixados no computador moderno. Alguns dos sinais de controlo de um motor que gira bandejas de metal revestidas. Seus dados são realmente armazenados sobre estas travessas. Outros sinais de mover a leitura / gravação cabeças de leitura ou gravar dados sobre os pratos. Esta máquina tão preciso que um ser humano cabelo não poderia mesmo passar entre a cabeças e pratos giratórios. No entanto, tudo funciona em velocidades incríveis. [FIM REPRODUÇÃO DE VÍDEO] DAVID MALAN: Zoom em um pouco mais profunda agora o que está realmente nesses pratos. [REPRODUÇÃO] -Vejamos o que acabamos de vi em câmera lenta. Quando um breve pulso de eletricidade é enviado para a cabeça de leitura / gravação, se vira em uma pequena electromagnética para uma fração de segundo. O ímã cria um campo, que muda a polaridade de um pequeno, minúsculo parte das partículas metálicas que casaco de cada superfície do prato. A série padrão desses pequenos, cobrada até áreas no disco representa um único bit dados no número binário sistema usado pelos computadores. Agora, se a corrente é enviada uma maneira através da leitura / escrita cabeça, a área é polarizada em uma direção. Se a corrente é enviada na sentido oposto, o polarização é invertida. Como você obter dados fora do disco rígido? Apenas reverter o processo. Por isso é que as partículas no disco que obtenha a corrente no leitura / escrita cabeça se movendo. Juntos milhões destes segmentos magnetizados e você tem um arquivo. Agora, as peças de um único arquivo pode ser espalhados por toda a unidade de pratos, como o tipo de bagunça de papéis em sua mesa. Assim, um arquivo muito especial mantém o controle de onde está tudo. Você não gostaria de ter algo assim? [FIM REPRODUÇÃO DE VÍDEO] DAVID MALAN: OK, provavelmente não. Assim como muitos de vocês Cresci com isso? OK, por isso é cada vez menos mãos a cada ano. Mas eu estou feliz que você esteja pelo menos familiarizado com eles, porque este e a nossa própria demonstração livro, infelizmente, estão morrendo muito retardar a morte aqui de familiaridade. Mas isso é o que eu, pelo menos, de volta ensino médio, o uso usada para backups. E foi incrível, porque você pode armazenar 1,4 megabytes em este disco em particular. E esta foi a versão de alta densidade, como indicado pela HD, que tem ou seja, antes de videos de hoje em HD. Densidade padrão era de 800 kilobytes. E antes disso, havia Discos de 400 kilobytes. E antes disso, havia 5 e 1/4 discos polegadas, que eram verdadeiramente flexível, e um pouco mais largo e mais alto que essas coisas aqui. Mas você pode realmente ver o chamado aspecto disquete destes discos. E funcionalmente, eles são realmente bastante semelhante ao de discos rígidos de pelo menos deste tipo. Mais uma vez, os SSDs em computadores mais novos trabalhar um pouco diferente. Mas se você se move, que guia de metal pouco, você pode realmente ver um pouco de biscoito, ou travessa. Não é de metal como este. Este é realmente um pouco mais barato material plástico. E você pode tipo de manobra-lo. E você trully apenas limpou alguns número de bits ou partículas magnéticas a partir deste disco. Então, graças a Deus, não há nada sobre ele. Se aquela coisa está no caminho - e cobrir seus olhos e os de seu vizinho - você pode apenas puxar este tipo de toda off bainha assim. Mas há uma pequena mola, que assim seja ciente de que, com os seus olhos. Então agora você tem realmente um disquete. E o que é notável sobre esta é que, na medida em que este é um representação em pequena escala de uma maior disco rígido, essas coisas são super, super simples. Se você apertar a parte inferior do mesmo, agora que que coisa de metal está fora, e descasque las abertas, tudo o que há é duas peças de feltros e o chamado disquete com uma peça de metal no interior. E lá se vai metade do conteúdo do meu disco. Lá vai outra metade deles. Mas isso é tudo o que estava girando dentro de seu computador no passado. E, novamente, para colocar isto em perspectiva, quão grande é a maioria de seu discos rígidos de hoje em dia? 500 gigabytes, um terabyte, talvez em um computador desktop, dois terabytes, 3 terabytes, 4 terabytes, certo? Este é um megabyte, mais ou menos, que não pode sequer colocar um MP3 típico digitamos mais alguns dias ou algumas arquivo de música similar. Assim, uma pequena lembrança para você hoje, e também para ajudar a contextualizar o nós vamos tomar para concedido agora no problema definir cinco. Portanto, estas são de sua propriedade. Então deixe-me transição para onde será passar a próxima pset bem. Então, nós temos agora definir esta página para - oh, um par de anúncios rapidamente. Nesta sexta-feira, se você quiser se juntar a CS50 para o almoço, vá para o lugar de costume, cs50.net/rsvp. E projeto final - então por o programa, nós postamos o especificação do projeto final já. Perceba que isso não significa que é devido particularmente em breve. Está publicado, na verdade, apenas para obter vocês pensando nisso. E, de fato, um super significativa percentagem será enfrentar projetos finais de material que nem sequer chegou a na classe, mas será já na próxima semana. Observe, porém, que a especificação exige alguns componentes diferentes do projeto final. A primeira, em algumas semanas, é um pré-proposta, um e-mail muito casual para o TF para lhe dizer ou o que você está pensando para o seu projeto, com nenhum compromisso. Proposta será seu especial compromisso, dizendo, aqui, é isso que Eu gostaria de fazer para o meu projeto. O que você acha? Muito grande? Muito pequena? É viável? E você vê a especificação para mais detalhes. Duas semanas após o que é o estado relatório, que é uma forma semelhante email casual para o seu TF para dizer o quão muito para trás você está no seu final, implementação do projeto, seguido por o CS50 Hackathon para o qual todos é convidado, que será um evento de 20:00 em uma noite até 07:00 AM na manhã seguinte. Pizza, como eu já mencionei na semana zero, wil ser servido às 9:00 horas, Comida chinesa em 1h00. E se você ainda estiver acordado à 5h00, vamos levá-lo para IHOP no café da manhã. Assim, o Hackathon é um dos mais experiências memoráveis ​​na classe. Em seguida, a implementação é devido, e em seguida, o CS50 Fair clímax. Mais detalhes sobre todos esses nas semanas que virão. Mas vamos voltar para algo escola de idade - outra vez, uma matriz. Assim, uma matriz foi bom, porque ele resolve problemas como vimos apenas um momento atrás, com estruturas estudantis ficando um pouco fora de controle se quer ter um aluno, estudante dois, estudante três, estudante dot dot dot, um número arbitrário de alunos. Então, matrizes, algumas semanas atrás, mergulhou em e resolveu todos os nossos problemas de não sabendo de antemão quantas coisas de algum tipo que pode querer. E temos visto que estruturas pode nos ajudar ainda organizar e manter o nosso código variáveis ​​conceitualmente semelhantes, como a nome e uma casa, em conjunto, de modo que podemos pode tratá-los como uma entidade, dentro dos quais existem pedaços menores. Mas matrizes têm algumas desvantagens. Quais são algumas das desvantagens nós encontramos com matrizes até agora? O que é isso? Tamanho fixo - isso mesmo que você poderia ser capaz de alocar memória para um matriz, uma vez que você sabe quantos alunos você tem, quantos caracteres você tem do usuário, uma vez que você tenha alocado a matriz, você tipo de pintado sozinho em um canto. Porque você não pode inserir novos elementos no meio de uma matriz. Você não pode inserir mais elementos no final de uma matriz. Realmente, você tem que recorrer à criação de um Toda nova matriz, como já discutido, copiando o antigo para o novo. E mais uma vez, que é a dor de cabeça que Lida com GetString para você. Mas, novamente, você não pode ainda inserir algo no meio da matriz se a taxa não está completamente cheia. Por exemplo, se essa matriz aqui de tamanho seis tem apenas cinco coisas nele, Bem, você pode apenas alinhavar algo para o efeito. Mas e se você quiser inserir algo no meio da matriz, embora possa ter cinco dos seis coisas nele? Bem, o que nós fizemos quando tivemos tudo dos nossos voluntários humanos no palco semana passado? Se quiséssemos colocar alguém aqui, também essas pessoas como mover este modo, ou essas pessoas como mover este forma, e que se tornou caro. O deslocamento de pessoas no interior de um série acabou somando e custando nos tempo, portanto, muito do nosso n ao quadrado vezes funcionando como um tipo de inserção, por exemplo, no pior dos casos. Assim, as matrizes são grandes, mas você tem que saber de antemão o quão grande você quiser. Então, bem, aqui está a solução. Se eu não saber de antemão quantos os alunos que eu poderia ter, e eu sei que uma vez Eu decido, porém, eu estou preso com que muitos estudantes, por que não eu sempre alocar o dobro do espaço como eu poderia pensar que eu preciso? Isso não é uma solução razoável? Na realidade, eu não acho que nós somos vai precisar de mais de 50 vagas em uma matriz para uma classe de tamanho médio, então vamos arredondar para cima. Vou fazer 100 slots em meu array, apenas para que possamos definitivamente o número de alunos que esperam ser, de alguma classe de tamanho médio. Então por que não reunir e alocar mais memória, tipicamente, por uma matriz que você acha que pode até mesmo precisar? O que é isso pushback simples a essa idéia? Você está apenas perdendo memória. Literalmente todos os programas que você escreve, em seguida, é talvez usando o dobro da memória como você realmente precisa. E isso só não se sente como um particularmente solução elegante. Além disso, apenas diminui o probabilidade de um problema. Se acontecer de você ter um curso popular um semestre e tem 101 estudantes, o programa ainda é fundamentalmente enfrentando o mesmo problema. Então, felizmente, há uma solução para Esse anúncio todos os nossos problemas, na forma de estruturas de dados que são mais complexos do que os temos visto até agora. Isso, eu afirmo, é uma lista encadeada. Esta é uma lista de números - 9, 17, 22, 26 e 34 - que foram ligadas entre si por forma do que eu desenhei como flechas. Em outras palavras, se eu queria representar uma matriz, que eu poderia fazer algo como isto. E eu vou colocar isso a sobrecarga em apenas um momento. Eu poderia fazer - Olá, tudo bem. Stand by. Novo computador aqui, claro - tudo bem. Então, se eu tenho esses números em ordem - 9, 17, 22, 26, 24 - não necessariamente à escala. Tudo bem, isso aqui é minha matriz - oh meu deus. Tudo bem, isso aqui é minha matriz. Oh meu deus. [Risos] DAVID MALAN: Pretend. É muito esforço para voltar e corrigir isso, então lá - 26. Então nós temos esse conjunto de 9, 17, 22, 26, e 34. Para aqueles de vocês pode ver o erro embaraçoso que acabou de fazer, aí está. Então, eu afirmo que este é um solução muito eficiente. Eu alocado como muitos ints como Eu preciso - um, dois, três, quatro, cinco ou seis - e eu então armazenados os números dentro dessa matriz. Mas vamos supor que, então, eu quero inserir um valor como o número 8? Bem, para onde ela vai? Suponha que eu queira inserir um número como 20. Bem, para onde ele vai? Em algum lugar no meio, ou o número 35 tem de passar algures no final. Mas eu estou fora do espaço. E por isso este é um desafio fundamental das matrizes que não são a solução. Afirmei há pouco, GetString resolve este problema. Se você deseja inserir um sexto número nessa matriz, o que é, pelo menos, um solução que você pode voltar a cair, com certeza, assim como fazemos com GetString? O que é isso? Bem, torná-lo maior é mais fácil de dizer do que fazer. Nós não podemos necessariamente fazer a matriz maior, mas o que podemos fazer? Faça uma nova matriz que é maior, de tamanho 6, ou talvez tamanho 10, se quisermos para chegar à frente das coisas, e então copie o array antigo para o novo, e, em seguida, libertar a antiga matriz. Mas o que é o tempo de execução agora desse processo? É grande S de n, porque a cópia vai lhe custar algumas unidades de tempo, por isso não é tão ideal, se for preciso alocar uma nova matriz, o que está acontecendo para consumir o dobro memória temporária. Copie velho em novo - Quero dizer, é só uma dor de cabeça, que é, mais uma vez, por que nós escrevemos GetString para você. Então, o que podemos fazer em vez disso? Bem, e se a nossa estrutura de dados na verdade, tem lacunas nele? Suponha que eu relaxo meu objetivo de ter blocos contíguos de memória, onde 9 é bem próximo a 17, o que é ao lado 22, e assim por diante. E suponha que 9 pode ser aqui em RAM, e 17 pode ser aqui na RAM, e 22 pode ser aqui na RAM. Em outras palavras, eu não preciso deles mesmo voltar para trás mais. Eu só tenho que de alguma forma enfiar uma agulha através de cada um desses números, ou cada desses nós, como vamos chamar o retângulos como eu desenhei-los, para lembrar de como chegar até o último tal nó do primeiro. Então o que é construir a programação vimos há pouco tempo com o qual eu pode implementar esse segmento, ou traçada aqui, com o qual eu posso implementar essas flechas? Ponteiros assim, certo? Se eu não alocar apenas um int, mas um nó - e por nó, eu só quero dizer container. E visualmente, eu quero dizer um retângulo. Assim, um nó aparentemente precisa para conter dois valores - o próprio int, e então, como se conclui da a metade inferior do retângulo, espaço suficiente para um int. Então, basta pensar no futuro aqui, quão grande é este nó, este recipiente em questão? Quantos bytes para o int? Presumivelmente, 4, se é o mesmo que o habitual. E então quantos bytes para o ponteiro? 4. Portanto, este recipiente, ou esse nó, é vai ser uma estrutura de 8 bytes. Oh, e isso é uma feliz coincidência que que acaba de introduzir a noção de uma estrutura, ou uma estrutura C. Então eu reclamar que eu quero dar um passo para isso mais sofisticado implementação de uma lista de números, um lista encadeada de números, eu preciso fazer uma pouco mais o pensamento na frente e declarar não apenas um int, mas um struct que eu ligo, convencionalmente aqui nó. Poderíamos chamá-lo de qualquer coisa que quisermos, mas nó vai ser temática em um monte das coisas que começar a olhar para agora. Dentro desse nó é um int n. E então essa sintaxe, um pouco estranho à primeira vista - struct node * seguinte. Bem pictoricamente, o que é isso? Essa é a metade inferior da o retângulo que vimos apenas um momento atrás. Mas por que estou dizendo struct node * ao invés de apenas nó *? Porque se esse ponteiro está apontando em outro nó, ele é apenas o endereço de um nó. Isso é consistente com o que nós temos discutido sobre ponteiros até o momento. Mas por que, se eu reclamar esta estrutura é chamado de nó, que eu tenho a dizer struct nó aqui dentro? Exatamente. É uma espécie de uma realidade estúpida C. O typedef, por assim dizer, não tem aconteceu ainda. C é super literal. Ele lê o seu código para cima baixo, da esquerda para a direita. E até atingir esse ponto e vírgula no linha de fundo, acho que não funciona existir como um tipo de dados? Nó, o nó entre aspas. Mas por causa da mais detalhado declaração que eu fiz na primeira linha - typedef struct node - porque que veio primeiro, antes do chaves, que é uma espécie de como pré-educar Clang isso, você Sabe, me dê um struct chamado nó struct. Francamente, eu não gosto de coisas que chamam struct node, struct node tudo todo o meu código. Mas eu só vou usá-lo uma vez, apenas dentro, para que eu possa efetivamente criar uma espécie de referência circular, não um ponteiro para mim mesmo, por si só, mas uma apontador para outra um do mesmo tipo. Assim, verifica-se que em uma estrutura de dados assim, há alguns as operações que podem ser de interesse para nós. Podemos querer inserir em uma lista como esta. Podemos querer apagar a partir de uma lista como esta. Podemos querer pesquisar a lista para um valor, ou, mais geralmente, travessia. E transversal é apenas uma maneira elegante de dizendo início à esquerda e mover todos o caminho para a direita. E notem, mesmo com esse pouco mais estrutura de dados sofisticado, vamos me propor que podemos emprestar alguns dos as idéias das últimas duas semanas e implementar uma função chamada pesquisa como esta. Vai retornar verdadeiro ou falsa, indicando, sim ou n, n está na lista. O segundo argumento é um ponteiro para a própria lista, de modo a ponteiro para um nó. Tudo o que eu vou fazer em seguida é declarar uma variável temporária. Vamos chamá-lo de ptr por convenção, para ponteiro. E eu atribuí-lo igual ao início da lista. E agora observe o loop while. Enquanto o ponteiro não é igual como nulo, vou verificar. É ponteiro de seta n igual a o n que foi passado em? E espere um minuto - novo pedaço de sintaxe. Qual é a flecha de repente? Sim? Exatamente. Assim, enquanto a poucos minutos atrás, usamos a notação de ponto para acessar algo o interior de uma estrutura, se a variável você não é o struct si mesmo, mas um ponteiro para um struct, felizmente, um pedaço de sintaxe que finalmente faz sentido intuitivo. A seta significa seguir o ponteiro, como nossas flechas normalmente significa pictoricamente, e ir em campo de dados no interior. Então seta é a mesma coisa que ponto, mas você usá-lo quando você tem um ponteiro. Então, só para recapitular, então, se o campo de n dentro da estrutura chamada de ponteiro é igual a igual a n, retorna true. Caso contrário, essa linha aqui - ponteiro igual ponteiro próximo. Então o que está fazendo, o aviso prévio, é que se eu atualmente estou apontando para a estrutura contendo 9 e 9 não é o número Estou procurando - suponho que eu estou procurando para n igual a 50 - Eu vou atualizar meu ponteiro temporário para não apontar para este nó mais, mas ponteiro seta ao lado, que vai me colocar aqui. Agora, percebi que é um turbilhão introdução. Na quarta-feira, vamos realmente fazer isso com alguns seres humanos, e com um pouco mais código em um ritmo mais lento. Mas percebo, agora estamos fazendo os nossos dados estruturas mais complexas para que o nosso algoritmos podem ficar mais eficiente, o que vai ser requisito para pset seis anos, quando nós carregamos dentro, mais uma vez, aqueles 150 mil palavras, mas precisa fazê-lo de forma eficiente e, idealmente, criar um programa que é executado para os nossos usuários não em linear, não em n ao quadrado, mas, em constante de tempo, no ideal. Vemo-nos na quarta-feira. Palestrante: Na próxima CS50, David esquece o seu caso base. DAVID MALAN: E é assim que você envia mensagens de texto com C. Que - [VÁRIOS MENSAGEM DE TEXTO Sons de notificação]