Ir ao conteúdo
  • Cadastre-se

C Tipos abstrato de dados em C


mandalori

Posts recomendados

Ola pessoal tudo bem?

Estou com um trabalho de faculdade deste semestre e por causa da pandemia não tivemos mais monitoria por isso se alguém puder me ajudar com meu trabalho agradeço.

o programa a baixo faz uso de TDA em linguagem C e o erro que acontece é exatamente no main.c não consigo mostrar os dados lido no arquivo. Agradeço muito se me ajudarem.

 

/** Programa que exibe na tela dados formatados
  * de um arquivo CSV conforme o padrão:
  *         Series;Value;Time
  *         <int>;<float>;<string>
  *         <int>;<float>;<string>
  */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "interface.h"


int main(){
    int i;
    int n_linhas = 0;

    dado_t **dados = ler_dados_csv("camera_temp.csv", &n_linhas);


    /* Imprima os dados:*/
    printf("Amostra----Temperatura------hora\n");
   /* for (i=0; i < n_linhas; i++){
        //printf( "...", obter_amostra(dados[i]), obter_temperatura(dados[i]), ... );
        printf("%d, %f, %s\n", dados[i]->amostra, dados[i]->temperatura, dados[i]->tempo);
    }
    */
    for (i=0; i < n_linhas; i++){
        //printf( "...", obter_amostra(dados[i]), obter_temperatura(dados[i]), ... );
        dados[i];
        for(int k=0; k<1; k++)
        {
            printf("%d, %f, %s", dados->amostra, dados->temperatura, dados->tempo);
        }

    }


    // liberar_dados();
   liberar_dados(dados);
    return 0;
}

main.c




/* de um arquivo CSV conforme o padrão:
  *         Series;Value;Time
  *         <int>;<float>;<string>
  *         <int>;<float>;<string> */

/* Includes de sistema */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Includes do módulo */
#include "interface.h"


/* Ativa depuração do módulo, comente para código final */
#define DEBUG


struct dados
{
    int amostra;        /*!< Identificador numérido da amostra */
    float temperatura;  /*!< Valor do dado: temperatura */
    char tempo[15];      /*!< Time stamp */
};
typedef struct dados dado_t;

dado_t * criar_dado (int amostra, float temperatura, char * timestamp)
{

    dado_t * meu_novo_dado = malloc(sizeof(struct dados));
    if(meu_novo_dado==NULL)
    {
        perror("abrir o arquivo");
            exit(1);
    }

    /* Novos dados são copiados para nova estrutura vinda do malloc */
    meu_novo_dado->amostra = amostra;
    meu_novo_dado->temperatura=temperatura;
    strcpy(meu_novo_dado->tempo, timestamp);
    //(...)
    //use strncpy para o timestamp


    return meu_novo_dado;
}


/*===========================Lê o arquivo e aloca memoria dinamicamente de acordo com o tamanho do arquivo===================*/
dado_t **ler_dados_csv(char *nome_do_arquivo, int *total_dados)
{
    char buffer[64];
    int i=0;
    /* Demais Variáveis */
    int n_linhas = 0;

    int amostra;
    float temperatura;
    char hora[15];
    /*Cria uma matriz de struct do tipo dado_t*/
    dado_t **dados;

    FILE *fp = fopen(nome_do_arquivo,"r");

    if (!fp)
    {
        perror("ler_dados_csv");
        exit(1);
    }

    /* Ignora primeira linha */
    fgets(buffer,64, fp);

    /* Contar todas as linhas: use while e fgets() */
    while( !feof(fp))
    {
        fgets(buffer, 64, fp);
        i++;
    }
    printf("%d: linhas lidas\n", i);
    /* Aloque memória:
     * Agora é um vetor de ponteiros */
    dados = (dado_t**)malloc(n_linhas*sizeof(dado_t *) );
    /*retorna ao inicio do arquivo*/
    rewind(fp);
    /* Ignora primeira linha */
    fgets(buffer,64, fp);


    while (fscanf (fp, "%d,%f,%14[^\n]s", &amostra, &temperatura, hora) == 3)
    {

#ifdef DEBUG
        printf("%d, %f, %s\n", amostra, temperatura, hora);
#endif

        /* Cria um novo dado abstrato e armazena a sua referência */
        // dados[i] = criar_dado(amostra, temperatura, data);
        dados[i]=criar_dado(amostra, temperatura, hora);
        i++;
    }
    /*total de linhas lidas*/
    *total_dados = i;
    /*retorna */
    return dados;
}


int obter_amostra(dado_t *dado)
{
    return dado->amostra;
}



void liberar_dados(dado_t **vetor)
{

free(vetor);

}

implemetacao.c




#ifndef DADO_H_INCLUDED

/* Criação de tipo abstrato dado_t */
typedef struct dados dado_t;

/**
  * @brief  Cria um novo dado
  * @param  amostra: identificador da amostra
  * @param  temperatura: valor da temperatura
  * @param  timestamp: data e hora da amostra
  *
  * @retval dado_t: ponteiro para uma novo dado
  */
dado_t * criar_dado (int amostra, float temperatura, char * timestamp);


/* Outras funções aqui: fazer os comentários */
void liberar_dados(dado_t **vetor);


dado_t **ler_dados_csv(char *nome_do_arquivo, int *total_dados);

//int obter_amostra(dado_t *dado);


#endif

interface.h

 

camera_temp.csv

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

Bom, vamos erro por erro. Aparentemente, o primeiro ocorre na função ler_dados_csv. Nessa linha:

 

dados = (dado_t**)malloc(n_linhas*sizeof(dado_t *) );

 

Você guardou o numero de linhas do arquivo na variável i e parece ter esquecido de passar o valor pra variável pensada pra isso, n_linhas. De fato n_linhas tá inicializada com zero neste ponto da execução. Então, falta

 

n_linhas = i;

 

depois da contagem e antes de alocar memoria. Lembre-se de limpar i (i = 0) case queira usá-la com o intuito de contagem novamente, o que parece ser o caso. 

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

Ola amigo 'V!OLADOR' tudo bom? obrigado pelo esforço em ajudar, de fato não tinha percebido essa atualização da variavel n_linhas porém eu não sei se você chegou a rodar o codigo mas na main.c acusa um erro de desreferenciar a matriz de ponteiro de ponteiro para struct, infelizmente não consegui entender essa parte procurei no livro c-total ou em alguma outras fontes mas não consegui uma explicação. puder dar mais um help valeu amigo Obrigado pela força.

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

1 hora atrás, mandalori disse:

Ola amigo 'V!OLADOR' tudo bom? obrigado pelo esforço em ajudar, de fato não tinha percebido essa atualização da variavel n_linhas porém eu não sei se você chegou a rodar o codigo mas na main.c acusa um erro de desreferenciar a matriz de ponteiro de ponteiro para struct, infelizmente não consegui entender essa parte procurei no livro c-total ou em alguma outras fontes mas não consegui uma explicação. puder dar mais um help valeu amigo Obrigado pela força.

 

pode postar o programa como está agora?

adicionado 4 minutos depois

 

Series,Value,Time
0,37.9,2018-11-18T08:41:03-02:00
1,37.9,2018-11-18T08:41:13-02:00
2,37.9,2018-11-18T08:41:23-02:00
3,37.9,2018-11-18T08:41:33-02:00
4,37.9,2018-11-18T08:41:43-02:00
5,37.9,2018-11-18T08:41:53-02:00
6,37.9,2018-11-18T08:42:03-02:00

Podia ter postado umas linhas ao menos do csv, certo? 

adicionado 31 minutos depois

talvez não tenha entendido o propósito de um header. É onde deve declarar as estruturas e protótipos.
Se tem um header porque declarou duas vezes o typedef de dado_t?

 

/* Criação de tipo abstrato dado_t */
typedef struct dados dado_t;

Sugiro evitar comentários assim. typedef cria um tipo. Sempre. E no fundo todo tipo de dado é abstrato em um programa de computador. E evite usar acentos nos comentários ou mesmo no programa a menos que seja esse o objetivo. Nem todo computador mostra isso direito. E não acrescenta nada.

 

Não declare variáveis nos protótipo. De nada serve e se errar algo na hora de digitar só vai perder tempo.

struct dados
{
    int     amostra;      /*!< Identificador numérido da amostra */
    float   temperatura;  /*!< Valor do dado: temperatura */
    char    tempo[15];    /*!< Time stamp */
};
typedef struct dados dado_t;

imagino que use essa convenção de sufixo _t para os tipos definidos no programa. 


Boa ideia. 👍

Outros usam a primeira letra em maiúscula como é comum em java, ou prefixos como t_

Mas os comentários não acrescentam muito: o nome já está bem: temperatura para temperatura. tempo para tempo. Talvez pudesse ter colocado a unidade de temperatura e o formato do timestamp no comentário se queria acrescentar algo...

 

Ao invés de 

struct dados
{
    int     amostra;      /*!< Identificador numérido da amostra */
    float   temperatura;  /*!< Valor do dado: temperatura */
    char    tempo[15];    /*!< Time stamp */
};
typedef struct dados dado_t;

Procure declarar 

typedef struct
{
    int     amostra;      /*!< Identificador numérido da amostra */
    float   temperatura;  /*!< Valor do dado: temperatura */
    char    tempo[15];    /*!< Time stamp */
}	dado_t;

se não usa struct dados em outro lugar... Esse é o propósito de typedef afinal...


E em main você declarou
 

dado_t** dados = ler_dados_csv("camera_temp.csv", &n_linhas);

usando o mesmo nome. Só dá confusão.

 

E

        for (int k = 0; k < 1; k++)
        {
            printf("%d, %f, %s", 
                   dados->amostra, 
                   dados->temperatura, 
                   dados->tempo);
        }

 

Se declarou dados como dado_t** então não pode escrever isso...

Pense bem:

se dados é dado_t** então:

  • *dados é dado_t*
  • **dados é então dado_t

poderia escrever (*dados)->amostra mas não dados->amostra porque dados não aponta para uma struct e sim para um ponteiro...

 

Está certo de que precisa declarar assim?

 

Achei seu programa um pouco complicado para ler um arquivo csv que é apenas uma tabela MxN de M registros com N campos definidos por N-1 delimitadores...

 

 

 

 

adicionado 58 minutos depois
2 horas atrás, mandalori disse:

não consegui entender essa parte procurei no livro c-total ou em alguma outras fontes mas não consegui uma explicação

 

Acho que agora entendeu, se leu o que escrevi acima.
 

Provavelmente o que queria escrever lá era o simples

        for (int k = 0; k < 1; k++)
        {
            printf("%d, %f, %s",
                dados[k]->amostra,
                dados[k]->temperatura,
                dados[k]->tempo);
        }

Desde que faça dados apontar de fato para um vetor de ponteiros para dada_t, certo?  Replicando a mesma conveniência que o sistema faz com argc e argv, montando um vetor de ponteiros para strings com todos os argumentos arrumadinhos. Nice!

 

Mas não: precisou de um argumento a mais, claro.

 

Onde iria arrumar o espaço amostral? o argc, o total de amostras? Talvez fosse melhor usar algo assim

struct dados
{
    int     amostra;      /*!< Identificador numérido da amostra */
    float   temperatura;  /*!< Valor do dado: temperatura */
    char    tempo[15];    /*!< Time stamp */
};
typedef struct dados dado_t;

typedef struct
{
    int         N;
    dado_t**    dados;

}    CSV;

Mais simples... Na leitura pode declarar 

CSV*    ler_dados_csv(char*);

// ou mesmo

int	carga(CSV* tabela, const char* arquivo);

// para carregar a tabela a partir do arquivo

Exemplo

CSV* tabela = ler_dados("camera_temp.csv");

// ou

int n = carga(tabela,"arquivo.txt");

Simplesmente imitando o sistema operacional... ;) 

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

Ola! arfneto tudo bom? obrigado pela dedicação amigo, nossa você domina a linguagem tanto de programação quanto em explicar o conteúdo. Não apliquei ainda (neste horário) mas vou aplicar sim para corrigir, explicando sobre a comlexidade do código, ele foi postado pelo próprio professor para para estudar bem sobre a implementação de um TAD, embora que outros programas em nivéis menos complexos de tipos abstratos de dados eu ja domino bem, porém ponteiro para ponteiro preciso melhor muito. Assim que eu aplicar as correções de acordo com sua explicação e funcionar já respondo, finalizando, desculpa por não deixar na descrição o conteúdo do arquivo. Obrigado.

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

8 horas atrás, mandalori disse:

ele foi postado pelo próprio professor para para estudar bem sobre a implementação de um TAD

 

Seu professor perdeu uma oportunidade :) de criar uma abstração importante. Muito da aparente dificuldade desse programa vem de uma abstração pobre, quase inexistente, de um arquivo csv. 

 

Quanto mais perto da realidade o modelo estiver mais simples fica o programa. Um arquivo CSV é uma matriz MxN de campos, um delimitador, um vetor de N posições se ele tiver um header --- e nesse caso pode descontar essa linha e ficar com Mx(N-1) no vetor de dados. Um char* com o nome do arquivo original, porque pode ser importante.

 

Assim ele fica abstrato e pode ser reconstruído no disco a qualquer momento. Isso é importante. O arquivo não pode morrer dentro do programa ou não vai servir pra nada.

 

O outro parâmetro que falta é saber se na entrada os campos tem ou não aspas ou algum delimitador determinando os campos, como aspas ou apóstrofes. E assim sabe se vai gerar na saída. Porque muitas vezes o arquivo vem com os campos entre aspas para proteger espaços ou caracteres de controle ou coisas como vírgulas dentro do campo.

 

EXEMPLO

LOAD DATA INFILE 'c:/tmp/discounts.csv' 
INTO TABLE discounts 
FIELDS TERMINATED BY ',' 
ENCLOSED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 ROWS;

Esse comando acima é o que se usa para importar um arquivo csv em um bando de dados MySQL. E dá pra entender do que estou falando. Tem algo semelhante para um milhão de outros programas.

 

E essa abstração de um bloco de parâmetros, argc/argv, existe para todos os programas em Windows ou Linux ou Unix ou OsX então deve ser importante, certo? E então deve ser uma boa solução ;) já que está em uso desde os '70. Como o CSV. 

 

Uma outra abstração importante mais recente seria JSON. Ou XML.

 

 

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

Bom dia @arfneto? sim eu entendo sua colocação as vezes vejo que o ensino da federal é muito enjessado, mas de qualquer forma preciso entender que o que esta sendo passado é para uma boa formação acredito eu.

 Bom, pesquisei na internet por alguns trabalhos, revisei o que estou fazendo mas não saio do mesmo lugar. Dentro do arquivo implementaca.c como o trabalho porposto sugeriu criar funçoes que retornam o valor de cada membro de cada indice apontado

como no caso.


int obter_amostra(dado_t *dado);
float obter_temperatura(dado_t *dado);
int obter_hora(dado_t *dado);

interface.h



int obter_amostra(dado_t *dado)
{

    return dado->amostra;
}

float obter_temperatura(dado_t *dado)
{
    return dado->temperatura;
}

int obter_hora(dado_t *dado)
{
    return dado->tempo;
}

implementacao.c

dado_t **sensor = ler_dados_csv("camera_temp.csv", &n_linhas);


    /* Imprima os dados:*/
    printf("Amostra----Temperatura------hora\n");

    for (i=0; i < 100; i++){
        //printf( "...", obter_amostra(dados[i]), obter_temperatura(dados[i]), ... );
        printf("%d, %f\n" , obter_amostra(sensor[i]), obter_temperatura(sensor[i]), obter_hora(sensor[i]));

    }

main.c

como pode ver, eu segui sua experiência em não escrever as declarações muito parecidas, agora ja estou sem conhecimento para terminar. Já abri o arquivo, li cada cada linha armazenei em cada campo da matriz **sensor, mas como acesso eles dentro da main.c? as funções obeter_xxx estão corretas? Dentro do for eu coloquei um inteiro  100 para garantir o laço po ruqe de fato não sei como usar o *tatal_dados. Precisar de mais detalhes é só pedir. Mais uma vez muito obrigado @arfneto.

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

30 minutos atrás, mandalori disse:

sim eu entendo sua colocação as vezes vejo que o ensino da federal é muito enjessado, mas de qualquer forma preciso entender que o que esta sendo passado é para uma boa formação acredito eu

 

"engessado" como em gesso.

 

Acho que é apenas um programa ruim. Não envolve assim muita coisa, como a escola ou o método...

 

31 minutos atrás, mandalori disse:

revisei o que estou fazendo mas não saio do mesmo lugar

 

Talvez seja um sinal de que eu tenho razão ;)

 

32 minutos atrás, mandalori disse:

Dentro do arquivo implementaca.c como o trabalho porposto sugeriu criar funçoes que retornam o valor de cada membro de cada indice apontado

 

int obter_amostra(dado_t *dado);
float obter_temperatura(dado_t *dado);
int obter_hora(dado_t *dado);

Não sei o que dizer. Esses genericamente se chamam getters e até alguns IDE geram automaticamente dependendo da linguagem, como o Idea para java e o CLion para C e C++.

 

Nada acrescentam ao seu modelo. 

 

E vão absolutamente na contra mão da abstração. Se pensar no último modelo que te expliquei, todos os campos são iguais e abstratos. Sequer o nome é salvo. Vou tentar explicar com um exemplo:

 

Um csv de 4 campos e 500 registros

  • vão vir de um arquivo com 501 linhas
  • a partir da primeira você gera um vetor nomes[4] e um vetor registro[500][4]. Essa é a realidade.
  • quando quer ler o terceiro campo do registro 350 saberia que o nome do campo está em vetor[2] e o valor em registro[349][2]. A realidade de novo. Enquanto não for operar com os dados você não converte nada. Apenas texto.
  • Se fosse ganhar um premio ou um dinheiro ou fosse importante pra você no futuro, seria trivial anexar um vetor de formatos, dizendo se o campo é char, string, int, float, ou se requer um tratamento especial. Apenas mais um vetor[4], um formato[4] que teria a descrição. Ex: 1 para char 2 para string 3 para int 4 para float.
  • Se fosse ganhar mais um premio, ou mais um dinheiro, ou quisesse apenas sossego por ter um tratamento genérico para CSV, podia usar o 0 no vetor de formatos e declarar um vetor de funções em C. E assim quando o cara tivesse um campo que precisava de um tratamento especial apenas escrevia por exemplo 0 no vetor de formatos e no seu programa você chamaria 
    	if ( !*formato[2] ) valor = (*rotina[2])( registro[349][2]);
    
    // se o campo 2 tem um tratamento especial, por exemplo ir no banco de dados e identificar a placa do veiculo que trouxe o pacote referente ao registro 350, alguem ja escreveu uma funcao para fazer isso e passa o endereco dela no vetor. O programa chama, o valor retorna e a vida segue. C foi escrita para esse tipo de problema

    se o campo 2 tem um tratamento especial, por exemplo ir no banco de dados e identificar a placa do veiculo que trouxe o pacote referente ao registro 350, alguém ja escreveu uma função para fazer isso e passa o endereço dela no vetor. O programa chama, o valor retorna e a vida segue. C foi escrita para esse tipo de problema

    Funciona bem. E em C não é difícil.

     

    1 hora atrás, mandalori disse:

    Já abri o arquivo, li cada cada linha armazenei em cada campo da matriz **sensor, mas como acesso eles dentro da main.c? as funções obeter_xxx estão corretas? Dentro do for eu coloquei um inteiro  100 para garantir o laço po ruqe de fato não sei como usar o *tatal_dados. Precisar de mais detalhes é só pedir

     

Vou ler o seu programa logo mais

 

adicionado 11 minutos depois
1 hora atrás, mandalori disse:

Dentro do for eu coloquei um inteiro  100 para garantir o laço po ruqe de fato não sei como usar o *tatal_dados. Precisar de mais detalhes é só pedir

 

tentei ler o programa mas vi que postou só trechos. Não ajuda muito.

 

As suas declarações estão certas.

 

Para acessar apenas precisa do endereço do CSV. Sua abstração como eu já disse não está muito boa. E por isso está tendo mais trabalho. Um csv deve corresponder a um modelo abstrato CSV completo. E eu já te expliquei porque.

 

Não deve se apegar a um modelo só porque veio do livro ou professor ou do livro do professor.

 

Espero que tenha entendido como o modelo pode ser mais útil, se leu o que escrevi acima e os outros posts. 

 

Para acessar algum campo precisa do número do registro e ele será dado[N] claro.

 

Seus getters não vão servir de nada. Só atrapalham. E se fossem 30 campos? E se fossem os dados de 300 sensores? Ia mesmo escrever uma função praticamente igual para cada um? Não, não ia. De todo modo basta escrever obter_amostra(dados[N]) 

 

 

Link para o comentário
Compartilhar em outros sites

12 minutos atrás, mandalori disse:

preciso avançar o conteudo em lista encadeada, mas retomo com mais calma em breve

 

Esse é outro problema, imagino. 

 

Quando voltar a esse problema recomendo que leia com atenção o que expliquei no tópico #9 porque vai economizar seu tempo.

Vou te mostrar um programa de exemplo que escrevi para outro propósito mas pode ajudar. Para o seu arquivo ele mostra

 

 Saída

 image.png.d9b20efe1e46c4c415a10b7648bd6706.png

 

E depois
 

image.png.96caf7c0ee0080c3d3faa0901557eb96.png

 

É bem simples e escrito em C mesmo

 

O programa completo:

 

Spoiler

#define _CRT_SECURE_NO_WARNINGS

#define _DELIM_ ','

#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>

typedef struct
{
    int    ispb; // o campo para classificar
    char   L[200]; // e a linha

}   Registro;


typedef struct
{
    int N;
    Registro R[300];

}    CSV;

CSV		tabela;

int main(int argc, char** argv)
{
    char mascara[16] = "%[^,\n]%c";
    mascara[3] = _DELIM_;

    setlocale(LC_ALL, "Portuguese");
    unsigned char campo[1024];
    char delim[4];
    int  n = 0;
    int campos = 0;
    int registros = 1;
    long tamanho = 0;
    int maior_linha = 0;

    char arquivo[132];
    if (argc < 1) return -1;
    strcpy(arquivo, argv[1]);
    FILE* ent = fopen(arquivo, "r");
    // agora le os campos da primeira linha

    while (fgets(campo, 1024, ent) != NULL)
    {
        n = strlen(campo);
        if (n > maior_linha) maior_linha = n;
        tamanho += n;
    };
    printf("Lidos %d bytes. Maior linha com %d caracteres\n",
        tamanho, maior_linha);
    rewind(ent);
    ent = fopen(arquivo, "r");

    setlocale(LC_ALL, "pt_BR.utf8");
    SetConsoleCP(850);
    SetConsoleOutputCP(850);

    printf("\nCampos:\n\n");
    while ((n = fscanf(ent, "%[^,\n]%c",
        campo, delim)) > 0)
    {
        printf("[%3d] '%s'\n", campos, campo);
        if (delim[0] == 10) break;
        campos += 1;
    };
    printf("\n\nLidos %d campos\n", 1 + campos);
    fclose(ent);

    printf("Cada registro tem %d bytes\n\n", sizeof(Registro));

    printf("Tecle ENTER para ler os registros de dados, Control-C para cancelar\n");
    char c = getc(stdin);
    ent = fopen(arquivo, "r");
    int este = 0;
    while ((n = fscanf(ent, "%[^,\n]%c",
        campo, delim)) > 0)
    {
        printf("[%3d:%3d] '%s'\n", registros, este, campo);
        este += 1;
        if (delim[0] == 10)
        {
            registros += 1;
            este = 0;
        };
    };   //while()
    printf("\n\nLidos %d registros\n", registros);
    fclose(ent);
    return 0;
};

int     insere_registro(Registro* R, CSV* csv)
{
    int pos = csv->N;
    for (; pos > 0; pos = pos - 1)
    {
        if (csv->R[pos - 1].ispb > R->ispb)
            csv->R[pos] = csv->R[pos - 1];
        else
            break;
    };
    // achou o lugar
    csv->R[pos] = *R;
    csv->R[pos].ispb = R->ispb;
    csv->N += 1;
    return csv->N;
};

//  fim ch 1465219 1465637

 

 

adicionado 0 minutos depois

É só um exemplo para um caso bem simples

Link para o comentário
Compartilhar em outros sites

Crie uma conta ou entre para comentar

Você precisa ser um usuário para fazer um comentário

Criar uma conta

Crie uma nova conta em nossa comunidade. É fácil!

Crie uma nova conta

Entrar

Já tem uma conta? Faça o login.

Entrar agora

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...