Ir ao conteúdo
  • Comunicados

    • Gabriel Torres

      Seja um moderador do Clube do Hardware!   12-02-2016

      Prezados membros do Clube do Hardware, Está aberto o processo de seleção de novos moderadores para diversos setores ou áreas do Clube do Hardware. Os requisitos são:   Pelo menos 500 posts e um ano de cadastro; Boa frequência de participação; Ser respeitoso, cordial e educado com os demais membros; Ter bom nível de português; Ter razoável conhecimento da área em que pretende atuar; Saber trabalhar em equipe (com os moderadores, coordenadores e administradores).   Os interessados deverão enviar uma mensagem privada para o usuário @Equipe Clube do Hardware com o título "Candidato a moderador". A mensagem deverá conter respostas às perguntas abaixo:   Qual o seu nome completo? Qual sua data de nascimento? Qual sua formação/profissão? Já atuou como moderador em algo outro fórum, se sim, qual? De forma sucinta, explique o porquê de querer ser moderador do fórum e conte-nos um pouco sobre você.   OBS: Não se trata de função remunerada. Todos que fazem parte do staff são voluntários.
    • DiF

      Poste seus códigos corretamente!   21-05-2016

      Prezados membros do Fórum do Clube do Hardware, O Fórum oferece um recurso chamado CODE, onde o ícone no painel do editor é  <>     O uso deste recurso é  imprescindível para uma melhor leitura, manter a organização, diferenciar de texto comum e principalmente evitar que os compiladores e IDEs acusem erro ao colar um código copiado daqui. Portanto convido-lhes para ler as instruções de como usar este recurso CODE neste tópico:  
Entre para seguir isso  
Werex

[Resolvido] Problemas com memória utilizada pelo programa.

Recommended Posts

Ola, estou fazendo um programa em c++ utilizando SDL. Porém, meu programa está usando valores absurdos de memória RAM. Ele é um programa simples mas inicia utilizando 40000 K de memória e vai subindo de 1000 em 1000 até valores absurdamente altos.

Eu ouvi dizer que isso poderia ser "Memory Leak". De acordo com o que pesquisei na internet parece ser isso mesmo. Porém, por mais que eu pesquise não faço ideia de como resolver isso.

Se alguem puder me explicar melhor como corrigir esse problema ou apenas me indicar uma fonte em português sobre o assunto, eu agradeceria.

Obrigado pela ajuda pessoal.

Editado por Werex
Colocar dados que eu não havia colocado.

Compartilhar este post


Link para o post
Compartilhar em outros sites
  • Autor do tópico
  • Desculpem por não ter postado o código antes, mas eu achei que ele estava grande de mais para postar aqui e fazer vocês ficarem procurando errinhos. Não tenho muita ideia de onde entá o erro, por isso postei tudo. Mas depois do código colocarei uma explicação mais detalhada do que está acontecendo.


    #include <stdlib.h>
    #include <SDL.h>
    #include <windows.h>
    #include <SDL_ttf.h>
    #include <sstream>
    #include <math.h>
    #include "SDL_rotozoom.h"
    using namespace std;




    char* DoubleToChar(double x){
    char c[100];
    sprintf(c,"%f",x);
    return c;
    }

    struct vetor{
    double x;
    double y;

    };

    int aproximar(int d){
    int i;
    if(d%1>0.5){
    i=d+(1-(d%1));
    }else{
    i=d-(d%1);
    }
    return i;
    }




    //função dos vetores
    vetor somarvetores(vetor vetor1,vetor vetor2){
    vetor1.x=vetor1.x + vetor2.x;
    vetor1.y=vetor1.y + vetor2.y;

    return vetor1;
    }

    //MAIN
    int main ( int argc, char** argv ){

    SDL_Init(SDL_INIT_EVERYTHING);
    TTF_Init();

    restart:
    double pii=3.15;
    double testenum;
    double passou;
    double gravidadevalor;

    int jh=900;
    int jw=1440;
    int FPS=70;
    int cvm=1;
    int cursory1,cursory2,cursorx1,cursorx2;
    int Gravidade=50;
    int vrr=0;
    int vra;
    int ang=359;
    int raio=26;

    bool pause = false;
    bool holding=false;
    bool running = true;
    bool restart = false;



    SDL_Surface *janela;
    SDL_WM_SetCaption("Teste vetores", NULL);
    janela = SDL_SetVideoMode(jw,jh,32,SDL_SWSURFACE);

    SDL_Surface *icone;
    icone = SDL_DisplayFormat(SDL_LoadBMP("bola.bmp"));
    SDL_SetColorKey(icone,SDL_SRCCOLORKEY,SDL_MapRGB(janela->format, 0xff,0,0xff));
    SDL_WM_SetIcon(icone,NULL);

    SDL_Surface *bola[ang];
    for(ang=0;ang<360;ang++){
    bola[ang]=SDL_DisplayFormat(SDL_LoadBMP("imagens/bola_Nova.bmp"));
    SDL_SetColorKey(bola[ang],SDL_SRCCOLORKEY,SDL_MapRGB(janela->format,0xff,0,0xff));
    bola[ang] = rotozoomSurface(SDL_DisplayFormat(bola[ang]), ang,1,0);

    }
    SDL_Surface *brilho[ang];
    for(ang=0;ang<360;ang++){
    brilho[ang]=SDL_DisplayFormat(SDL_LoadBMP("imagens/brilho.bmp"));
    SDL_SetColorKey(brilho[ang],SDL_SRCCOLORKEY,SDL_MapRGB(janela->format,0xff,0,0xff));
    brilho[ang] = rotozoomSurface(SDL_DisplayFormat(brilho[ang]), ang,1,0);
    SDL_SetAlpha(brilho[ang],SDL_SRCALPHA,150);

    }
    ang=0; //apenas ajeita o ângulo para que o rect não crashe o aplicativo

    SDL_Surface *texto;
    SDL_Color cortexto = {0,0,0};
    TTF_Font *brewmastermodern = TTF_OpenFont("fontes/arial.ttf",50);




    SDL_Rect pbola;
    pbola.w=52;
    pbola.h=52;
    pbola.y=pbola.h;
    pbola.x=jw/2;

    SDL_Rect rbola;
    rbola.w=bola[ang]->w;
    rbola.h=bola[ang]->h;
    rbola.y=rbola.h;
    rbola.x=jw/2;

    SDL_Rect sbola;
    sbola.w=bola[ang]->w;
    sbola.h=bola[ang]->h;





    SDL_Rect teste;
    teste.h=1;
    teste.w=100;
    teste.y=pbola.y;
    teste.x=jw/2;



    SDL_Rect rtexto;


    SDL_Event evento;

    //vetores

    vetor cursor;

    vetor gravidade;
    gravidade.y=1;
    gravidade.x=0;

    vetor atritoar;
    atritoar.x=0.2;


    vetor velocidade;
    velocidade.y=0;
    velocidade.x=0;

    //ang=390;
    //inicio do Loop do jogo

    while(running == true){
    int inicio = SDL_GetTicks();



    //eventos
    while(SDL_PollEvent(&evento)){
    switch(evento.type){
    case SDL_QUIT:
    running=false;
    break;
    case SDL_MOUSEMOTION:
    cursor.x = evento.motion.x;
    cursor.y = evento.motion.y;
    //mouse down
    case SDL_MOUSEBUTTONDOWN:
    switch(evento.button.button){

    case SDL_BUTTON_LEFT:
    if(pbola.x<cursor.x && cursor.x<pbola.x+pbola.w && pbola.y<cursor.y && cursor.y<pbola.y+pbola.h)
    holding=true;

    break;


    }
    break;
    //mouse up
    case SDL_MOUSEBUTTONUP:
    switch(evento.button.button){
    case SDL_BUTTON_LEFT:
    if(holding==true){
    if(cvm==1){
    velocidade.x=cursorx2-cursorx1;
    velocidade.y=cursory2-cursory1;
    }else{
    velocidade.x=cursorx1-cursorx2;
    velocidade.y=cursory1-cursory2;
    }
    }
    holding=false;
    }
    break;

    //teclado
    case SDL_KEYDOWN:
    switch (evento.key.keysym.sym){

    case SDLK_ESCAPE:
    running=false;
    break;

    case SDLK_BACKSPACE:
    velocidade.x=0;
    velocidade.y=0;
    break;
    case SDLK_r:
    restart = true;
    running=false;
    break;
    case SDLK_p:
    pause=true;
    }
    }
    break;
    }

    //ajuda a calcular velocidade do cursor
    if(cvm==1){
    cursorx1=cursor.x;
    cursory1=cursor.y;
    cvm=2;
    }else{
    cursorx2=cursor.x;
    cursory2=cursor.y;
    cvm=1;
    }

    //pause
    while(pause==true){
    while(SDL_PollEvent(&evento)){
    switch(evento.type){
    case SDL_KEYDOWN:
    switch(evento.key.keysym.sym){
    case SDLK_p:
    pause=false;
    break;
    }
    break;

    }
    }

    }


    //holding
    if (holding==true){
    pbola.y=cursor.y-(pbola.h/2);
    pbola.x=cursor.x-(pbola.w/2);
    velocidade.x=0;
    velocidade.y=0;
    }else{
    pbola.x=pbola.x+velocidade.x;
    pbola.y=pbola.y+velocidade.y;
    }



    //colisão paredes

    if(pbola.y<=0){

    pbola.y=0;
    velocidade.y=velocidade.y*-1;
    }

    if(pbola.x+pbola.w>=jw){
    pbola.x=jw-pbola.w;
    velocidade.x=velocidade.x*-1;
    }

    if(pbola.x<=0){
    pbola.x=0;
    velocidade.x=velocidade.x*-1;
    }

    if(pbola.y+pbola.h>=jh){
    pbola.y=jh-pbola.h;
    velocidade.y=velocidade.y*-1;
    vrr=360*(velocidade.x/(2*(pii*26)));


    }else if(holding == true){


    }else{
    //soma dos vetores
    velocidade = somarvetores(velocidade,gravidade);
    }
    vra=round(vrr);
    ang=ang+(-vra);




    //correção do ângulo

    if(ang>359){
    ang=0+(ang-360);
    }else if(ang<0){
    ang=360-(0-ang);
    }


    sbola.y=((bola[ang]->h)/2)-raio;
    sbola.x=((bola[ang]->w)/2)-raio;


    //atualizando textos
    texto=TTF_RenderText_Solid(brewmastermodern,DoubleToChar(ang),cortexto);
    rtexto.h=texto->h;
    rtexto.w=texto->w;
    rtexto.y=jh-rtexto.h;
    rtexto.x=jw-rtexto.w;

    //corrigindo movimento
    rbola.y=pbola.y;
    rbola.x=pbola.x;
    if(pbola.y%1>=0.5){
    rbola.y= rbola.y+(1-(pbola.y%1));
    }else if(pbola.y%1<0.5){
    rbola.y=rbola.y-(pbola.y%1);
    }
    if(pbola.x%1>0.5){
    rbola.x= rbola.x+(1-(pbola.x%1));
    }else if(pbola.y%1<0.5){
    rbola.x=rbola.x-(pbola.x%1);
    }
    //sbola.x=pbola.x+(pbola.w/2)-(sbola.w/2);
    //sbola.y=pbola.y+(pbola.h/2)-(sbola.h/2);
    //printing
    SDL_FillRect(janela,&janela->clip_rect,SDL_MapRGB(janela->format,0xdd,0xdd,0xdd));
    SDL_FillRect(janela,&teste,SDL_MapRGB(janela->format,0,0,0xff));
    SDL_BlitSurface(bola[ang],&sbola,janela,&pbola);
    SDL_BlitSurface(brilho[0],NULL,janela,&pbola);
    SDL_BlitSurface(texto,NULL,janela,&rtexto);


    SDL_Flip(janela);
    if((SDL_GetTicks()- inicio)<(1000/FPS)){
    SDL_Delay((1000/FPS)-(SDL_GetTicks()-inicio));
    }
    }

    //liberando memória
    for(ang=0;ang<360;ang++){
    SDL_FreeSurface(bola[ang]);
    }
    for(ang=0;ang<360;ang++){
    SDL_FreeSurface(brilho[ang]);
    }
    SDL_FreeSurface(janela);
    SDL_FreeSurface(texto);
    TTF_CloseFont(brewmastermodern);

    if(restart==true){
    goto restart; //restart
    }

    TTF_Quit();
    SDL_Quit();

    return 0;
    }

    O programa também tem um acréscimo de memória quando aperto o botão "restart" que criei, a princípio pensei que esse acréscimo era causado pelo carregaento das imagens, mas mesmo depois que reorganizei o código para que o "restart" só ocorra depois dos "SDL_FreeSurface" o problema continua. Portanto, acredito que o pricipais suspeitos sejam as classes e structures que criei ou talvez até as variáveis.

    Ah, antes que eu me esqueça. Sei que carregar 359 imagens de uma bolinha é um exagero, mas foi o única maneira que encontrei para fazer o programa funcionar com o rotozoom.h. Quando tentei fazer o programa calcular a rotação da bola em tempo real, o programa parou de funcionar, então a solução foi carregar tudo antes. Aproveitando o fato de que postei o código inteiro, gostaria de saber se tem uma maneira de otimizar isso também. Isso também vai ajudar a reduzir a memória utilizada pelo programa.

    E lembrem-se, o código não está terminado, então se encontrarem algo inútil no meio é porque eu vou usar ainda. Ah, e me perdoem se eu cometi alguma "noobisse" no meio do código. Sou iniciante.

    Obrigado pela atenção de todos.

    Editado por dif
    Usar a tag SPOILER quando seu código for extenso demais.

    Compartilhar este post


    Link para o post
    Compartilhar em outros sites

    Eu estou usando o g++ no linux e por isso não deve ter compilado então fica difícil te ajudar...

    Compartilhar este post


    Link para o post
    Compartilhar em outros sites
  • Autor do tópico
  • Não sei se isso vai ajudar, mas aqui está um link para o download do programa:

    http://www.4shared.com/rar/pEozZ0BC/testevetores.html

    Quando você abrir o programa, veja a memória utilizada pelo programa no gerenciador de tarefas. Você vai notar que o uso de memória vai subindo cada vez mais.

    Controles:

    p- Pausa

    r- Restart - Esse comando adiciona uns 20000 k de utilização de memória pelo programa, ele é uma das fontes do problema.

    Backspace- Zera a velocidade da bola.

    Vou dar mais uma pesquisada aqui para ver se eu acho uma solução. Se eu conseguir, eu posto aqui.

    Ah, e obrigado pela atenção Waltton ;)

    Editado por Werex
    Complementar dados.

    Compartilhar este post


    Link para o post
    Compartilhar em outros sites

    Olhando "por cima" parece que você esta criando uma nova "Surface" com o texto a cada interação do loop.


    texto=TTF_RenderText_Solid(brewmastermodern,DoubleToChar(ang),cortexto);

    e você só esta liberando ela da memoria no final do programa ao invés do final do loop.

    A função SDL_LoadBMP retorna uma "surface" e a SDL_DisplayFormat cria uma copia em outro formato, portanto você tem que liberar a "surface" criada pela SDL_LoadBMP após a "conversão".


    for(ang=0;ang<360;ang++){
    bola[ang]=SDL_DisplayFormat(SDL_LoadBMP("imagens/bola_Nova.bmp"));
    SDL_SetColorKey(bola[ang],SDL_SRCCOLORKEY,SDL_MapRGB(janela->format,0xff,0,0xff));
    bola[ang] = rotozoomSurface(SDL_DisplayFormat(bola[ang]), ang,1,0);

    }
    SDL_Surface *brilho[ang];
    for(ang=0;ang<360;ang++){
    brilho[ang]=SDL_DisplayFormat(SDL_LoadBMP("imagens/brilho.bmp"));
    SDL_SetColorKey(brilho[ang],SDL_SRCCOLORKEY,SDL_MapRGB(janela->format,0xff,0,0xff));
    brilho[ang] = rotozoomSurface(SDL_DisplayFormat(brilho[ang]), ang,1,0);
    SDL_SetAlpha(brilho[ang],SDL_SRCALPHA,150);

    }

    Corrija esses problemas, depois procuraremos mais.

    Compartilhar este post


    Link para o post
    Compartilhar em outros sites
  • Autor do tópico
  • Muito obrigado hacker_wap! Só foi liberar a superfície do texto no final do loop que o uso de memória já se estabilizou. Você entende mesmo do assunto heim.^_^

    Então espera aí, deixe-me ver se entendi: Sempre que eu igualo uma surface à alguma coisa:


    SDL_Surface *texto;
    texto=SDL_LoadBMP(imagem1);

    eu não estou substituindo o que eu havia antes nessa surface e sim adicionando algo novo, por isso o uso de memória aumenta? Isso significa que sempre que eu quiser modificar a imagem contida na surface eu preciso liberar o que tem nela antes?

    exemplo:


    SDL_Surface *texto;
    texto=SDL_LoadBMP(imagem1);
    SDL_FreeSurface(texto);
    texto=SDL_LoadBMP(imagem2);

    Ah, e estou trabalhando para corrigir os outros erros. Agora que já conheço os principais suspeitos ficará mais fácil de resolver. Quando eu conseguir eu postarei as modificações realizada. Obrigrado pela atenção de todos ;)

    Compartilhar este post


    Link para o post
    Compartilhar em outros sites

    Conforme solicitado, estamos reabrindo este tópico!

    Compartilhar este post


    Link para o post
    Compartilhar em outros sites

    Então espera aí, deixe-me ver se entendi: Sempre que eu igualo uma surface à alguma coisa:


    SDL_Surface *texto;
    texto=SDL_LoadBMP(imagem1);

    Basicamente é isso sim, a variavel texto é um ponteiro para uma surface(armazena o endereço de uma), SDL_LoadBMP cria uma surface baseada no arquivo e retorna um ponteiro para ela, quando você faz a atribuição você troca o endereço que texto aponta, o conteúdo que ele apontava antes continua existindo.

    Então sim, sempre que você quiser trocar o conteúdo que a variável aponta, o anterior deve ser liberado.

    Nesse trecho aqui.


    brilho[ang]=SDL_DisplayFormat(SDL_LoadBMP("imagens/brilho.bmp"));

    o retorno da SDL_LoadBMP esta ficando perdido pois não esta sendo armazenado em lugar nenhum.

    Compartilhar este post


    Link para o post
    Compartilhar em outros sites
  • Autor do tópico
  • SDL_Surface  *bolac[ang];
    SDL_Surface *bola[ang];
    for(ang=0;ang<360;ang++){
    bolac[ang]=SDL_LoadBMP("imagens/bola_Nova.bmp");
    SDL_SetColorKey(bolac[ang],SDL_SRCCOLORKEY,SDL_MapRGB(janela->format,0xff,0,0xff));
    bolac[ang] = rotozoomSurface(bolac[ang], ang,1,0);
    bola[ang] = SDL_DisplayFormat(bolac[ang]);
    SDL_SetColorKey(bola[ang],SDL_SRCCOLORKEY,SDL_MapRGB(janela->format,0xff,0,0xff));
    SDL_FreeSurface(bolac[ang]);

    }

    Assim está correto? O problema desse método que usei é que o código parece ficar muito poluído já que tenho que criar duas surfaces para que tudo funcione. Isso é normal? Foi a única maneira que consegui fazer funcionar junto com o color key. E realmente reduziu em muito o uso de memóra. Antes começava com 40000k agora somente com 27000k. Antes toda vez que eu apertava o botão "restart" o programa ganhava 20000k de memória e agora com meus vários testes de tentativa e erro consegui um mínimo de 10000k por restart. Muito obrigado hacker_wap.

    Fiz algumas pesquisas e descobri algumas coisas interessantes sobre os comandos que estou usando.

    Esta página até tem um aviso sobre o memory leak que o SDL_DisplayFormat pode gerar:

    http://sdl.beuc.net/sdl.wiki/SDL_DisplayFormat

    As vezes parece que o código fica melhor sem o SDL_DisplayFormat.

    Ah, e desculpem por estar prolongando demais esse tópico, mas já pesquisei em inglês, português e até em espanhol e não consegui achar aguma coisa que me ajude efetivamente a resolver esse problema. Até pensei em modificar o funcionamento botão de restart, mas isso implicaria em uma mudaça brusca na estrutura do código ou poderia me trazer problemas mais tarde. Além disso, é melhor aprender a consertar isso agora do que jogar o problema para baixo do tapete e futuramente precisar criar outro tópico para resolver isso. Mais uma vez agradeço a atenção de todos.

    Editado por Werex
    complementos

    Compartilhar este post


    Link para o post
    Compartilhar em outros sites

    pelo que eu andei lendo rotozoomSurface também retorna uma nova surface, então você tem que liberar ela também.

    eu faria esse trecho da seguinte forma.


    SDL_Surface *bolas[ang];
    SDL_Surface *bola = SDL_LoadBMP("imagens/bola_Nova.bmp");

    SDL_SetColorKey(bola, SDL_SRCCOLORKEY, SDL_MapRGB(janela->format, 0xff,0, 0xff));

    for(ang = 0; ang < 360; ++ang) {
    SDL_Surface *temp = rotozoomSurface(bola, ang, 1, 0);
    bolas[ang] = SDL_DisplayFormat(temp);
    SDL_FreeSurface(temp);
    }

    SDL_FreeSurface(bola);

    Apesar da mudança do código o comportamento deve se manter o mesmo.

    Também faria o mesmo para a parte do brilho, talvez até juntasse as duas em um único loop.

    Quanto ao restart eu colocaria ele bem mais abaixo aonde as variáveis de controle são inicializadas para não ter que fazer o carregando das imagens novamente, atualmente pelo que vi quando ocorre o restart você faz tudo de novo mais não libera as surfaces que existiam.

    Agora sinceramente eu preferiria refazer todo esse código de forma diferente evitando goto, criando funções para realizar tarefas comuns, tentando simplificar o código etc..

    Sobre a SDL_DisplayFormat ela retorna uma surface no formato padrão do vídeo assim fica mais rápido para ele trabalhar com ela, você não precisa realizar a conversão se o desempenho no formato normal esta bom para você, porém lembre-se que ainda que agora o desempenho seja satisfatório futuramente quando você adicionar mais coisas ele pode deixar de ser e você vai ter que alterar o código(eu preferiria já fazer tudo agora).

    de qualquer forma estamos aqui para qualquer duvida :)

    Editado por hacker_wap
    Adicionado comentário sobre a SDL_DisplayFormat

    Compartilhar este post


    Link para o post
    Compartilhar em outros sites
  • Autor do tópico
  • Muito obrigado hacker_wap! O código final ficou assim:

    SDL_Surface *bola[ang];
    SDL_Surface *bolac = SDL_LoadBMP("imagens/bola_Nova.bmp");
    SDL_SetColorKey(bolac,SDL_SRCCOLORKEY,SDL_MapRGB(janela->format,0xff,0,0xff));
    for(ang=0;ang<360;ang++){
    SDL_Surface *temp = rotozoomSurface(bolac,ang,1,0);
    SDL_SetColorKey(temp,SDL_SRCCOLORKEY,SDL_MapRGB(janela->format,0xff,0,0xff));

    //foi necessário usar um segundo SDL_SetColorKey. Se eu retirar qualquer um dos dois SDL_SetColorKey a imagem não carregará adequadamente.

    bola[ang]=SDL_DisplayFormat(temp);
    SDL_FreeSurface(temp);
    }
    SDL_FreeSurface(bolac);

    Agora está tudo completamente estável! :D

    Até quando aperto o botão de restart!

    Eu tive alguns problemas; por algum motivo, quando usei esta mesma técnica com o brilho, o programa começou a crashar depois do primeiro restart. Descobri que a causa era o SDL_FreeSurface(). Porém, decidi esquecer esse nogócio de colocar uma iluminação dinâmica nesse programa e resolvi carregar somente uma imagem de brilho. Isso deixou o programa incrivelmente mais leve. Agora ele apenas usa 13000k de memória.:)

    E realmente, fazer um botão de restart com o comando "goto" foi uma tremenda furada, da próxima vez vou bolar uma maneira mais fácil de criar esse botão. Mas apesar de tudo, tive a oportunidade de aprender mais sobre as surfaces e os comandos básicos do SDL. E agora sei como resolver esses problemas de memory leak quando eu precisar.

    Posteriormente colocarei um modo de interação com botões e em breve postarei aqui o programa completo para quem quiser dar uma olhada no código. E com certeza colocarei você hacker_wap nos "agradecimentos especiais". Muito obrigado!

    Editado por Werex
    corrigir erros
    • Curtir 1

    Compartilhar este post


    Link para o post
    Compartilhar em outros sites

    Caso o autor do tópico necessite, o mesmo será reaberto, para isso deverá entrar em contato com a moderação solicitando o desbloqueio.

    Compartilhar este post


    Link para o post
    Compartilhar em outros sites
    Visitante
    Este tópico está impedido de receber novos posts.
    Entre para seguir isso  





    Sobre o Clube do Hardware

    No ar desde 1996, o Clube do Hardware é uma das maiores, mais antigas e mais respeitadas publicações 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

    ×