Ir ao conteúdo

arfneto

Membro Pleno
  • Posts

    6.526
  • Cadastrado em

  • Última visita

Tudo que arfneto postou

  1. arfneto

    C++ Sobrecarga do operador ->

    Exceções são assim ,exceções. No geral não se recomenda ou usa isso porque sai muito caro em termos de recursos e tempo de execução. Mas no caso dessa classe exemplo Conjunto achei que faria sentido para interceptar operações inválidas como por exemplo a divisão de Conjunto, que não foi implementada ainda. E nesse caso é uma exceção definida pelo usuário, criando uma classe nova a partir da classe exception e reescrevendo a função padrão what() para permitir ao usuário da classe tratar a exceção se quiser. Mas o principal é mostrar uma maneira de IMPEDIR o uso do operador / Bem bobo: divisão por zero não é uma exceção em C++ e é muito mais simples tratar o erro testando antes o dividendo. Vou deixar um outro exemplo aqui, meio fora do tópico mas por estar ainda no contexto da discussão. Um programa que gera e intercepta algumas --- seis --- exceções do C++ Saida Teste 0 OUT_OF_RANGE: invalid string position Teste 1 BAD_ARRAY: bad array new length Teste 2 UNDERFLOW: E agora? Teste 3 Deu pau em outra coisa... Teste 4 tentando alocar -1 bytes BAD_ARRAY: bad array new length Teste 5 tentando alocar 1000000000 bytes bad_alloc: bad allocation O programa #include<exception> #include <iostream> using namespace std; class Aloca { private: int* buffer; int tamanho; public: Aloca(int t) : tamanho(t) { cout << "tentando alocar " << t << " bytes" << endl; buffer = new int[tamanho]; } }; // end class int main( int argc, char** argv ) { string pequena_string = "pequena"; int i = 0; do { try { switch (i) { case 0: cout << "Teste " << i << endl; i++; cout << pequena_string.substr(700) << endl; break; case 1: { cout << "Teste " << i << endl; i++; int z = -1; char* vetor = new char[z]; break; } case 2: { cout << "Teste " << i << endl; i++; throw std::underflow_error("E agora?"); break; } case 3: { cout << "Teste " << i << endl; ++i; throw 32; } case 4: cout << "Teste " << i << endl; i++; Aloca(-1); break; case 5: cout << "Teste " << i << endl; i++; Aloca(1000000000); break; default: exit(0); } // end switch } catch (std::out_of_range & e) {// OK std::cout << "OUT_OF_RANGE: " << e.what() << endl; } catch (std::invalid_argument & e) {// OK std::cout << "INVALID: " << e.what() << endl; } catch (std::length_error & e) {// OK std::cout << "LENGTH: " << e.what() << endl; } catch (std::bad_array_new_length & e) {// OK std::cout << "BAD_ARRAY: " << e.what() << endl; } catch (std::underflow_error & e) {// OK std::cout << "UNDERFLOW: " << e.what() << endl; } catch (std::bad_alloc & e) { std::cout << "bad_alloc: " << e.what() << endl; } catch (std::exception & e) { std::cout << "std::exception " << e.what() << endl; std::cout << "exception x " << i << endl; } catch (...) { std::cout << "Deu pau em outra coisa..." << endl; } // end try{} } while (1); // end do{} cout << "fim normal" << endl; return 0; } O programa sobrevive a erros de underflow, bad_alloc, argumentos inválidos e termina normalmente
  2. arfneto

    C++ Sobrecarga do operador ->

    Não acha mais impressionante ainda essa linha abaixo funcionar? Conjunto c,d; Conjunto expressao = a^b + (d + d) * c + 300; Considerando que a, b, c e d são Conjunto --- conjuntos de int ordenados e escondidos dentro da classe Conjunto? . E a classe conjunto foi implementada usando discretamente a classe Set da STL (^ é o operador de intersecção, que eu criei ontem)? Sim, mas nesse caso a soma é a União de conjuntos e o produto é o produto cartesiano dos dois conjuntos. E algumas operações não fazem sentido se você ver bem: A + 0 é diferente de A a menos que já tenha um zero no conjunto A ^ pode ser considerado obscuro porque ao ler isso você não saberia que se trata da intersecção Se a = b então a!=b Isso porque ao copiar a para b no nosso código o nome do conjunto b fica "b = 'a'"indicando que ele foi copiado, e então eles não são mais iguais: Sem pirataria na classe Conjunto Em muitos casos os operadores não fazem sentido para a classe e então se pode utilizar os tais operadores para descrever qualquer operação associativa que seria difícil de escrever com funções convencionais. E aí se você lê o programa focado no conceito de que + é mais e ++ é incremento pode se surpreender mesmo... Uma classe que reproduz áudio poderia usar > e >> para aumentar a velocidade de reprodução por exemplo... Quando se cria uma classe assim pode ser interessante declarar a classe como final para que não possa ser derivada. já que o código nas classes derivadas pode comprometer a lógica desses operadores. O operador ^ pode ser a intersecção de conjuntos... Faz sentido implementar esse operador para a interseção de conjuntos A e B Então seria Conjunto A; Conjunto B; cout A^B >> endl; deve mostrar todos os elementos que estão em A e B e só esses.... Entendeu o lance da exceção para interceptar o uso de operadores sem função? Os links do programa e da pasta agora estão públicos. Estavam na parte privada do GitHub. Foi mal. Eu não tinha visto porque meu link já entrava com a senha... Lista dos operadores que podem e não podem ser sobrecarregados, direto de cplusplus.org Inclui alguns dos operadores que faltavam e gerei um novo programa de teste com uma rotina mais formal. Pode ser uma boa leitura porque é um inferno achar uma referência para essas coisas. Ou eu não sei procurar... O programa está em aqui, e tem um botão pra download no formato zip Ou pode ser lido direto em aqui Um exemplo da saída está aqui
  3. arfneto

    C++ Sobrecarga do operador ->

    Em geral não se quer de jeito nenhum redefinir operadores. Sobrecarregar operadores. De jeito nenhum. É um porre e você tem que tomar muitos cuidados e documentar bem o que está fazendo. Documentar para você e para os outros. Ao ler um programa a gente tem uma expectativa em relação aos operadores, como você tinha inicialmente, e quando não é mais isso pode dar muita confusão. Por outro lado se você cria uma classe e alguém vai lá e tenta operar com ela pode dar um problema enorme. Quase na totalidade das vezes que se faz isso é para no mínimo redefinir os operadores == = e != como eu expliquei antes. Isso porque pode ser que ninguém queira multiplicar duas instâncias de Cadastro mas pode muito bem ser que alguém acabe comparando... Cadastro cadastro_a; Cadastro cadastro_b; cadastro_b = cadastro_a * cadastro_b; // difícil if( cadastro_a == cadastro_b )... // bem mais provavel E se você tem estruturas de dados ou aloca memória nessa classe Cadastro quase certamente essa comparação não vai funcionar e você vai ter sim que escrever código para o operador == e != código para o operador = construtor de cópia para quando alguém usar Conjunto a = b + c; por exemplo destrutor para liberar a memória no final da vida da instância Mas e aí? Melhor esquecer? Não. De modo algum. E muitas vezes não dá mesmo. Teria eu um exemplo então? Sim. Agora que --- eu acho -- o princípio ficou claro, vou mostrar um exemplo que pode ajudar a escrever código para isso, e entender quando escrever. Talvez. Tenha paciência. Pode ser algo interessante. Eu por exemplo gostaria muito de ter lido um programa assim antes das primeiras vezes em que usei essa #$%#%$#%% Vou usar uma classe Conjunto e redefinir uns operadores e mostrar os resultados ou a falta deles. class Conjunto { private: set<int> elementos; string nome; unsigned short tamanho; public: Conjunto() : tamanho{ 0 }, nome("conjunto"){} // sem parametros Conjunto(string str) : tamanho{ 0 }, nome(str){} // define nome Conjunto(string str, short primeiro, short limite, short incremento) : tamanho{ 0 }, nome(str) { for (int i = primeiro; i <= limite; i += incremento) { insere(i); } // end for } // construtor de exemplo, tipo um for: insere os caras void aborta(); void define_nome(string); void insere(int); string const ve_nome() const; int const ve_tamanho() const; }; // end class Conjunto Bem simples: é só um conjunto. De int. Na biblioteca padrão tem uma classe Set que é muito legal e é isso, um conjunto. Em java também tem, e em outras linguagens, porque é uma estrutura importante. Vou usar um conjunto de int que é como um vetor. Um conjunto de int é isso: um punhado de int ordenadinhos e sem repetição. No nosso caso cada Conjunto tem um nome um tamanho uma lista de elementos, todos int. Nào faz diferença de que modo eles estão gravados. Esse conceito é MUITO importante em programação com objetos e se chama encapsulamento. História curta: você pode usar a classe sem saber como eles estão guardados lá dentro. E estão declarados dentro daquele trecho private: então você não pode nem mexer. Nem copiar. Nada. E os métodos? aborta() - faz o óbvio: derruba seu programa. Como dividir por zero. Outro conceito MUITO importante: exceção --- EXCEPTION. define_nome() - permite que você altere o nome do Conjunto, já que você não tem acesso direto a isso. ve_nome() - devolve o nome do conjunto, pela mesma razão: você não tem acesso porque foi declarado como private. ve_tamanho() - faz o óbvio: devolve o tamanho do conjunto. insere() - faz o óbvio: insere o int lá, na posição certinha. Se ele já não estava lá Só isso. Não vou implementar uma classe todinha funcional porque não vai fazer falta. Continue lendo. Contruindo Conjunto: o construtor da classe. Por definição na classe Conjunto qualquer função --- método é o nome usado --- declarado Conjunto() é chamada construtor e o compilador procura e chama isso quando você declara uma variável da classe. Quando tem mais de um ele se vira para achar o certo. Quando não tem nenhum ele cria um bem simples. Temos 3: Conjunto() : tamanho{ 0 }, nome("conjunto"){} // sem parametros Conjunto(string str) : tamanho{ 0 }, nome(str){} // define nome Conjunto(string str, short primeiro, short limite, short incremento) :tamanho{ 0 }, nome(str) Então posso usar Conjunto a(); // vai rodar o primeiro e cria um conjunto vazio com o nome "conjunto" Conjunto b("C++"); // vai rodar o segundo e criar um conjunto com o nome "C++" Conjunto c("conjunto 10", 1,10,1); // vai gerar o conjunto "conjunto 10" e colocar os int de 1 a 10 lá dentro Esse último é legal porque queremos testar o programa e não ficar digitando dados e assim Conjunto d("meio milhao", 1,1000000,2); // impares ate 1 milhao, bem besta Mas aí você quer listar o conjunto... Algo assim Conjunto c; Conjunto d("D"); Conjunto e("E 2 ate 20 de 2 em 2", 2, 20, 2); cout << c.ve_nome() << " tem agora " << c.ve_tamanho() << " elemetos" << endl; c.define_nome("outro"); cout << c.ve_nome() << " tem agora " << c.ve_tamanho() << " elementos" << endl; para ver se a função que muda o nome está ok e tal. E claro que vamos querer listar o conteúdo. Mas para cada Conjunto vou ter que escrever muitos desses cout... Muito chato (Nem escrevi as funções que acessam o conteúdo ainda porque não é o que quero mostrar). Eu poderia escrever uma função mostra() dentro da classe Conjunto e aí seria só chamar c.mostra(), d.mostra() e tal... Mas e se eu pudesse escrever cout << c << endl; e funcionasse já? algo assim no programa Conjunto e("22 caras", 1, 22, 1); cout << e; e mostrasse isso: Conjunto: '22 caras' Elementos:....22 { 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 } Mais ainda, algo assim Conjunto e("2 pares", 2, 4, 2); Conjunto f("2 impares", 1, 3, 1); cout << e << f << endl << "encadeados tambem funciona sem fazer mais nada" << endl;; Mostraria Conjunto: '2 pares' Elementos:....2 { 2 4 } Fim do conjunto '2 pares' Conjunto: '2 impares' Elementos:....3 { 1 2 3 } Fim do conjunto '2 impares' encadeados tambem funciona sem fazer mais nada Bem favorável, certo? Pois é: essa é uma razão para redefinir o operador << nesse caso: eu posso usar no meio de um cout por exemplo e funciona direitinho e se eu quiser mudar o formato ainda mudo em um único lugar. E como faz essa p#%%@? Bem, veja a linha cout << e << endl; E vamos ver o que tem lá. o IDE em geral até já mostra a declaração: basta deixar o mouse sobre o nome. cout é do tipo std::ostream e por isso você declarou o #include <iostream> entre outras coisas. E e --- é óbvio --- é instância da classe Conjunto declarada na linha de cima. Então esse operador que queremos definir é algo como ostream& operator<< (ostream&, const Conjunto&); Em C++ é um método que pega uma referência a ostream e uma referência a conjunto, e retorna uma referência a ostream. Um ponteiro. Ou quase isso. Esses const reforçam o fato de que nosso método não pode por a mão nem no conjunto nem na stream. Não faria sentido. Com esse método declarado o compilador sabe que ao encontrar cout << e; vai ter que chamar nosso método. Só isso. Para esse código Conjunto f("teste", 10, 30, 1); cout << f; Mostrar isso Conjunto: 'teste' Elementos:....21 { 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 } Fim do conjunto 'teste' Foi usado esse método no programa de teste, que inclui as chaves e formata tudo ostream& operator<<(ostream& o, const Conjunto& conjunto) { o << endl; o << "Conjunto: '" << conjunto.nome << "'" << endl << endl; o << " Elementos:...." << conjunto.tamanho << endl; // estara vazio? if (conjunto.elementos.size() == 0) return o; o << endl << "{" << endl; int n = 0; for (auto i : conjunto.elementos) { o << setw(6) << i; n = n + 1; if (n % 5 == 0) cout << endl; } o << endl << "}" << endl; o << endl << "Fim do conjunto '" << conjunto.nome << "'" << endl << endl; return o; } // end operator<< E se eu quisesse o produto? c = a * b para a,b e c da classe Conjunto? Faria sentido? Talvez não. Mas a classe é nossa e em Teoria de Conjuntos se pode imaginar o produto dos dois conjuntos como sendo um novo conjunto onde estão todos os possíveis produtos entre os elementos de a e b. Produto cartesiano se chama. O tamanho do conjunto final vai depender dos elementos porque não tem duplicata no nosso Conjunto. E o nome podia ser indicador do produto, algo assim: Para Conjunto c("3", 1, 3, 1); Conjunto d("2", 1, 2, 1); cout << c << d << c * d << endl; Seria algo assim Conjunto: '3' Elementos:....3 { 1 2 3 } Fim do conjunto '3' Conjunto: '2' Elementos:....2 { 1 2 } Fim do conjunto '2' Conjunto: '3*2' Elementos:....5 { 1 2 3 4 6 } Fim do conjunto '3*2' Nada mal certo? e se pode escrever dentro do cout sem mudar nada. e até o nome está certinho e o total de elementos corrigido. e o 2 que seria gerado por 2*2 e já tinha no conjunto não ficou duplicado... E nem precisei declarar outro conjunto. E nem por parenteses. Faz sentido. E como declarar o operador de multiplicação? Veja a linha que nos interessa: cout << c << d << c * d << endl; // so interessa mesmo c * d Então a operação com o operando retorna um novo Conjunto a partir de dois conjuntos a e b que saem inalterados. Parece simples. Algo assim Conjunto operator*(const Conjunto& a, const Conjunto& b) { Conjunto produto(a.nome + "*" + b.nome); for (auto x : a.elementos) { for (auto y : b.elementos) { produto.elementos.insert(x * y); } } // produto cartesiano produto.tamanho = (unsigned short) produto.elementos.size(); return produto; } // end operator* ); E não é que funciona? E agora? E para dividir por exemplo? Sei lá. Já vimos o suficiente. Se define um comportamento seguro para o operador, define o método e segue em frente. Isto aqui é só um exemplo e não vou definir todos os operadores aqui. No programa de teste vou implementar conjunto + int que simplemnte insere o int no conjunto se ele ainda não estiver lá, como a = a + 300; e lógico int + conjunto porque não dá pra garantir em que sentido vai estar no programa isso e tem que funcionar para a = 300 + a; E por consequência vai funcionar para a = 150 + a + 151. Note que isso vai inserir os dois int no Conjunto! Somar conjuntos não é somar int a = a + b para Conjunto simplesmente soma os valores, é a união entre os conjuntos Esses estão no programa de teste friend ostream& operator<<(ostream&, const Conjunto&); friend Conjunto operator+(const Conjunto&, const Conjunto&); friend Conjunto operator+(const Conjunto&, const int); friend Conjunto operator+(const int, const Conjunto&); friend Conjunto operator*(const Conjunto&, const Conjunto&); friend Conjunto operator/(const Conjunto&, const Conjunto&); Esse comando friend faz com que o método possa ter acesso às variáveis da classe Conjunto. Já discutimos << * e os +. Vou discutir algo sobre o / e encerrar por aqui. Escreva se tiver alguma dúvida. No final estará o link para o programa de teste e lá tem um botão de download e tal. Note que com o que temos até aqui dá pra escrever Conjunto c("3", 1, 3, 1); Conjunto d("2", 1, 2, 1); Conjunto produto = c * d; Conjunto expressao = (d + d) * c + 300; // nada mal cout << c; cout << d; cout << expressao << endl; E em meia hora a classe Conjunto aprendeu essas várias funções, associativas como os operadores tem que ser. E tem o << que ajuda bem a mostrar os valores sem escrever mais códigos... Veja o resultado Conjunto: '3' Elementos:....3 { 1 2 3 } Fim do conjunto '3' Conjunto: '2' Elementos:....2 { 1 2 } Fim do conjunto '2' Conjunto: ''2' + '2'*3' Elementos:....6 { 1 2 3 4 6 300 } Fim do conjunto ''2' + '2'*3' Talvez ninguém vá ler até aqui O que fazer com o / que até aqui não tem sentido? Simples: cancelar o programa. Mas talvez de uma maneira educada. Assim damos a oportunidade de um usuário de nossa classe tratar o erro e continuar a vida, ou cancelar o programa de vez até ele desistir ou a gente implementar algum tipo de divisão de conjuntos Veja o código para divisão como está no programa de teste: Conjunto operator/(const Conjunto& a, const Conjunto& b) { a.aborta(); return a; } // end operator/ Pois é: chama aborta(). Tanto faz chamar a.aborta() ou b.aborta() porque o programa vai abortar mesmo. E essa aborta()? void Conjunto::aborta() const { throw Erro_op_Conjunto(); } Que é isso? Criamos uma exceção para nossa classe, Erro_op_Conjunto. E e se alguém tentar usar algum operador não implementado a gente pode: interceptar no programa e tratar cancelar como no caso de uma divisão por zero. Pra que? Porque não faz sentido continuar. E como faz isso? Criando uma nova classe a partir da classe exception do C++ class Erro_op_Conjunto : public exception { public: virtual const char* what() const throw() { return "Conjunto: Operacao Invalida ou Nao implementada ainda"; } }; // end Erro_op_Conjunto() E como trata no programa? Algo assim int teste_outro() { try { Conjunto c("3", 1, 3, 1); Conjunto d("2", 1, 2, 1); Conjunto produto = c * d; Conjunto expressao = (d + d) * c + 300; cout << c; cout << d; cout << expressao << endl; cout << c / d; // vai cancelar aqui } catch (Erro_op_Conjunto& excecao) { cout << excecao.what() << endl; cout << "Erro de operacao em Conjunto foi capturado: retorna normalmente" << endl; } // end try return 0; } Esse não é um exemplo completo, e deve ter até erros. Mas mostra alguns dos conceitos de overloading e um pouco da prática de usar isso. Me dei ao trabalho de postar isso porque eu quando precisei nunca achei algo escrito pequeno e comentado e tal pra eu testar... E eu tinha isso quse pronto na verdade Se leu até aqui e vai rodar o programa de teste em seu computador, não me diga que ele está dando erro Foi feito assim. Eis main() int main(int argc, char** argv) { teste_outro(); teste_trata_excecao(); cout << "***** voltou do teste *****" << endl; cout << "***** agora testa sem tratar erro em Conjunto *****" << endl; teste_nao_trata_excecao(); cout << "***** voltou do teste *****" << endl; return 0; } // end main() O programa de teste e um exemplo da saída está aqui, na pasta conjunto E o programa pode ser lido neste endereco
  4. arfneto

    C++ Sobrecarga do operador ->

    Sim. Mas criar classes e todo o conceito que vem junto cria programas mais sólidos e simboliza melhor toda uma classe de problemas. E é preciso considerar as classes que estão disponíveis para você usar, coisas impressionantes como a biblioteca padrão, as classes .Net, cloisas como o DirectX e o openGL... Essa eu acho que é a exata definição do que acontece. Muito bom. Mais tarde eu vou postar um programa de exemplo que eu escrevi, para mostrar isso funcionando numa implementação de uma classe bem simples. Mas vou implementar alguns operadores e uma exceção e umas rotinas de teste
  5. Você resumiu muito bem a questão: IDE é um problema nesse caso porque eu acho que raramente os instrutores explicam o que está sendo "integrado" ali... Não, não vai: pense isso: Implementation e Interface só tem a ver com o compilador e programas fonte. Você pode não ter acesso a isso e em geral não tem mesmo. O fato de você incluir a implementação do código através de bibliotecas dinâmicas em tempo de execução ou bibliotecas estáticas na geração do executável não deixa você mais perto da tal Implementation que seria uma particular implementação do código que está usando. Em muitos casos você tem até mais de uma versão da mesma biblioteca e decide na hora do link qual vai gerar. E nunca viu a tal implementation. É assim em todas as linguagens.
  6. @soumma essa trecho em uma resposta acima é perfeito para ilustrar o porque de sempre aparecerem esses questionamentos. O I de IDE significa integrado. IDE significa ambiente integrado de desenvolvimento e é como viajar num ônibus sem janelas: quando chega você não sabe nada sobre o caminho Em seu programa, mesmo tendo umas poucas linhas, você usou #include <iostream> Em muitos casos você vai usar memory.h, math.h, SDL.h <stdlib.h> e outros desses headers. No entanto você não vai compilar essas funções todas que eventualmente estão em seu programa, coisas como cout, rand(), sort(), classes importantes como vetor<>, set<>, deque<> list<> e tantas outras que estão por exemplo em stdlib.h. Se você escrever 300 classes para tratar um problema de seu interesse e escrever depois uns programas de teste para suas excelentes 300 funções, não vai querer compilar toda vez as 300 funções sabendo que você não mudou uma única linha delas. Quando você compila um programa é gerado um código intermediário, muitas vezes chamado de programa objeto, que é gravado com extensão .o ou .OBJ pelo compilador. Para gerar esse código o compilador lê o seu programa e precisa gerar código para todas as funções que estão nele. Para isso ele só precisa da declaração delas. Por isso você declara às vezes o que se chama de protótipo de uma função, algo como int pular( float ); Isso é o que basta para o compilador entender nas suas funções que ao encontrar int i = pular( 3.5 ); deve gerar código para chamar tal função com este nome e que retorna um int e tem um argumento float. Basta isso. E se você logo abaixo escreveu int n = pular ( "alto" ); O compilador vai saber que está em desacordo com a declaração que está no protótipo e vai gerar um erro de compilação. Mas se o código correspondente a pular() não está em seus arquivos .cpp não importa para o compilador porque o header tem toda a definição que importa. Afinalpor exemplo o código de cout também não está, nem o de sort() ou rand() ou sei lá. O LINKER Esse tipo de programa, como o LINK da Microsoft ou o ld no linux/UNIX , pega esses códigos intermediários gerados pelo seu compilador e outros obtidos com o sistema operacional por exemplo, ou comprados ou obtidos de alguém, e junta tudo para criar um arquivo executável. E para fazer isso ele usa informações do sistema sobre onde estão essas tais "bibliotecas" com essas mágicas funções. O Linker não compila nada. O compilador não gera nenhum programa. Link Dinâmico ou Estático Esse é um outro pedaço da história: por exemplo, todos os programas de computador que escrevem uma mensagem na tela usam as mesmas funções do sistema, que criam uma janela na tela, coloca aqueles 3 botões de fechar, minimizar, maximizar e tal. Quando o LINKER gera o código para um programa algumas coisas podem ser deixadas para resolver quando o programa roda, e isso se chama link dinâmico. Você pode compilar seu programa em uma máquina que não tenha as funções de banco de dados por exemplo, mas qundo seu programa for rodar você vai levá-lo para um lugar que tenha isso. Mas em outros casos você precisa incorporar direto no seu programa executável o código de certas funções. Porque? Simples: assim você garante que não vai chegar a rodar em uma máquina que não tenha esses códigos. Como é melhor? Depende. Lógico que se todo programa tiver que incorporar todo o código necessário não vai rolar. Por isso seu sistema Windows tem milhares de arquivos .DLL, sua máquina Linux tem milhares que arquivos .so. Por outro lado tem muitas razões para você querer incorporar funções direto em seu arquivo .EXE, como performance e um pouco de sossego. O IDE Mas quando você usa o IDE não vê nada disso. Claro, todo IDE tem comandos para só compilar seus programas rapidinho sem gerar executável ou tentar rodar um programa, mas em geral se começa co programas minúsculos que rodam direto na tela. Em geral o primeiro programa que o aluno escreve mostra uma mensagem fixa na tela, e para mudar a mensagem de Hello Wordl para qualquer outra é preciso compilar o programa de novo. Claro que não faz sentido na prática. Você escreve um programa hoje e espera usá-lo muitas vezes. Mandar para alguém que não vai poder compilar, que só quer usar o programa. Nem todo computador do mundo tem o Code::Blocks ou ambiente de compilação de programas. Sim, isso é possível mas não é o padrão. É possível desde que você tenha o código fonte de tudo. Veja o caso desses #include todos que a gente simplesmente declara sem qualquer noção de como foi escrito o código ou onde está. Ou seja, não é na prática possível. Não é assim. Nada tem a ver com herança, que é um conceito de derivação de classes, tipo uma especialização de classes e é comum à arquitetura de objetos e não uma característica dessa linguagem Pois é: se leu até aqui já deve ter entendido que nada tem a ver com C++. Ou C. Ou java ou Pascal ou COBOL ou javascript. Funciona de modo idêntico. Em java por exemplo cada classe tem seu arquivo mas você tem aquelas pastas com centenas de arquivos .jar com classes prontas pra você usar... Os header tem informação de declaração para o compilador e podem ser distribuídos para terceiros poderem usar seu código, suas classes, sem saber como ou de onde elas vieram. Por outro lado você vai distribuir o código compilado e assim poderá usar suas rotinas em outro computador para gerar outros executáveis onde outros main() fazem outras coisas com suas rotinas. imagine esse header coisas.h // coisas.h #include "stdio.h" #include "math.h" void andar(float); void pular(float); // fim de coisas.h e um programa C coisas.c --- tanto faz a linguagem // coisas.c #include "stdio.h" #include "math.h" void andar(float quanto) { printf("Andando %f\n", quanto); return; } void pular(float quanto) { printf("Pulando %f\n", quanto); return; } // fim de coisas.c E você roda isso no terminal: cc -c coisas.c Vai gerar um arquivo coisas.o que você pode distribuir junto com o coisas.h, mas SEM o coisas.c. E quem tiver esses arquivos vai poder compilar programas que usam essas funções. Note que coisas.c não tem uma função main(). Não vai ser executado: só queremos gerar o objeto para distribuir pros clientes ou levar pra outros micros sem ter que compilar tudo de novo #include "coisas.h" Com o coisas.h disponível, na outra máquina eu posso rodar cc -c pular.c que vai compilar um hipotético programa que usa essa rotina porque o header diz exatamente como compilar pular() e andar(). cc -c pular.c E depois posso rodar ld pular pular.o coisas.o e vai gerar o executável pular que pode ser levado para outro micro onde não tem nem o coisas.h nem o coisas.o e o cara digitar pular 1.2 e sair na tela Pulando 1.2 É só um exemplo e as opções podem nem ser exatamente assim. Se leu até aqui entendeu que não é o caso. A geração de código objeto (compilação) é separada da geração de programas executáveis (link) Não, não é. Implementation pode nem estar disponível porque por exemplo não se sabe sequer quem escreveu. Muitas vezes você não tem o código. Até gostaria talvez, mas não tem. Mas você quer usar. Como chamar OpenGL a partir de seu programa Pascal para plotar aquele gráfico, ou usar wxPascal para usar as rotinas de wxWidgets e escrever interfaces gráficas portáveis para Linux e Windows... Exato. Imagine compilar as centenas de milhares de linhas de <stdlib.h> e <iostream.h> para aquele programa em C++ que roda std::cout << "Hello World" << std::endl; Continue escrevendo se ainda não ficou clara a mecânica da geração de código, IDE a parte.
  7. arfneto

    C++ Sobrecarga do operador ->

    Olá @Bimetal Eu acho mesmo que aquele é um exemplo besta. Mas eu já havia visto isso antes com outros nomes e em outra língua. E que naquele exemplo sem redefinir você não poderia usar tal operador. Entenda que ao redefinir -> permite que você use o operador em uma situação em que normalmente ele não funcionaria, como te mostrei. Mas só isso. Bem, isso vai sempre depender de como 'a' foi declarado do atual significado do operador -> para a classe 'a' que pode não ser um tipo padrão Na verdade é o contrário: ao redefinir o operador você é que vai dizer como funciona. Leu a parte em que falei do exemplo de uma classe trem? Você redefine operadores para que eles funcionem com classes especializadas que você escreveu. Dois casos clássicos: Overload = == e != Muitas vezes comparar ou copiar dois objetos de uma classe envolve procedimentos não triviais, como percorrer listas internas e comparar resultados de consultas dinâmicas, de modo que você precisa interceptar isso no seu programa e escrever sua própria rotina. Overload << Muitas vezes, em especial em programas de teste para classes que você está desenvolvendo ainda, você pode querer muito isso, para poder escrever algo assim MuitoComplexa m1; std::cout << m1 << endl; e ter uma saída adequada para o que você quer mostrar de sua classe no momento, porque é muito mais prático que ficar escrevendo comandos de um metro para cada vez que for preciso testar. E se aparece uma novidade você só precisa alterar na implementação do operador << Como eu disse, nada impede que você crie operador + que multiplica, operador [] que lista, tudo depende da necessidade e sentido no momento. No geral você não faz isso a menos que seja obrigado. Por exemplo, quase sempre que você precisa de um construtor de cópia é porque sua classe por exemplo aloca memória e tem listas e ponteiros e tal. E aí não dá pra usar ClasseComPonteirosEstrutrasComplexas coisa; ClasseComPonteirosEstrutrasComplexas outraCoisa; ClasseComPonteirosEstrutrasComplexas mesmaCoisa = coisa; coisa = outraCoisa = mesmaCoisa; Porque vai certamente abortar seu programa. Então você calmamente sobrecarrega os operadores = != == e talvez outros cria um destrutor pra liberar a memória na ordem inversa da que alocou as coisas lá dentro cria um construtor de cópia para alocar memória e recriar as estruturas de dados para criar uma nova cópia de sua instância da classe Ou não vai funcionar.
  8. Olá Se você tem a rede 172.17.0.0 com a máscara 255.255.0.0 --- pode ser escrito também 172.17.0.0/16 --- tem 16 bits para endereçar suas redes e hosts e vai controlar o uso internamente usando a máscara de rede para separar os endereços originais, 65536 deles, em grupos de redes e hosts dentro das redes. Veja um exemplo minimalista: 172.17.0.0/30 onde você usa 30 bits dos 32 para endereçar a rede e... DOIS para endereçar os hosts. Então você tem com dois bits 4 endereços para host. Mas não pode usar o primeiro, que vai ser o endereço da sub-rede, nem o último, que vai ser o endereço de broadcast na sub-rede. Dos 16 que você tinha pode usar 14 para as redes então vai poder usar 16.384 redes, 2^14. Sua primeira rede vai ter os 14 bits em zero então vai ser 172.17.0.0 e a máscara 255.255.255.252. Veja em binário: IP 172. 17. 0. 0 1010 1100 0001 0001 0000 0000 0000 00000 Máscara 255. 255. 255. 252. 1111 1111 1111 1111 1111 1111 1111 11100 Então nesse exemplo você poderia variar os dois últimos bits para seus hosts em cada rede, com 4 possíveis valores, exceto como eu disse o primeiro e o ultimo. Então sobram 2 por rede. No caso da primeira de suas redes do exemplo Sub-rede 0: Endereco 172.17.0.0 Mascara 255.255.255.252 Broadcast 172.17.0.3 Total de Hosts 2 172.17.0.1 e 172.17.0.2 e assim usou os 4 endereços Escrevi isso porque é o caso mínimo e fica assim fácil de ver como derivar os endereços. De volta ao problema original Internamente em sua rede pode manipular 16 bits da máscara, esses com X 255. 255. 0. 0. 1111 1111 1111 1111 XXXX XXXX XXXX XXXX E você precisa de 130 hosts. Se fossem um pouco menos você poderia usar 7 bits, que acomodariam até 128 deles, menos 2 como falamos antes, então 126. Mas são 140 e nesse caso precisa de OITO. E assim poderá ter até 256 - 2 = 254 hosts. E restaram 8 para os endereços de (sub-)rede, que vão ser mais que suficientes para as necessárias 140 redes. Se optar por usar na ordem, de zero pra frente, , sua última sub-rede seria 172.17.139.0 com a máscara 255.255.255.0. E os hosts nessa última sub-rede rede iriam de 172.17.139.1 até 172.17.139.130, inclusive. O último endereço dessa última rede seria 172.17.139.254 porque 172.17.139.255 é o endereço de broadcast dessa rede. O tráfego para esse endereço é replicado para todos os hosts da rede --- broadcast --- e por isso ele é reservado em cada sub-rede Entenda que não dá pra dividir os bits então você vai ajustando pela necessidade -- ou pelo enunciado.
  9. Olá! Estamos falando de uma rede criada através do protocolo TCP/IP então essa rede tem um endereço e uma máscara, como o clássico 192.168.0.1 com a máscara 255.255.255.0. Então você tem máquinas que podem estar conectadas com endereço fíxo e outras que usam endereço dinâmico. As que tem endereço fixo Estas são difíceis de identificar, até onde eu sei. Talvez o mais simples seja monitorar o tráfego no gateway da rede, aquele outro parâmetro da configuração e que geralmente é nas redes domésticas o endereço do --- único --- roteador. As que em endereço dinâmico Nesse caso é mais simples: basta ver a lista de clientes conectados ao servidor ou servidores DHCP. Como a absoluta maioria das redes tem apenas um, essa é a informação autoritativa. No entanto ela não é precisa até o último segundo, porque seria muito custoso em termos de recurso monitorar a saída de um telefone ou dispositivo qualquer da rede a cada segundo então isso ocorre por timeouts de modo que dispositivos que estão na lista podem não estar mais conectados.
  10. arfneto

    C++ Sobrecarga do operador ->

    Olá Eu nunca entendi a razão desse nome, "sobrecarga" ou antes "overloading". Trata-se de uma redefinição apenas. Não consegui entender o que quis dizer nesse parágrafo talvez você pudesse explicar com outras palavras De volta ao exemplo e ao operador em si Sobre a declaração: esse operador retorna um ponteiro para a classe e não aceita parâmetros. Na implementacão exemplo apenas retorna o próprio argumento implícito, this. Um comportamento similar ao do operador -> para um ponteiro. Nesse caso do exemplo ele vai deixar o uso de -> como alternativa ao operador . exatamente como seria se estivesse acessando um ponteiro pra o objeto e não o próprio objeto. Veja esse exemplo --- um pouco mais instrutivo eu acho -- isso não quer dizer legível : #include <iostream> class Seta { public: int i; Seta* operator->() { return this; } }; struct Coisa { int membro; }; int main() { Seta obj; Seta* outroObj = &obj; Coisa coisa; Coisa* pCoisa = &coisa; obj->i = 10; std::cout << "obj.i : " << obj.i << std::endl; std::cout << "obj->i : " << obj->i << std::endl; std::cout << "outroObj->i " << outroObj->i << std::endl; std::cout << "(*outroObj)->i " << (*outroObj)->i << std::endl; coisa.membro = 12; pCoisa->membro = 11; /* coisa->membro = 1; // expressao invalida */ } // end main() Todos os cout() vão imprimir o valor 10 Mas veja o que está em comentários: para a classe Coisa eu não posso escrever coisa->membro = 1 porque não vai compilar: coisa foi declarado como Coisa coisa; Mas eu preciso escrever pCoisa->membro = 11 porque pCoisa foi declarado Coisa* pCoisa; Agora se eu redefinir o operador -> para essa classe como no exemplo eu poderia usar os dois indistintamente. Acho esse um exemplo inútil e bem besta. Tem autores que parecem fascinados pela possibilidade de sobrecarregar operadores em C++ mas que não pensam muito ao criar exemplos. E outros autores parecem furiosos porque isso segundo eles criaria programas difíceis de ler e manter. Mas poder redefinir um operador permite que ao definir uma classe você possa definir o comportamento dos operadores para instâncias de sua classe e isso permite que você escreva essas funções de um modo que faça sentido. Imagine uma classe trem em que você possa usar locomotivas e vagões. E você tenha locomotivas de várias potências e vagões de vários pesos e capacidades. Cada instância de trem pode ter uma configuração variável dessas coisas. Então se eu puder redefinir os operadores aritméticos eu posso definir como somar ou subtrair trens por exemplo, e nos métodos se pode somar os pesos e número dos vagões. Eu posso definir como é somar uma instância de locomotiva ao trem e assim ter o comprimento do trem, potência, capacidades de frenagem e tudo atualizados na classe sem escrever mais uma linha de código. Eu posso definir como é adicionar um vagão ao trem e gerar uma exceção por overflow se exceder o comprimento permitido, em metros! Eu posso definir que somar um trem ao outro seja somar as locomotivas e os vagões e ao listar a composição tudo está atualizado como mágica... Por outro lado, os operadores podem ser apenas reaproveitados para qualquer uso... Uma classe deslocamento por exemplo poderia usar os operadores + - * e * para mover um modelo para norte, sul, leste e oeste. Uma classe folclórica poderia usar + para subtrair e - para somar. Assim é. E é um grande recurso se considerar o que eu expliquei. Se eu tiver tempo nos próximos dias posto aqui um exemplo com classes trem, locomotiva e vagão e uns operadores exemplo
  11. Olá! Mais palpites: Considere que deve tratar ate 1024-mer, o que está no enunciado. Claro que nesse caso o resultado é óbvio. Pensando no caso de uma cadeia "ABCDABCDABCD" e 3-MER para facilitar, temos os possíveis 3-mer a partir de ABC 00 ABCDABCDABCD 01 ABC 02 BCD 03 CDA 04 DAB 05 ABC 06 BCD 07 CDA 08 DAB 09 ABC 10 BCD Um 3-mer não pode iniciar antes da segunda posição simplesmente porque não cabe lá, certo? Então Para uma cadeia de comprimento N temos um total de (N-(k-1)) possíveis k-mer Obviamente pode haver repetição, então podem ser menos que isso. Quanto menos? Simples: numa cadeia onde todas as posições são iguais temos um único k-mer --- "AAAAA" por exemplo. Então uma função que, dada uma cadeia C e um inteiro K devolvesse uma lista de todos os k-mer sem repetição seira muito bem recebida. Somada à rotina count() obrigatória e mais uma tabela dá a solução para o problema, certo? Usando a lista de k-mers únicos chamamos count() e tabelamos os valores. Pegamos o maior --- ou maiores --- e pronto Esse é um ponto importante: como o enunciado mostra um exemplo com repetição, não podemos apenas mostrar um dos máximos: vai ser preciso mostrar todos os k-mer que tem o comprimento máximo. Chato isso. Saberia declarar essa função lista_k-mer()? adicionado 48 minutos depois Olá! Eis uma estrutura que pode funcionar para resumir cada pesquisa --- pode ajudar a construir o programa completo: struct K_mer { char* k_mer; int total; }; struct Pesquisa { char* cadeia; unsigned short k; // 1024 max unsigned short total_k_mer; struct K_mer* k_mer; }; typedef struct Pesquisa pesquisa; Assim a função de que falei só precisaria preencher essa estrutura... Veja como podia declarar isso para os exemplos do enunciado // as cadeias do enunciado porque nao? char* entrada1 = "ACAACTATGCATCACTATCGGGAACTATCCT"; // 5-mer "ACTAT" char* entrada2 = "CGATATATCCATAG"; // 3-mer "ATA" char* entrada3 = "ACGTTGCATGTCGCATGATGCATGAGAGCT"; // 4-mer = "GCAT" e "CATG" pesquisa p1; p1.cadeia = entrada1; p1.k = 5; p1.total_k_mer = 0; p1.k_mer = NULL; pesquisa p2; p1.cadeia = entrada2; p1.k = 3; p1.total_k_mer = 0; p1.k_mer = NULL; pesquisa p3; p1.cadeia = entrada3; p1.k = 4; p1.total_k_mer = 0; p1.k_mer = NULL;
  12. O enunciado apenas diz Mas devem ficar bem similares as funções já que muda apenas o sentido do desenho Não sou o instrutor que escreveu essa questão --- nem o cara que vai avaliar os programas --- mas pelo exemplo parece claro que isso é pra ser mostrado assim na tela, de modo que possa ser salvo, editado, impresso, mostrado no forum do Clube do Hardware, coisas assim. Mas sou apenas voluntário aqui pra tirar dúvidas sobre o tema do forum Mas programar o cursor para escrever asteriscos em coordenadas não parece ser o caso. Por exemplo, como eu disse antes, se o instrutor usar programa > Daniel-grafico-dv.txt para salvar os tais gráficos não vai ter nada pra ver. De volta ao enunciado, parte 2: Uma função como essa abaixo imprime na vertical int imprime_vertical(int elemento[], int total) { int i; int max = elemento[0]; for (i = 1; i < total; ++i) { if (elemento[i] > max) max = elemento[i]; } // end for: max e o maior valor for (int y = max; y >= 1; --y) { // imprime as linhas de max ate 1, 'total'colunas for (int x = 0; x < total; ++x) { if (elemento[x] >= y) printf("*"); else printf(" "); } // end for printf("\n"); // fim da linha y } // end for return 0; } // end imprime_vertical() E é claro bem parecida com a "transposta" rotina que imprime na horizontal, que podia ser escrita assim: int imprime_horizontal(int elemento[], int total) { for (int x = 0; x < total; ++x) { // para cada linha imprime coluna com os * for (int y = 0; y < elemento[x]; ++y) printf("*"); printf("\n"); // passa para a proxima linha } // end for return 0; // so isso } // end imprime_horizontal() Dois loops como a anterior, linha e coluna. Apenas tem um loop antes para achar o número de linhas que vai imprimir Estou postando o código porque afinal é apenas a transcrição do que já escrevi no outro post. Espero não ferir muito as regras do forum. Esse programa de teste int main() { int teste[5] = { 4,5,6,1,8 }; int curva[40] = { 4,4,4,4,3,3,3,3,2,2,2,2,1,1,1,1,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4 }; printf("[1 horizontal]\n\n"); imprime_horizontal(teste, 5); printf("[2 vertical]\n\n"); imprime_vertical(teste, 5); printf("[3 vertical]\n\n"); imprime_vertical( curva, 40); return 0; } Mostraria isso [1 horizontal] **** ***** ****** * ******** [2 vertical] * * * * ** * *** * *** * *** * ***** [3 vertical] **** **** ******** ******** ************ ************ **************** **************** Que pode ser editado, impresso e postado, já que não endereçamos cursor nem nada. Bom final de semana a todos
  13. Pode ser, mas nem precisa... Entendo a ideia de @devair1010 mas veja que o enunciado diz desenhar um gráfico. Ninguém disse que pode usar um cursor e criar algo que só vai funcionar na tela. Imagine seu programa.c e alguém escrever programa > Daniel-grafico.txt para imprimir ou mandar para alguém... De volta ao enunciado e exemplo, vou imaginar que o vetor foi escrito duas vezes por engano já que só aparece uma vez no gráfico Claro que no computador podemos usar milhões de técnicas e estruturas e tal, mas nesse caso não é preciso nada: olhe bem para o vetor e desenho final e considere é claro que para imprimir na vertical você vai imprimir um número de linhas igual ao topo do gráfico, que é claro o valor mais... ALTO. No exemplo 8. Então você vai imprimir um número de linhas igual ao maior valor. Em cada linha você vai imprimir Y valores, onde Y é o tamanho do vetor, 5 no exemplo. Sempre Y Então é um loop para o total de X linhas onde você imprime SEMPRE Y colunas, ou com branco ou com asterisco Na linha inicial você imprime branco ou asterisco dependendo do valor do vetor para tal linha: no exemplo, a linha inicial é 8 então imprime asterisco para os que tiverem valor >= 8 Na linha seguinte imprime de novo Y valores, com asterisco para os que tem valor >=7 Até que na última e oitava linha imprime Y valores, com asterisco para as colunas com valor >= 1. Todas, no caso do exemplo. Não precisa de matriz ou salvar nada porque não tem que ter memória: só precisa saber a linha que está imprimindo, que é a variável do loop, o total de colunas, que é o tamanho do vetor, e o total de linhas, que é o maior valor no vetor, que vai ditar a altura da maior coluna... Depois que imprimiu já era Só precisa de um loop, tipo um for, o total de colunas que é o tamanho do vetor dado por strlen(vetor), dois char para preenchido/não preenchido que no caso estão definidos como branco e asterisco, e o total delinhas, que é o maior valor no vetor, que você pode calcular com um for logo no início.
  14. Olá! Ainda sobre esse problema, count() é essencial para resolver isso, mais alguma rotina como a de que falei, strstr(). E o enunciado tem 3 exemplos com resposta... Então escreva e teste cont() primeiro e teste com os valores do enunciado... veja isso: testando para 'ACAACTATGCATCACTATCGGGAACTATCCT' e 5-mer Primeiro 5-mer em 'ACAACTATGCATCACTATCGGGAACTATCCT' = 'ACAAC' Buscando 'ACAAC(5)' em 'ACAACTATGCATCACTATCGGGAACTATCCT(31)' strstr() encontrou 'ACAAC' em 'ACAACTATGCATCACTATCGGGAACTATCCT' !!!! count() retornou 0 testando para 'CGATATATCCATAG' e 3-mer Primeiro 3-mer em 'CGATATATCCATAG' = 'CGA' Buscando 'CGA(3)' em 'CGATATATCCATAG(14)' strstr() encontrou 'CGA' em 'CGATATATCCATAG' !!!! count() retornou 0 testando para 'ACGTTGCATGTCGCATGATGCATGAGAGCT' e 4-mer Primeiro 4-mer em 'ACGTTGCATGTCGCATGATGCATGAGAGCT' = 'ACGT' Buscando 'ACGT(4)' em 'ACGTTGCATGTCGCATGATGCATGAGAGCT(30)' strstr() encontrou 'ACGT' em 'ACGTTGCATGTCGCATGATGCATGAGAGCT' !!!! count() retornou 0 que o programa abaixo mostra a partir dos dados do exercício e já declarando count() e testo o primeiro k-mer de cada string com strstr() #define _CRT_SECURE_NO_WARNINGS #include <memory.h> #include <stdio.h> #include <stdlib.h> #include <string.h> // prototipos para referencia int count(char*, char*); int testa_count(char*, int); int main(int argc, char** argv) { // as cadeias do enunciado porque nao? char* entrada1 = "ACAACTATGCATCACTATCGGGAACTATCCT"; // 5-mer "ACTAT" char* entrada2 = "CGATATATCCATAG"; // 3-mer "ATA" char* entrada3 = "ACGTTGCATGTCGCATGATGCATGAGAGCT"; // 4-mer = "GCAT" e "CATG" testa_count(entrada1, 5); testa_count(entrada2, 3); testa_count(entrada3, 4); return 0; } int count(char* cadeia, char* kmer) { // count() dee devolver o numero de vezes em que // k-mer aparece em cadeia printf( "Buscando '%s(%d)' em '%s(%d)'\n", kmer, strlen(kmer), cadeia, strlen(cadeia) ); char* pos = strstr(cadeia, kmer); if (pos == NULL) printf( "Nao tem '%s' em '%s'\n", kmer, cadeia); else printf("strstr() encontrou '%s' em '%s' !!!!\n", kmer, cadeia); return 0; } // end count() int testa_count(char* cadeia, int n_kmer) { char parte[80]; printf("\n\ntestando para '%s' e %d-mer\n", cadeia, n_kmer); int l = strlen(cadeia); if (l < n_kmer) return -1; // nao pode ser menor if (l == n_kmer) return 1; // sao do mesmo tamanho! // monta em parte o primeiro k-mer possivel memcpy(parte, cadeia, n_kmer); parte[n_kmer] = 0; // ok: em 'parte' esta o primeiro valor printf("Primeiro %d-mer em '%s' = '%s'\n", n_kmer, cadeia, parte); // testa count() int n = count(cadeia, parte); printf("count() retornou %d\n", n); return 0; } // end testa_count() e talvez ajude a entender o que estou falando: Use o que já tem e vá devagar
  15. Recomendo mesmo usar strstr() e buscar o k-mer dentro da cadeia original e ir adiantando o ponteiro dela conforme for encontrando ocorrências. Experimente antes no papel por umas vezes... Eu sempre recomendo o seguinte: não misture as coisas. Esqueça esse lance de ler a string e o k-mer de teste. Só vai perder tempo. Use por exemplo o enunciado e não mexa enquanto não funcionar. Depois em 5 minutos você coloca a leitura.
  16. @MB_ Entendo sua preocupação com a escassez de recursos mas entenda que esse é um programa de exemplo para um curso de algoritmos e estruturas de dados e a ideia era usar a linguagem de programação para replicar o algoritmo descrito, possivelmente usando ... estruturas de dados. Nesse caso, a matriz é totalmente supérflua e existe apenas para o aluno poder testar ou mostrar as etapas na saída mantendo a analogia do que foi feito no enunciado. Apagando todas as referências a tabela o programa continua funcionando porque nenhuma operação era feita com a matriz. Esse abaixo dá na mesma: int prod(const int x, const int y) { int quociente = x; int b = y; int passo = 1; int valor = 0; if ((x % 2) == 1) valor = valor + y; do { quociente = quociente / 2; b = b * 2; if ((quociente % 2) == 1) valor = valor + b; passo = passo + 1; } while (quociente > 1); return valor; } // end produto() Em relação aos recursos desperdiçados, considere que uma matriz de 256 BYTES cobre todo o espectro int Sobre a multiplicação por constantes, entenda que os compiladores otimizam esses códigos há décadas e muitas vezes a operação é até substituída por tabelas ou códigos de interpolação. Por exemplo, coisas como trocar (a * 2) por (a + a) ou( a<<1) são até românticas mas talvez não sejam mais vantagem alguma. E dependendo da linguagem pode ter até efeitos colaterais indesejáveis ao se usar um outro operador para supostamente otimizar uma operação, porque pode estar descartando alguma otimização que o próprio autor da classe tenha feito na operação --- no caso de C++ por exemplo. Veja o código gerado em um compilador moderno para essas linhas, sem mudar nada das configurações padrão Note que a divisão e a multiplicação por 2 foram trocadas por shift left e shift right: shl e sar Mas entendo sua preocupação e acho bastante válida Abraço adicionado 27 minutos depois Esqueci de postar o programa de teste... Está em https://github.com/ARFNeto-CH/ch-190927-divisao onde tem um botão de download em formato zip Ou apenas para ler o programa neste link
  17. Olá! Acho que apenas repetir o que fez no enunciado estaria bem. Se quiser conferir pode usar algo bem igual.Você vai calcular um produto e um int tem em geral 32 bits então são poucas linhas a calcular mesmo. Pode declarar int linha[32][2]; e estará bem. Como é só um exercício acho que não precisa entrar no mérito de poder estourar o valores do produto e não caber num int. Se for essencial avise. Claro que tem muitas maneiras de escrever qualquer coisa mas essa sugestão é só uma reprodução linear do enunciado int produto(const int x, const int y) { int linha[32][2]; int quociente = x; int b = y; int passo = 1; int valor = 0; ... Essas variáveis já fariam o serviço e facilitariam na hora de ler o texto por ter os nomes de acordo com o método. Veja uma possível saída de um teste de quem está desconfiado e quer conferir: Calculando produto de (9,6) saida depois de 4 passos. Eis a tabela: 00 9 <== 6 * 01 4 12 02 2 24 03 1 <== 48 * produto(9,6) retornou 54 (ok) Claro que o ok à direita do produto é porque o programa multiplicou do modo convencional e conferiu Enquanto a gente não confia, pode ser assim: printf("\nCalculando produto de (%d,%d)\n", x, y); linha[0][0] = x; linha[0][1] = y; if ((x%2) == 1) valor = valor + y; do { quociente = quociente / 2; b = b * 2; linha[passo][0] = quociente; linha[passo][1] = b; if ( (quociente%2) == 1) valor = valor + b; passo = passo + 1; } while (quociente > 1); printf("saida depois de %d passos. Eis a tabela:\n", passo); for (int i = 0; i < passo; ++i) { if (linha[i][0] % 2 == 1) { printf("%02d %10d <== %10d *\n", i, linha[i][0], linha[i][1]); } else { printf("%02d %10d %10d\n", i, linha[i][0], linha[i][1]); } } // end for return valor; E podemos testar com aquele "ok" ou "ERRO" int testa_produto(int a, int b) { int p = produto(a, b); int r = a * b; if (p == r) { printf("produto(%d,%d) retornou %d (ok)\n", a, b, p); } else { printf("produto(%d,%d) retornou %d (ERRO)\n", a, b, p); } return (p == a * b); } // end testa_produto() E esse código int main() { testa_produto(9, 6); testa_produto(6, 9); testa_produto(10, 1000); testa_produto(90987, 656); return 0; } mostraria Calculando produto de (9,6) saida depois de 4 passos. Eis a tabela: 00 9 <== 6 * 01 4 12 02 2 24 03 1 <== 48 * produto(9,6) retornou 54 (ok) Calculando produto de (6,9) saida depois de 3 passos. Eis a tabela: 00 6 9 01 3 <== 18 * 02 1 <== 36 * produto(6,9) retornou 54 (ok) Calculando produto de (10,1000) saida depois de 4 passos. Eis a tabela: 00 10 1000 01 5 <== 2000 * 02 2 4000 03 1 <== 8000 * produto(10,1000) retornou 10000 (ok) Calculando produto de (90987,656) saida depois de 17 passos. Eis a tabela: 00 90987 <== 656 * 01 45493 <== 1312 * 02 22746 2624 03 11373 <== 5248 * 04 5686 10496 05 2843 <== 20992 * 06 1421 <== 41984 * 07 710 83968 08 355 <== 167936 * 09 177 <== 335872 * 10 88 671744 11 44 1343488 12 22 2686976 13 11 <== 5373952 * 14 5 <== 10747904 * 15 2 21495808 16 1 <== 42991616 * produto(90987,656) retornou 59687472 (ok) Uma versão mais compacta não precisaria dessas mensagens todas, como essa abaixo int prod(const int x, const int y) { int linha[32][2]; int quociente = x; int b = y; int passo = 1; int valor = 0; linha[0][0] = x; linha[0][1] = y; if ((x % 2) == 1) valor = valor + y; do { quociente = quociente / 2; b = b * 2; linha[passo][0] = quociente; linha[passo][1] = b; if ((quociente % 2) == 1) valor = valor + b; passo = passo + 1; } while (quociente > 1); return valor; } // end produto() que mostraria produto(9,6) retornou 54 (ok) produto(6,9) retornou 54 (ok) produto(10,1000) retornou 10000 (ok) produto(90987,656) retornou 59687472 (ok) para os mesmos números Boa sorte. Essa é só uma maneira de fazer. Nada criativa. Só imitando o enunciado
  18. Olá Não que seja essencial mas, para não ter que escrever outra, entre as funções que manipulam strings em string.h tem strstr() que é declarada assim no seu caso char * strstr ( const char* cadeia, const char* k-mer ); Ela devolve um ponteiro para a primeira ocorrência de k-mer na string, ou null. E então você pode usar um loop para varrer a string de entrada variando os k-mer possíveis a partir do início e acumulando em uma tabela para mostrar no final. A cada sucesso você avança o ponteiro da string inicial para procurar adiante. Varrendo da esquerda para a direita eles já vão sair na ordem em sua tabela
  19. Então, Vitor, deve ter um milhão de maneiras de escrever isso, mas antes de tudo pense em como você escreveu: ia retornar A ou B e o resto do código serviria para que na função? Use as chaves e separe as condições... Pense em como faria no papel: tem 3 números apenas A, B e C. Comparando dois números, ou o primeiro é maior que o segundo ou o segundo é maior que o primeiro. Se forem iguais não importa nesse caso. Se os 3 forem iguais qualquer um é maior, menor, mais feio, mais bonito.... Vendo pela ordem: se (A>B) então claro que B não é o maior dos 3. Pode ser A ou C. Fim da questão: se (A>C) então o maior é A senão o maior é C mas se não era então (B>A) e então por certo A não é o maior dos 3. Fim da questão: Pode ser B ou C. Se (B>C) então o maior é B senão é C. Só isso. Em linguagem C não por acaso fica igualzinho, porque a realidade é uma só: if (A > B) { if (A > C) return A; return C; } // entao ==> B > A if (B > C) return B; // entao ==> B > A mas ==> C > B return C; Use seu computador para testar: int testa_maior(int a, int b, int c) { printf("maior(%d,%d,%d) retornou %d\n", a, b, c, maior(a, b, c)); } nada mal, certo? Assim você pode escrever int main() { testa_maior(1, 2, 3); testa_maior(1, 3, 2); testa_maior(2, 1, 3); testa_maior(2, 3, 1); testa_maior(3, 1, 2); testa_maior(3, 2, 1); } E ver na sua tela maior(1,2,3) retornou 3 maior(1,3,2) retornou 3 maior(2,1,3) retornou 3 maior(2,3,1) retornou 3 maior(3,1,2) retornou 3 maior(3,2,1) retornou 3 são 6 as combinações de 3 elementos 2 a 2, dizia o ensino médio... Se você testar as 6 estará coberto. Por isso os 6 testes em main() É claro que há muitas outras maneiras de escrever, como o exemplo citado por @devair1010 .usando condições compostas e tal. Mas else e condições compostas são dispensáveis porque a solução é, digamos, linear. Eis um programa de teste, mas acho que nem precisa disso int maior(int A, int B, int C) { if (A > B) { if (A > C) return A; return C; } // entao ==> B > A if (B > C) return B; // entao ==> B > A mas ==> C > B return C; } // end maior() int testa_maior(int a, int b, int c) { printf("maior(%d,%d,%d) retornou %d\n", a, b, c, maior(a, b, c)); } int main(int argc, char** argv) { testa_maior(1, 2, 3); testa_maior(1, 3, 2); testa_maior(2, 1, 3); testa_maior(2, 3, 1); testa_maior(3, 1, 2); testa_maior(3, 2, 1); }
  20. Olá! Não entendo a lógica por trás do que tentou escrever na função... Por exemplo if(A>B) return A; return B; Mas e onde entra C? e se C for o maior? E se você vai de qualquer forma retornar, pra que serve o resto do código da função? Seu compilador aceitou isso? Era pra dar erro por 'código inatingível' ou sei lá o que...
  21. Olá! Ajude o pessoal a ajudar você e poste o programa completo ou um link para ele. Não deve ser muito grande afinal Não posso olhar tudo agora, mas posso dar uns palpites e um brinde. Quando e se você postar o programa e se ainda precisar de ajuda eu olho de novo. Os palpites Não misture as coisas: Ler os valores e digitar a estrutura completa vai fazer você perder uma enormidade de tempo pra testar seu programa. Computadores são muito bons em recortar e colar coisas então faça o programa aos poucos. Imagine sua lista por exemplo: Vai cadastrar os alunos a partir de um ou outro campo, tipo o CPF ou o nome e eventualmente o o nome e o CPF caso tenha caras com o mesmo nome. Então cadastre inicialmente só esses campos chave na estrutura. Quando estiver funcionando você inclui o resto na função de leitura Crie uns valores direto no programa usando algo como struct Cadastro c1, c2, c3, c4; char *nome1 = "Igor"; char *cpf1 = "123.456.789.01"; char *nome2 = "Vargas"; char *cpf2 = "123.456.789.02"; char *nome3 = "Clube"; char *cpf3 = "123.456.789.03"; char *nome4 = "Hardware"; char *cpf4 = "123.456.789.04"; strcpy( c1.nome, nome1 ); strcpy( c1.cpf, cpf1); strcpy( c2.nome, nome2 ); strcpy( c1.cpf, cpf2); strcpy( c3.nome, nome3 ); strcpy( c3.cpf, cpf3); strcpy( c4.nome, nome4 ); strcpy( c4.cpf, cpf4); Usando valores conhecidos e diferentes mas quase iguais para poder usar recortar e colar Teste sua lista mudando a ordem de inserção: teste inserir no ínício, no fim e no meio, inserir todos já na ordem e inserir todos ao contrário. Com 4 elementos já dá pra testar tudo. Antes de tudo crie a função que lista os valores do cadastro. Não perca tempo. Você precisa testar os encadeamentos nos dois sentidos então faça sua função lista() mostrar os caras usando o ponteiro prox até NULL e aí liste usando o ponteiro pra tras ant até NULL e você testa de uma vez se está construindo certo a estrutura. E o brinde? Seria legal se você tivesse uma função que criasse um aluno do jeito que você precisa, mas numerando os alunos a partir de "Aluno-0000" e os CPF a partir de "123.456.700.00" em diante e a cada chamada ela fosse aumentando os últimos números até, sei lá, 9999 Assim você poderia testar com 0 até 10.000 alunos na sua lista à vontade sem ter nem que copiar e colar. Ou pior, digitar na entrada do programa. Para 15 alunos por exemplo criaria assim: Nome: [Aluno-0000] CPF: [123.456.700-00] Nome: [Aluno-0001] CPF: [123.456.700-01] Nome: [Aluno-0002] CPF: [123.456.700-02] Nome: [Aluno-0003] CPF: [123.456.700-03] Nome: [Aluno-0004] CPF: [123.456.700-04] Nome: [Aluno-0005] CPF: [123.456.700-05] Nome: [Aluno-0006] CPF: [123.456.700-06] Nome: [Aluno-0007] CPF: [123.456.700-07] Nome: [Aluno-0008] CPF: [123.456.700-08] Nome: [Aluno-0009] CPF: [123.456.700-09] Nome: [Aluno-0010] CPF: [123.456.700-10] Nome: [Aluno-0011] CPF: [123.456.700-11] Nome: [Aluno-0012] CPF: [123.456.700-12] Nome: [Aluno-0013] CPF: [123.456.700-13] Nome: [Aluno-0014] CPF: [123.456.700-14] Um programa de teste de 10 linhas para uma hipotética função le_aluno() int main() { Aluno* aluno_lido = NULL; for (int i = 0; i < 15; ++i) { aluno_lido = le_aluno(); printf("Nome: [%s] CPF: [%s]\n", aluno_lido->nome, aluno_lido->cpf); free(aluno_lido); } // end for return 0; } E uma função le_aluno()? Veja uma: a cada vez que você chama ela cria um aluno novinho e devolve o endereço dele e aí você pode inserir na sua lista. E quando estiver certo você troca essa rotina por outra que lê mesmo os caras da entrada padrão... static Aluno* le_aluno() { // essa rotina pode salvar horas: a cada vez que e chamada // devolve o endereço de um aluno bem comportado com nome a partir de // "Aluno-0000" e CPF "123.456.7XY- ZT" // limitando o teste a 'apenas' 10.000 alunos // so que não precisa ler nenhum static int quantos = -1; char* pref_nome = "Aluno-"; char* pref_CPF = "123.456.7"; char valor[50]; quantos++; int X = quantos / 1000; int Y = (quantos % 1000) / 100; int Z = (quantos % 100)/ 10; int T = quantos % 10; // monta o nome do aluno Aluno* pAluno = (Aluno*)malloc(sizeof(Aluno)); if(pAluno == NULL) return NULL; sprintf(valor, "%s%d%d%d%d", pref_nome, X, Y, Z, T); strcpy(pAluno->nome, valor); sprintf(valor, "%s%1d%1d-%1d%1d", pref_CPF, X, Y, Z, T); strcpy(pAluno->cpf, valor); return pAluno; } // end le_akuno() Claro que tem maneiras menos preguiçosas de escrever isso mas já é tarde. Não tem nada de especial na rotina, exceto talvez a variável quantos declarada como static para não perder o valor entre cada chamada. e as chamadas a sprintf() para formatar os valores sem esquentar a cabeça com índices e tal... Obviamente você pode usar essa rotina e criar a estrutura inteira preenchida usando a mesma técnica, e mesmo depois ir lendo os valores aos poucos e usando os gerados por ela pra ganhar mais um tempo... Boa sorte aí com o trabalho
  22. Não, acho que não entendeu... Nós já discutimos isso... Você cortou a linha que imprime o último... Não é o "meu código" mas sim o "seu enunciado"... E você falava sobre isso printf("vetor:\n"); for (int i = 0; i < n - 1; i++) printf("%d, ", vetor[i]); printf("%d.\n", vetor[n - 1]); E mesmo assim cortou a última linha Sim, já falamos disso... Apenas imprima a posição e o valor Mas preste atenção: o enunciado diz o contrário e eu te mostrei um exemplo disso no tópico #2 Apenas um loop, e você imprime o par valor, i E nada disse sobre ordenar o vetor... precisa escrever isso mesmo tendo uma função pra isso na linguagem? Entendeu o que te expliquei no programa de teste? Pode simplesmente usar qsort() como expliquei? Pouca chance de a gente escrever algo melhor ou achar algo melhor na internet...
  23. @Ivan de Freitas Correa Bom que conseguiu uma resposta. Note que aqueles dois comandos for que estão logo acima na mensagem que deixei tambem sao solucao, mas veja tambem que vocë precisou usar a linha if (i%2 != 0)... para condicionar a execuçao do write, ja que o for esta rodando o dobro de vezes. Nao e meu aluno, mas sugiro fazer o simples. O mais simples e correto e apenas incrementar a variavel do primeiro for de dois em dois a cada loop e assim gerar os valores com o numero minimo de iteracoes. Veja o comando i = i + 2 logo abaixo
  24. Olá! Em C++ bool é um tipo e imagino que seja só isso que o enunciado quer destacar. Em C zero é falso, o resto é verdade Em C++ tem as constantes true e false e as variáveis podem ser declaradas como bool Exemplo par(0) 1 par(1) 0 o tipo de 'outro'=bool o tipo de 'par()'=bool __cdecl(int) falso outro-par(3) 0 É o que esse programa mostraria // par.cpp : This file contains the 'main' function. Program execution begins and ends there. // #include <iostream> using namespace std; bool par(int valor) { return valor % 2 == 0; } auto outropar(int valor) -> int{ return valor % 2 == 0; } int main() { std::cout << "par(0) " << par(0) << endl; std::cout << "par(1) " << par(1) << endl; auto valor = true; bool outro = false; decltype(outro) outroBool; cout << "o tipo de 'outro'=" << typeid(outro).name() << endl; cout << "o tipo de 'par()'=" << typeid(par).name() << endl; outro = par(3); outroBool = outropar(8); outroBool = false; if (outro) { cout << "verdadeiro outro-par(3) " << outro << endl; } else { cout << "falso outro-par(3) " << outro << endl; } } // end main() Note que a função par() retorna bool como esperado Em C++ true e false são constantes comuns Em C++ se pode declarar uma variável como sendo do tipo de outra usando decltype(outra). Isso pode ser muito útil porque às vezes o tipo da variável só é determinado quando o programa está rodando. Em C++ se pode declarar variáveis como auto e o compilador vê pelo contexto que tipo deve ser usado. Útil quando o seu tipo é muito grande pra ficar digitando toda hora, mas mostra que ele entende que auto v = false; deve gerar v como bool. Em C++ se pode interrogar o tipo de uma variável usando typeid(outro).name() como no programa acima Em C a função int par(int valor) { return valor % 2 == 0; } // end par() faria a mesma coisa e um teste assim if (par(2)) { printf("dois e par\n"); } else { printf("dois nao e par\n"); } funciona como esperado porque o if avalia a expressão como true/false de todo modo. Se você imprimir o resultado de uma expressão vai mostrar zero somente quando ela for falsa...
  25. Talvez tenha entendido errado. Foi usado esse índice apenas porque nesse caso --- o ultimo --- não vai usar a vírgula e sim o ponto depois do cara. Então até o penúltimo você usa o loop que imprime com a vírgula depois. E apenas para o último imprime com o ponto depois Entendeu? Eu suspeitava. Era muito fácil pra ser só isso: imprimir de dois jeitos a mesma coisa A própria biblioteca padrão do C tem uma rotina para classificar. Ao menos uma. Nem precisa do Google. é declarada assim: void qsort( void* endereco_inicial, size_t total_de_items, size_t tamanho_de_cada_um, int (*funcao_que_compara)(const void *, const void*)) E ela classifica o próprio vetor. No seu caso não tem problema porque não vai usar mais. São só 4 parâmetros e pode parecer assustador mas na verdade é bem óbvio... onde estão os valores a classificar: você passa o endereço do vetor claro quantos são: fácil: são 30 qual o tamanho deles? Fácil: sizeof(int) Como saber qual de dois deles é maior? Esse é o caso de uma função: você escreve uma que retorna um número menor que zero se o primeiro é menor, 0 se os dois são iguais e um número maior que 0 se o segundo é maior Só que no seu caso os elementos são inteiros então basta retornar (a-b) certo? afinal se (a>b) a diferença é positiva se (a-b) a diferença é zero se (a<b) (a-b) é sem surpresas negativo Então int funcao_que_compara(const void* a, const void* b) { int* primeiro = (int*)a; int* segundo = (int*)b; return -(*primeiro - *segundo); } Esses parâmetros estranhos são porque essa rotina tem que funcionar para comparar qualquer coisa: int ou float ou structs ou sei lá. Então se você tiver uma struct com nome CEP e RG e CPF você pode ter uma rotina que classifica por cada critério desses e pode classificar por CEP ou nome ou RG só mudando o nome da função. Esse é um recurso muito importante. A história curta é que basta passar o nome da função. Também por isso C é tão versátil qsort(vetor, 30, sizeof(int), funcao_que_compara); classifica os caras em ordem decrescente, por causa desse comando return -(*primeiro - *segundo); Note o sinal '-' que inverte a conta. Se usar return (*primeiro - *segundo); vai classificar em ordem crescente, claro. Basta ver a definição. Por favor não diga que eu poderia ter invertido os operandos e simplesmente ter escrito return (*segundo - *primeiro); Eu só preferi manter na ordem. No seu programa o valor de n não está definido... Talvez devesse declarar de imediato int n = 30; como no programa de teste Em tempo: se precisar realmente escrever você uma rotina para classificar os caras escreva de novo. No enunciado não fala nada e isso faz parte do C desde sempre. qsort() é uma rotina muito otimizada. Eis um programa de teste que imprime isso vetor: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29. vetor: 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60. vetor: 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60. vetor: 60, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60. vetor: 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60. vetor: 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60. vetor: 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60. ***** em ordem decrescente vetor: 60, 58, 56, 54, 52, 50, 48, 46, 44, 42, 40, 38, 36, 34, 32, 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2. ***** em ordem crescente vetor: 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60. Assim você vê como é esperto o lance da função... #define _CRT_SECURE_NO_WARNINGS #include "memory.h" #include <stdio.h> #include <stdlib.h> #include <string.h> int compara_crescente(const void*, const void*); int funcao_que_compara(const void*, const void*); int mostra(int[], int); int main(int argc, char** argv) { int vetor[30]; int n = 30; int i; int valor; for (int i = 0; i < n; i++) vetor[i] = i; mostra(vetor, 30); // 1 for (int i = 0; i<30; i = i+1) { vetor[i] = i + i + 2; } mostra(vetor, 30); // 2 i = 0; for (int valor = 2; valor <= 60; valor = valor + 2) { vetor[i] = valor; i = i + 1; } mostra(vetor, 30); //3 valor = 2; i = 0; do { vetor[i] = valor; valor += 2; } while (valor <= 60); mostra(vetor, 30); //4 valor = 2; i = 0; while (i < 30) { vetor[i] = valor; valor += 2; i = i + 1; } mostra(vetor, 30); //5 for (valor = 2, i = 0; i < 30; i = i + 1, valor=valor+2) { vetor[i] = valor; } mostra(vetor, 30); //6 valor = 2; i = 0; de_novo: vetor[i] = valor; i++; valor += 2; if (i < 30) goto de_novo; mostra(vetor, 30); printf("\n\n***** em ordem decrescente\n"); qsort(vetor, 30, sizeof(int), funcao_que_compara); mostra(vetor, 30); printf("\n\n***** em ordem crescente\n"); qsort(vetor, 30, sizeof(int), compara_crescente); mostra(vetor, 30); return 0; } int compara_crescente(const void* a, const void* b) { int* primeiro = (int*)a; int* segundo = (int*)b; return (*primeiro - *segundo); } int funcao_que_compara(const void* a, const void* b) { int* primeiro = (int*)a; int* segundo = (int*)b; return -(*primeiro - *segundo); } int mostra(int vetor[], int n) { printf("vetor:\n"); for (int i = 0; i < n - 1; i++) printf("%d, ", vetor[i]); printf("%d.\n", vetor[n - 1]); return 0; } // end mostra()

Sobre o Clube do Hardware

No ar desde 1996, o Clube do Hardware é uma das maiores, mais antigas e mais respeitadas comunidades sobre tecnologia do Brasil. Leia mais

Direitos autorais

Não permitimos a cópia ou reprodução do conteúdo do nosso site, fórum, newsletters e redes sociais, mesmo citando-se a fonte. Leia mais

×
×
  • Criar novo...

LANÇAMENTO!

eletronica2025-popup.jpg


CLIQUE AQUI E BAIXE AGORA MESMO!