Ir ao conteúdo

Posts recomendados

Postado
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>

typedef struct lin
{
    int valor;

}Linha;

typedef struct col
{

    Linha vetor[5];
    int  qtd; //Quantidade de valores dentro do indice do vetor

}Coluna;

void imprimeCol(Coluna *col[10]){
    int i,vet, val;

    for(i=0;i<10; i++){

        col = i;

        printf("\t%d",col);
        imprimeLinha(vet);
        printf("\n");
    }
}



void imprimeLinha(Linha *vetor, int valor){

    int i;
    vetor = (int *) malloc(5 * sizeof(int));
    for(i=0;i<5; i++){
    vetor = NULL;
    printf("\t[  %d  ]",vetor);

    }
}


int inserirnoVetor(Linha *vetor, int valor){

    Linha *aux = vetor->valor;

}


int main()
{
    setlocale(LC_ALL, "Portuguese");

    int col,vet,valor,coluna;


    imprimeCol(col);
    
    
    printf("Digite um valor: ");
    scanf("%d", &valor);
    
    printf("Digite um valor: ");
    scanf("%d", &coluna);
    
    



 //vet = vetor[0];
 // printf("\n\n%d", vet);

    return 0;
}

 

Estou com duvidas em como usar o vetor dentro da struct, gostaria de fazer como a imagem, digitar um valor e qual coluna eu quero inserir, a partir daí, sempre escolher a partir do primeiro indice, se caso eu repetir essa ação, se o primeiro indice tiver ocupado, partir para o próximo. Não fiz as repetições ainda, porque estou com dificuldades de usar o vetor da struct.

Alguém poderia me dar alguma dica?

 

vetor.png

  • Obrigado 1
Postado

@Lipeco    seu código não compila ,  tem erros , e por que você quer usar alocação  dinâmica nessas struct's ?  ,   seria mais fáciL se você puder / quiser usar structs e vetores do modo normal , e também não vi nenhum motivo para você usar um vetor dentro da struct ,  creio que não precisa pois você cria um vetor para a struct toda , e seu código funciona assim  :

#define WIN32_WINNT 0x600
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
typedef struct lin
{
    int valor;
}Linha;
typedef struct col
{
    Linha vetor[5];
    int  qtd;        // Quantidade de valores dentro do indice do vetor
}Coluna;
void imprimeCol(Coluna col[] )
{
    int i, vet, val;
    printf("Cols[0].Vetor[0].Valor = %d\n", col[0].vetor[0].valor);
    for (i = 0; i < 10; i++)
    {
        printf("%d\n", col[i].qtd);
        //imprimeLinha(vet);
        printf("\n");
    }
}
void imprimeLinha(Linha vetor[], int valor)
{
    int i;
    //vetor = (int*)malloc(5 * sizeof(int));
    for (i = 0; i < 5; i++)
    {
        vetor = NULL;
        printf("\t[  %d  ]\n", vetor[i]);
    }
}
int inserirnoVetor(Linha vetor[], int valor)
{
    //Linha* aux = vetor->valor;
    return 0;
}
int main()
{
    setlocale(LC_ALL, "Portuguese");
    int col, vet, valor, coluna;
    Coluna cols[10] = { 0 };
    cols[0].qtd = 11;
    cols->vetor[0].valor = 512;
    printf("cols.qtd = %d\n",cols[0].qtd);
    vet = cols->qtd;
    printf("Vet = %d\n", vet);
    imprimeCol(cols);
    printf("Digite um valor: ");
    scanf("%d", &valor);
    printf("Digite um valor Para Coluna : ");
    scanf("%d", &coluna);
    vet = cols->vetor[0].valor = 1;
    printf("\nval vet = %d\n", vet);
    return 0;
}

 

  • Curtir 1
  • Obrigado 1
Postado
typedef struct col
{
    Linha vetor[5];
    int  qtd; //Quantidade de valores dentro do indice do vetor

}   Coluna;

 

E isso

 

typedef struct lin
{
    int valor;

}   Linha;

 

Mais essa figura

 

image.png.943000ae2ac32a1845d40f55ff988481.pngMostram um tipo curioso de dado onde as colunas estão em linhas e vetor tem as colunas.

 

E parece que estaria bem representado como um Dado X[linhas][colunas] onde Dado seria o tipo entre os colchetes.

 

Isso é uma construção bem comum e se precisa de alocação dinâmica apenas faça como todo e qualquer programa em C --- ou C++ ou java ou muitas outras linguagens --- que declara main como ...

 

 

 

 

        int main(int argc, char** argv)

 

E construa as estruturas de acordo. Isso deve servir, já que atende bem desde os anos 60. Crie uma estrutura para os dados  e aloque um vetor de linhas com um ponteiro para cada coluna, exatamente como o sistema faz para TODO programa C

 

 

  • Curtir 1
  • Obrigado 1
Postado

@arfneto Então, consegui evoluir um pouco com meu exercício.
Agora estou com problema para remover um valor do meu vetor.

 

Estou fazendo um exercício da função Hash. Estou com dificuldade de remover o valor, exemplo, digito a Chave 27, e Valor qualquer, digito a chave 97 e um valor qualquer, 27 e o 92 vão para o endereço 1, aí o 27 vai para o indice 0 do vetor e o 92 vai para o indice 1 do vetor. Para remover eu escolho a chave, porém está sempre removendo o indice 0.

 

#include <stdio.h>
#include <stdlib.h>

typedef struct _elem{

    int chave;
    float valor;

}Elemento;

typedef struct _hash{

    Elemento vet[10];
    int num_elem;//tamanho da lista

}Linha_hash;

Linha_hash endereco[13];


void imprimeEndereco(){

    int i;
    Linha_hash *end;

    printf("\n\tEndereço\t\t\tElementos  ----  [CHAVE; VALOR]\n\n");
    for(i=0;i<13; i++){
    end = &endereco[i];

        printf("\t | %.2d |",i);
        imprimeElemento(end);
        printf("\n");
    }
}

void imprimeElemento(Linha_hash *endereco){

    int i;
    Elemento *l;

    for(i=0;i<10; i++){
        l= &endereco->vet[i];

        if(l->chave == NULL){
            printf(" ");
        }else
        {
            printf(" [%i ; %.2f]",l->chave,l->valor);

        }
    }
}

int funcaoHash(int chave){
    return chave % 13;
}

int inserir(int posicaoEndereco, int vChave, float valor){

    int i = 0;
    Elemento *elem;
    Linha_hash *end;
    end = &endereco[posicaoEndereco];
    elem= &end->vet[i];

    do
    {
        if(elem->chave == NULL)
        {
            elem->chave = vChave;
            elem->valor = valor;
            i = 10;
        }
        else
        {
            elem = &end->vet[++i];
        }
    }while(i<10);
}

int remover(int valorChaveRemov, int valorchave, int chaveRemov){


    int i = 0;
    Elemento *elem;
    Linha_hash *end;
    end = &endereco[chaveRemov];
    elem= &end->vet[i];

    printf("\nO valor %i está no indice %i", elem->chave, i);

    elem->chave = valorchave;

    do
    {
            if(valorChaveRemov == valorchave)
            {

                printf("Iguais\n");

                elem->chave = 0;


               // elem->valor = valor;
                i = 10;
            }
            else
            {
                printf("Diferentes");
                elem = &end->vet[++i];
            }


    }while(i<10);
}

int main (){

    setlocale(LC_ALL, "Portuguese");

    int opcao,chave,valorchave,valorchaveRemov, chaveRemov, cont =0;
    float valor;

    imprimeEndereco();

    printf("\n\n");

    do{

        printf("\tQual a chave deseja inserir?\n");
        scanf("%i", &chave);
        valorchave= chave;
        chave = funcaoHash(chave);
        printf("\tQual o valor deseja inserir?\n");
        scanf("%f", &valor);
        inserir(chave,valorchave,valor);
        imprimeEndereco();
        cont++;

     }while(cont < 2);

    printf("\n\n");

    printf("\tQual a chave deseja remover?\n");
    scanf("%i", &chaveRemov);
    valorchaveRemov= chaveRemov;
    chaveRemov = funcaoHash(chave);
    remover(valorchaveRemov, valorchave, chaveRemov);
     printf("\n\n");
    imprimeEndereco();


    return 0;
}


















 

  • Obrigado 1
Postado

Seu programa parece ter vários problemas e sequer compila direito.

 

  • Evite void.
  • Use protótipos
  • declare variáveis de controle dos loops DENTRO do loop sempre que possível
  • TESTE o retorno de scanf() sempre
  • não escreva programas interativos nunca
  • não use números mágicos, use constantes definidas no início
  • NUNCA use globais. É proibido em toda parte. E só vai te dar problemas

Escreva em torno dos dados. Seu dado é uma tabela hash e sequer tem isso claro em seu programa.

 

 

 

typedef struct _elem
{
    int   chave;
    float valor;

} Elemento;


Porque essa estrutura não é anônima? Não há auto-referência e não usa _elem para nada.

 

Mais ainda,  não use campos com nomes iniciando por '_".  É ruim de ler e são informalmente reservados para código de bibliotecas.

 

Mesmo caso dessa

 

typedef struct _hash
{
    Elemento vet[10];
    int      num_elem;  // tamanho da lista

} Linha_hash;

 

E porque 

 

    Linha_hash endereco[13];

 

Com tantas estruturas porque um vetor global?

 

 

E o que pretende com o elemento valor sendo float? 

 

Poste o enunciado e abra outro tópico para poder ajudar mais gente

 

  • Curtir 3
Postado

@arfneto Então, tenho que fazer as repetições ainda, estou só testando algumas coisas, mas vou seguir sua dica.

 

Esse é o exercício pede o seguinte:

 

Uma tabela de dispersão com 13 endereços base (índices de 0 a 12) e empregue a função de dispersão h(x) = x mod 13, em que x representa a chave do elemento cujo endereço-base deve ser calculado. Se a chave x for igual a 27, por exemplo, a função de dispersão retornará o valor 1, indicando o local onde esse elemento deverá ser armazenado. Se a mesma aplicação considerar a inserção da chave 92, o cálculo retornará o mesmo valor 1, ocorrendo nesse caso uma colisão.

Técnicas de tratamento de colisões são utilizadas para resolver os conflitos nos casos em que mais de uma chave é mapeada para um mesmo endereço-base da tabela. Um tratamento bem simples é associar a cada endereço-base uma lista de elementos e fazer o acesso em duas etapas, primeiro determinando o endereço-base com a função hash e depois percorrendo a lista associada para encontrar o local de armazenamento. Crie um programa que implemente uma demonstração de funcionamento de uma tabela Hash simples com 13 endereços-chave, considerando chave inteira e valor real, e utilizando a função hash e o tratamento de conflitos apresentados acima. O programa deverá possibilitar que o usuário insira e remova elementos [chave;valor] e possa observar o conteúdo da tabela. Pede-se utilizar um vetor de estruturas Linha_hash, cuja definição é apresentada abaixo. 

typedef struct _elem 
{ 
 int chave; 
 float valor; 
} Elemento; 

typedef struct _hash 
{ 
 Elemento vet[10]; //vetor de elementos 
 int num_elem; //número de elementos armazenados 
} Linha_hash; 

HASHH.png

  • Curtir 1
Postado
1 hora atrás, Lipeco disse:

Então, tenho que fazer as repetições ainda, estou só testando algumas coisas, mas vou seguir sua dica.

abra novo tópico sobre tabelas hash em C, como eu disse. Assim é o forum e ajuda mais gente que pesquise por isso. Vou ver seu programa de novo

Esse enunciado é ruim. A nomenclatura é ruim. As estruturas são ruins, e é uma pena tentar aprender a partir de um exercício assim. 

 

Se seu curso adota um livro sugiro focar no livro. Se um exercício assim está em seu livro arrume outro livro. Isso do modo como está descrito é uma bobagem, pra não dizer que está simplesmente errado.

 

função hash

 

hash é um mapeamento. O resultado (retorno) de uma função hash é a posição ou um erro.

 

image.png.ee26bd1616949ef8ef7df6ccf1f5e07d.png

Isso mostrado em seu livro/apostila/coisa mostra uma terceira entidade e só vai levar a confusão: isso não existe. A função hash vai pegar o valor que quer armazenar ou pesquisar e retornar um valor, a chave, que é a posição onde ele deve estar. 

 

Esse troço à esquerda aqui sugere 3 entidades: chave, valor e endereço. Mas só tem duas e uma função de associação, mapeamento. A função computa o tal hash que é a posição onde o valor deve ir ou deve estar.

 

Escreva em torno dos dados. 

 

Seu dado é um valor real, o tal

 

        float valor;

 

Sua função hash vai pegar esse valor e devolver um número entre 0 e o tamanho de sua tabela, que em geral é um primo ou uma potência de 2.

 

Assim em seu exemplo a realidade seria que hash(7.3) retornaria 1 e hash(10.1) retornaria 1. Não há razão para um campo chave. O que seria chave? Porque seria necessária uma chave?

 

hash e a colisão

 

Quando 2 valores tem a mesma chave --- colisão --- é preciso fazer algo, e o algo pode ser algo simples ou algo sofisticado. Claro que uma tabela hash quase cheia vai dar colisão toda hora, certo? Não é mágica.

 

Sua tabela tem 13 posições então é claro que a função vai retornar 13 possíveis valores. Dá pra imaginar que com uns 6 ou 7 deles já vai dar colisão toda hora. 

 

Seu enunciado usa uma lista ligada para ir anotando os valores conforme aparecem com a mesma chave. É uma possibilidade, com seus prós e contras. Pode gerar um gasto grande de memória e tem as alocações por causa da lista e toda o lance de hash é a performance. Muitas vezes se fica com algo mais rápido como simplesmente usar o próximo slot e limitar o total de colisões e retornar erro, forçando a uma reorganização da tabela já com um novo tamanho.

 

hash é para performance. A bem da verdade se é pra usar lista ligada provavelmente uma lista de listas ligadas com os valores já em ordem faria quase a mesma coisa que hash e uma série de listas.

 

Imagino que já tenha escrito uns programas para tratar listas ligadas e se escreveu ao menos um programa decente desses tem as funções de lista e vai usar igualzinho, implementando 13 listas ligadas de float, que é o que tem aqui.

 

Não precisa começar com isso, pode parar na primeira colisão ou usar o próximo slot como expliquei e testar suas funções todas.

 

O que importa é testar logo. Ter partes funcionando. Tudo é desenvolvido assim.

 

Um vetor de colisões de tamanho fixo

 

Seu enunciado não deixa claro do que está falando em termos de lista: o desenho e a realidade remetem a pensar numa lista ligada, mas pode ser também uma tabela de colisão com 10 slots o que tem no desenho, e apesar de possível dá pra ver que vai ter um certo desperdício de memória porque já vai começar com 10x o necessário: 130 para 13 elementos.

 

Outra coisa é entender que provavelmente o autor da questão pensou errado em tudo: A linha hash não teria 10 Elemento com a chave, que seria a mesma, certo? Por isso se chama colisão.

 

Ou eu não entendi mesmo nadinha, e não seria a primeira vez nem a última 🤔

 

Mas acho que é mesmo uma bobagem

 

 

 

 

 

  • Curtir 2
  • Obrigado 1
Postado
Em 28/02/2022 às 14:50, devair1010 disse:

também não vi nenhum motivo para você usar um vetor dentro da struct ,  creio que não precisa pois você cria um vetor para a struct toda

 

20 horas atrás, Lipeco disse:

Pede-se utilizar um vetor de estruturas Linha_hash, cuja definição é apresentada abaixo. 

 

Agora com o enunciado dá pra ver um motivo. 🙂 

 

Um exemplo de hash em C usando essas estruturas

 

Não me lembro de ter visto exemplos de tabelas hash aqui no forum. então vou deixar um para referência. E vou tentar mostrar o sentido disso tudo.

 

O enunciado como recebido pelo @Lipeco é fraco 😞 e não ajuda em nada. 

 

Citação

 

Uma tabela de float é algo pra ser usado com cuidado porque por exemplo 2.05 é diferente de 2.0500001 e se o valor a ser pesquisado vem de cálculos pode não dar certo nunca.

 

 

Escrevendo em torno dos dados

 

typedef struct
{
    int   chave; // não usado aqui
    float valor;
} Elemento;

 

Esse é o dado, o valor

 

Para cada posição, slot na tabela há uma previsão de um certo número de ocorrências, chamadas colisões na literatura. No enunciado 10 valores podem ocorrer para cada chave.

 

typedef struct
{
    size_t    N;    // total atual de itens com essa chave
    Elemento* vet;  // o vetor de itens
} Linha;

 

Essa é uma linha da tabela. No enunciado são 13 linhas. Como tem um limite no enunciado 10 e como no início está tudo vazio, pode ser mais simples usar um ponteiro para criar o vetor na hora. Veja mais a seguir

 

Escrevendo em torno dos dados: e a tabela hash?

 

No código do autor 
 

        Linha_hash endereco[13];

 

Claro que é um vetor de struct e atende o enunciado mas é problemático: falta informação para o programa e o valor ainda é global, algo desastroso. Por exemplo é quase impossível generalizar isso, usar em outro lugar ou mesmo usar mais de uma tabela em um programa ou usar essas funções em outro programa, e não se escreve um programa assim.

 

Uma tabela hash

 

typedef struct
{
    size_t n_col;  // limite de colisoes
    size_t n_lin;  // quantas linhas tem
    size_t ttl;    // quantos elementos tem
    Linha* linha;  // o vetor de linhas de hash
} Hash;

 

Agora temos uma opção usável:

  • n_col é o total de colisões, 10 no enunciado
  • n_lin é o total de slots, as linhas da tabela, 13 no enunciado
  • ttl é o total de linhas da tabela num dado momento
  • linha é o tal vetor de linhas e tem os dados

E isso fecha a conta, ANTES de escrever uma linha de programa sequer.

 

Isso quer dizer que as funções de hash podem usar uma tabela dessas e ao receber o ponteiro para uma tabela está tudo lá dentro. Tudo que importa para o mecanismo funcionar.

 

E a tal função hash()?

 

Essa função é um simples mapeamento de um Elemento para uma chave, uma posição na tabela.

 

size_t hash(Elemento*, Hash*);  // do valor devolve a chave

 

Então recebe uma tabela e um elemento e retorna um valor que é o índice onde o valor deve ser gravado ou procurado. Nada mais. E como os dois vem como parâmetros e o size_t --- um inteiro sem sinal, claro --- é a posição, o resultado da função. É o princípio de única responsabilidade, SRP.

 

E que outras funções podem ser importantes?

 

No mínimo essas, imagino:

 

Hash* h_cria(size_t, size_t);  // retorna uma tabela nova
Hash* h_destroi(Hash*);        // destroi

int h_busca(const float, Hash*);   // tem esse?
int h_insere(Elemento*, Hash*);    // insere esse
int h_lista(Hash*, const char*);   // mostra a tabela
int h_remove(const float, Hash*);  // apaga esse

 

criar e destruir uma tabela hash, buscar, inserir e remover um elemento. E mostrar a tabela toda na tela para poder conferir, certo? 😉 

 

E o código?

 

A função hash(): uma implementação

 

size_t hash(Elemento* el, Hash* tbl)
{  // nesse caso cada elemento e um float
    // uma funcao hash obvia seria usar
    // os 32 bits como inteiro sem sinal
    // e calcular o modulo para o tamanho da tabela
    if (tbl == NULL) return 0;
    union
    {   float como_float;
        int   como_int;
    } Valor;
    Valor.como_float = el->valor;
    return Valor.como_int % tbl->n_lin;
}

 

No fundo é só uma linha de código. Como o float que é o dado tem 32 bits e um int também tem, dá para ver o float como int e calcular o módulo e retornar a posição/ E o mesmo código do autor da questão.

 

Como funciona a tabela?

 

Tabelas hash são tabelas de pesquisa, look-up tables:

  • a partir da chave a função hash determina onde deve estar o valor.
    • Na hora de inserir a função hash diz onde deve ir o valor e um algoritmo diz o que fazer se não cabe lá.
    • Na hora de procurar a função hash diz onde procurar e um algoritmo diz o que fazer se ele não está lá na posição

Uma implementação para insere()

 

int h_insere(Elemento* el, Hash* tbl)
{
    if (tbl == NULL) return -1;
    size_t pos = hash(el, tbl);  // define a linha
    if (tbl->linha[pos].N >= tbl->n_col)
    {
        fprintf(stderr, "Erro: a tabela esta cheia\n");
        return -1;
    }
    tbl->linha[pos].vet[tbl->linha[pos].N].valor =
        el->valor;
    tbl->linha[pos].N += 1;
    tbl->ttl += 1;  // conta esse
    return 0;
}

 

O simples: calcula a posição e coloca o valor lá. Retorna 0 se ok, -1 se encheu a tabela. Note que se a tabela encheu o que se faz é criar uma tabela maior e inserir tudo de volta. Em geral a partir de uns 70% de uso mesmo com uma função boa de hash() a coisa começa a ficar ruim.

 

Uma implementação para busca()

 

int h_busca(const float valor, Hash* T)
{
    if (T == NULL) return 0;
    Elemento el  = {.valor = valor};
    size_t   pos = hash(&el, T);  // onde deve estar
    for (size_t x = 0; x < T->linha[pos].N; x += 1)
        for (size_t y = 0; y < T->linha[pos].N; y += 1)
            if (T->linha[pos].vet[y].valor == el.valor)
                return 1;
    return 0;  // olhou todos entao não tem esse valor
}

 

Claro, busca é quase a mesma coisa. Identifica a linha e olha em todas as colunas. Se achou retorna 1. Se não tem retorna 0, o simples

 

Uma implementação para remove()

 

int h_remove(const float valor, Hash* tbl)
{   // remover e a mesma coisa que buscar e apagar
    if (tbl == NULL) return 0; // sem tabela
    Elemento el  = {.valor = valor};
    size_t   pos = hash(&el, tbl);  // onde deve estar
    for (size_t x = 0; x < tbl->linha[pos].N; x += 1)
        for (size_t y = 0; y < tbl->linha[pos].N; y += 1)
            if (tbl->linha[pos].vet[y].valor == el.valor)
            {  // tem que remover esse
                if (y != (tbl->linha[pos].N - 1))
                {  // não e o ultimo: troca com o ultimo
                    tbl->linha[pos].vet[y].valor =
                        tbl->linha[pos]
                            .vet[tbl->linha[pos].N - 1]
                            .valor;
                }
                tbl->linha[pos].N -= 1;
                tbl->ttl -= 1;  // apagou 1 afinal
                return 1;
            };
    return 0;  // olhou todos entao não tem esse valor
}

 

Claro que é quase igual. A diferença é que pra remover tem que estar lá. Se não está retorna 0 (falso). E se está e se não for a última ocorrência da tabela se troca pelo último e diminui o total. Se for o último basta diminuir o total, certo? 😉 Só isso. 

 

Como listar a tabela toda?
 

Claro que depende da necessidade do programa, mas se é pra testar basta mostrar tudo que está lá dentro:

 

int h_lista(Hash* tbl, const char* tit)
{
    if (tbl == NULL) return -1;
    if (tit != NULL) printf("%s\n", tit);
    printf(
        "[%lu slots x max %lu valores por "
        "chave, %lu elementos agora (%4.2f%% ocupada)]\n\n",
        tbl->n_lin, tbl->n_col, tbl->ttl,
        (100.f * (float)tbl->ttl /
         (tbl->n_col * tbl->n_lin)));
    for (size_t i = 0; i < tbl->n_lin; i += 1)
    {
        printf("%02lu\t[#%02lu]", i, tbl->linha[i].N);
        for (size_t j = 0; j < tbl->linha[i].N; j += 1)
            printf("  %6.2f", tbl->linha[i].vet[j].valor);
        printf("\n");
    }
    return 0;
}

 

O tit é uma conveniência, um título que já sai na tela. O resto é o óbvio.

 

Eis um exemplo chamado assim:

 

    h_lista(T, "\nTabela ao final [cheia]:");

 

que mostra

 

Tabela ao final [cheia]:
[13 slots x max 2 valores por chave, 14 elementos agora (53.85% ocupada)]
00      [#01]   26.00
01      [#01]   39.44
02      [#00]
03      [#02]   35.93   75.71
04      [#02]   13.50   86.88
05      [#01]   53.83
06      [#01]   48.01
07      [#01]   20.79
08      [#01]   15.75
09      [#01]   81.50
10      [#00]
11      [#01]   65.47
12      [#02]   34.40   58.86

 

Com a tabela assim qualquer valor que cair para as linhas 3 4 ou 12 vão dar a tabela como cheia, como eu expliquei antes.

 

Como criar a tabela?

 

Basta preencher os campos, já que vai estar tudo vazio...
 

Hash* h_cria(size_t tamanho, size_t limite_col)
{
    Hash* nova = (Hash*)malloc(sizeof(Hash));
    if (nova == NULL) return NULL;
    nova->n_lin = tamanho;
    nova->n_col = limite_col;
    nova->ttl   = 0;
    nova->linha = (Linha*)malloc(tamanho * sizeof(Linha));
    for (size_t i = 0; i < tamanho; i += 1)
    {
        nova->linha[i].N   = 0;  // linha vazia
        nova->linha[i].vet = (Elemento*)malloc(
            nova->n_col * sizeof(Elemento));
    }
    return nova;
}

 

No código do exemplo:

 

    Hash* T = h_cria(DIM_HASH, LIM_COL);

 

ou por exemplo

 

    Hash* grande_tabela = h_cria( 7919, 10);

 

7919 é o milésimo primo

 

E essa é uma das vantagens de ter a tabela toda em um lugar. É a noção de encapsulamento, comum em OOP. Mesmo as funções acima poderiam fazer parte da struct Hash, criando algo como uma classe C++  ou C#.

 

Como apagar uma tabela?
 

Hash* h_destroi(Hash* tbl)
{
    if (tbl == NULL) return NULL;
    free(tbl->linha);
    free(tbl);
    return NULL;
}

 

O simples. Apaga tudo na ordem inversa à de criação.

 

Como testar uma coisa dessas enquanto escreve?

 

Um exemplo seria usar uma factory function, e gerar valores para inserir na tabela até dar erro, e usar h_lista() para ir acompanhando. Nada criativo.

 

Ao mesmo tempo em que insere() pode chamar busca() para ver se acha e claro que tem que estar lá.

 

Uma vez que a tabela está cheia se pode repetir a sequência usando a mesma factory funtion e remover elemento a elemento. Claro que ao final a tabela deve estar vazia, o que pode ser visto chamando a mesma função lista().

 

E assim a conta fecha.

 

Complexo? Nem tanto:

 

int main(void)
{
    unsigned int raiz = 220301;
    int      res = 0;
    srand(raiz);
    Hash* T = h_cria(DIM_HASH, LIM_COL);
    h_lista(T, "\nTabela Vazia:");
    teste_enche(T);
    h_lista(T, "\nTabela ao final [cheia]:");

    Elemento el = {2022.f}; // esse não tem
    printf(
        "\n\tBusca por %5.2f retornou %d (1 = "
        "encontrado)\n",
        el.valor, h_busca(el.valor, T));

    printf(
        "\n\tBusca por %7.4f retornou %d (1 = "
        "encontrado. Na tabela tem 20.79]\n",
        20.7901f, h_busca(20.7901f, T));

    srand(raiz); // reinicia sequencia
    teste_esvazia(T);  // remove todo mundo

    h_lista(T, "\nDepois de tentar remover todos:");
    T = h_destroi(T); // apaga tudo
    return 0;
}

 

Sem surpresas, teste_enche e teste_esvazia fazem o previsto:

 

void teste_enche(Hash* T)
{
    Elemento el;
    int      res = 0;
    printf(
        "\n\n\tinsere valores ate encher a "
        "tabela:\n\n");
    do {
        el.valor =
            (float)(rand() % 100) + rand() % 100 / 100.f;
        res = h_insere(&el, T);
        printf(
            "  %5.2f: insere() %d (0 = ok)\t"
            "busca() %d (1 = encontrado)\n",
            el.valor, res, h_busca(el.valor, T));
    } while (res == 0);
    return;
}

 

Um pouco de matemática garante valores entre 0 e 99.99. O valor é inserido e depois procurado na tabela. Eis uma saída 

 

        insere valores ate encher a tabela:

  20.79: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
  39.44: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
  53.83: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
  34.40: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
  58.86: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
  35.93: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
  75.71: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
  81.50: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
  15.75: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
  48.01: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
  65.47: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
  26.00: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
  13.50: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
  86.88: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
Erro: a tabela esta cheia
  35.54: insere() -1 (0 = ok)   busca() 0 (1 = encontrado)

 

E para esvaziar é simétrico:

 

void teste_esvazia(Hash* T)
{   // tenta apagar todos
    size_t total = T->ttl;
    int    res   = 0;
    float  valor = 0.f;
    printf("\n\n\tTenta apagar %lu elementos\n\n", total);
    for (size_t i = 0; i < total; i += 1)
    {
        valor =
            (float)(rand() % 100) + rand() % 100 / 100.f;
        res = h_remove(valor, T);
        printf(
            "  remove(%5.2f) = %d (1 = removido)\t"
            " Restam %03lu na tabela\n",
            valor, res, T->ttl);
    };
    return;
}

 

E como a tabela já tem DENTRO dela o total de itens e a gente sabe que a ordem é a mesma então é fácil imaginar que tem que terminar vazia...

 


    teste_esvazia(T);  // remove todo mundo
    h_lista(T, "\nDepois de tentar remover todos:");

 

E funciona com qualquer tabela, qualquer tamanho que caiba na memória, sem passar contadores ou misturar outros dados.

 

Eis uma versão de main() para um teste assim

 

int main(void)
{
    unsigned int raiz = 220301;
    int      res = 0;
    srand(raiz);
    Hash* T = h_cria(DIM_HASH, LIM_COL);
    h_lista(T, "\nTabela Vazia:");
    teste_enche(T);
    h_lista(T, "\nTabela ao final [cheia]:");

    Elemento el = {2022.f}; // esse não tem
    printf(
        "\n\tBusca por %5.2f retornou %d (1 = "
        "encontrado)\n",
        el.valor, h_busca(el.valor, T));

    printf(
        "\n\tBusca por %7.4f retornou %d (1 = "
        "encontrado. Na tabela tem 20.79]\n",
        20.7901f, h_busca(20.7901f, T));

    srand(raiz); // reinicia sequencia
    teste_esvazia(T);  // remove todo mundo
    h_lista(T, "\nDepois de tentar remover todos:");
    T = h_destroi(T); // apaga tudo
    return 0;
}

 

Usando 13 linhas e apenas 2 colisões para não ficar muito comprido vem 

 

Tabela Vazia:
[13 slots x max 2 valores por chave, 0 elementos agora (0.00% ocupada)]

00      [#00]
01      [#00]
02      [#00]
03      [#00]
04      [#00]
05      [#00]
06      [#00]
07      [#00]
08      [#00]
09      [#00]
10      [#00]
11      [#00]
12      [#00]


        insere valores ate encher a tabela:

  20.79: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
  39.44: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
  53.83: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
  34.40: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
  58.86: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
  35.93: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
  75.71: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
  81.50: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
  15.75: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
  48.01: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
  65.47: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
  26.00: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
  13.50: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
  86.88: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
Erro: a tabela esta cheia
  35.54: insere() -1 (0 = ok)   busca() 0 (1 = encontrado)

Tabela ao final [cheia]:
[13 slots x max 2 valores por chave, 14 elementos agora (53.85% ocupada)]

00      [#01]   26.00
01      [#01]   39.44
02      [#00]
03      [#02]   35.93   75.71
04      [#02]   13.50   86.88
05      [#01]   53.83
06      [#01]   48.01
07      [#01]   20.79
08      [#01]   15.75
09      [#01]   81.50
10      [#00]
11      [#01]   65.47
12      [#02]   34.40   58.86

        Busca por 2022.00 retornou 0 (1 = encontrado)

        Busca por 20.7901 retornou 0 (1 = encontrado. Na tabela tem 20.79]


        Tenta apagar 14 elementos

  remove(20.79) = 1 (1 = removido)       Restam 013 na tabela
  remove(39.44) = 1 (1 = removido)       Restam 012 na tabela
  remove(53.83) = 1 (1 = removido)       Restam 011 na tabela
  remove(34.40) = 1 (1 = removido)       Restam 010 na tabela
  remove(58.86) = 1 (1 = removido)       Restam 009 na tabela
  remove(35.93) = 1 (1 = removido)       Restam 008 na tabela
  remove(75.71) = 1 (1 = removido)       Restam 007 na tabela
  remove(81.50) = 1 (1 = removido)       Restam 006 na tabela
  remove(15.75) = 1 (1 = removido)       Restam 005 na tabela
  remove(48.01) = 1 (1 = removido)       Restam 004 na tabela
  remove(65.47) = 1 (1 = removido)       Restam 003 na tabela
  remove(26.00) = 1 (1 = removido)       Restam 002 na tabela
  remove(13.50) = 1 (1 = removido)       Restam 001 na tabela
  remove(86.88) = 1 (1 = removido)       Restam 000 na tabela

Depois de tentar remover todos:
[13 slots x max 2 valores por chave, 0 elementos agora (0.00% ocupada)]

00      [#00]
01      [#00]
02      [#00]
03      [#00]
04      [#00]
05      [#00]
06      [#00]
07      [#00]
08      [#00]
09      [#00]
10      [#00]
11      [#00]
12      [#00]

 

As duas chamadas avulsas busca() em main() são para testar com valores quaisquer, só exemplos.

 

O código completo

 

thash.h

 

#pragma once
#include <stdio.h>

typedef struct
{
    int   chave; // não usado aqui
    float valor;
} Elemento;

typedef struct
{
    size_t    N;    // total atual de itens com essa chave
    Elemento* vet;  // o vetor de itens
} Linha;

typedef struct
{
    size_t n_col;  // limite de colisoes
    size_t n_lin;  // quantas linhas tem
    size_t ttl;    // quantos elementos tem
    Linha* linha;  // o vetor de linhas de hash
} Hash;

Hash* h_cria(size_t, size_t);  // retorna uma tabela nova
Hash* h_destroi(Hash*);        // destroi

size_t hash(Elemento*, Hash*);  // do valor devolve a chave

int h_busca(const float, Hash*);   // tem esse?
int h_insere(Elemento*, Hash*);    // insere esse
int h_lista(Hash*, const char*);   // mostra a tabela
int h_remove(const float, Hash*);  // apaga esse

 

Aqui tem as estruturas e as funções e assim se pode usar em outros programas. Afinal é para isso que serve um programa.

 

Eis uma implementação das funções, a mesma que está acima

 

thash.c

 

#include "thash.h"

#include <memory.h>
#include <stdio.h>
#include <stdlib.h>

int h_busca(const float valor, Hash* T)
{
    if (T == NULL) return 0;
    Elemento el  = {.valor = valor};
    size_t   pos = hash(&el, T);  // onde deve estar
    for (size_t x = 0; x < T->linha[pos].N; x += 1)
        for (size_t y = 0; y < T->linha[pos].N; y += 1)
            if (T->linha[pos].vet[y].valor == el.valor)
                return 1;
    return 0;  // olhou todos entao não tem esse valor
}

Hash* h_cria(size_t tamanho, size_t limite_col)
{
    Hash* nova = (Hash*)malloc(sizeof(Hash));
    if (nova == NULL) return NULL;
    nova->n_lin = tamanho;
    nova->n_col = limite_col;
    nova->ttl   = 0;
    nova->linha = (Linha*)malloc(tamanho * sizeof(Linha));
    for (size_t i = 0; i < tamanho; i += 1)
    {
        nova->linha[i].N   = 0;  // linha vazia
        nova->linha[i].vet = (Elemento*)malloc(
            nova->n_col * sizeof(Elemento));
    }
    return nova;
}

Hash* h_destroi(Hash* tbl)
{
    if (tbl == NULL) return NULL;
    free(tbl->linha);
    free(tbl);
    return NULL;
}

size_t hash(Elemento* el, Hash* tbl)
{  // nesse caso cada elemento e um float
    // uma funcao hash obvia seria usar
    // os 32 bits como inteiro sem sinal
    // e calcular o modulo para o tamanho da tabela
    if (tbl == NULL) return 0;
    union
    {   float como_float;
        int   como_int;
    } Valor;
    Valor.como_float = el->valor;
    return Valor.como_int % tbl->n_lin;
}

int h_insere(Elemento* el, Hash* tbl)
{
    if (tbl == NULL) return -1;
    size_t pos = hash(el, tbl);  // define a linha
    if (tbl->linha[pos].N >= tbl->n_col)
    {
        fprintf(stderr, "Erro: a tabela esta cheia\n");
        return -1;
    }
    tbl->linha[pos].vet[tbl->linha[pos].N].valor =
        el->valor;
    tbl->linha[pos].N += 1;
    tbl->ttl += 1;  // conta esse
    return 0;
}

int h_lista(Hash* tbl, const char* tit)
{
    if (tbl == NULL) return -1;
    if (tit != NULL) printf("%s\n", tit);
    printf(
        "[%lu slots x max %lu valores por "
        "chave, %lu elementos agora (%4.2f%% ocupada)]\n\n",
        tbl->n_lin, tbl->n_col, tbl->ttl,
        (100.f * (float)tbl->ttl /
         (tbl->n_col * tbl->n_lin)));
    for (size_t i = 0; i < tbl->n_lin; i += 1)
    {
        printf("%02lu\t[#%02lu]", i, tbl->linha[i].N);
        for (size_t j = 0; j < tbl->linha[i].N; j += 1)
            printf("  %6.2f", tbl->linha[i].vet[j].valor);
        printf("\n");
    }
    return 0;
}

int h_remove(const float valor, Hash* tbl)
{   // remover e a mesma coisa que buscar e apagar
    if (tbl == NULL) return 0; // sem tabela
    Elemento el  = {.valor = valor};
    size_t   pos = hash(&el, tbl);  // onde deve estar
    for (size_t x = 0; x < tbl->linha[pos].N; x += 1)
        for (size_t y = 0; y < tbl->linha[pos].N; y += 1)
            if (tbl->linha[pos].vet[y].valor == el.valor)
            {  // tem que remover esse
                if (y != (tbl->linha[pos].N - 1))
                {  // não e o ultimo: troca com o ultimo
                    tbl->linha[pos].vet[y].valor =
                        tbl->linha[pos]
                            .vet[tbl->linha[pos].N - 1]
                            .valor;
                }
                tbl->linha[pos].N -= 1;
                tbl->ttl -= 1;  // apagou 1 afinal
                return 1;
            };
    return 0;  // olhou todos entao não tem esse valor
}

 

e um teste como descrito

 

#define DIM_HASH (13)  // total de posições na tabela
#define LIM_COL (2)   // maximo de colisoes para cada chave

#include <stdio.h>
#include <stdlib.h>
#include "thash.h"

void teste_enche(Hash*);
void teste_esvazia(Hash*);

int main(void)
{
    unsigned int raiz = 220301;
    int      res = 0;
    srand(raiz);
    Hash* T = h_cria(DIM_HASH, LIM_COL);
    h_lista(T, "\nTabela Vazia:");
    teste_enche(T);
    h_lista(T, "\nTabela ao final [cheia]:");

    Elemento el = {2022.f}; // esse não tem
    printf(
        "\n\tBusca por %5.2f retornou %d (1 = "
        "encontrado)\n",
        el.valor, h_busca(el.valor, T));

    printf(
        "\n\tBusca por %7.4f retornou %d (1 = "
        "encontrado. Na tabela tem 20.79]\n",
        20.7901f, h_busca(20.7901f, T));

    srand(raiz); // reinicia sequencia
    teste_esvazia(T);  // remove todo mundo
    h_lista(T, "\nDepois de tentar remover todos:");
    T = h_destroi(T); // apaga tudo
    return 0;
}

void teste_enche(Hash* T)
{
    Elemento el;
    int      res = 0;
    printf(
        "\n\n\tinsere valores ate encher a "
        "tabela:\n\n");
    do {
        el.valor =
            (float)(rand() % 100) + rand() % 100 / 100.f;
        res = h_insere(&el, T);
        printf(
            "  %5.2f: insere() %d (0 = ok)\t"
            "busca() %d (1 = encontrado)\n",
            el.valor, res, h_busca(el.valor, T));
    } while (res == 0);
    return;
}

void teste_esvazia(Hash* T)
{   // tenta apagar todos
    size_t total = T->ttl;
    int    res   = 0;
    float  valor = 0.f;
    printf("\n\n\tTenta apagar %lu elementos\n\n", total);
    for (size_t i = 0; i < total; i += 1)
    {
        valor =
            (float)(rand() % 100) + rand() % 100 / 100.f;
        res = h_remove(valor, T);
        printf(
            "  remove(%5.2f) = %d (1 = removido)\t"
            " Restam %03lu na tabela\n",
            valor, res, T->ttl);
    };
    return;
}

 

Escrevo isso para ter algum programa que trata tabela de hash em C recente aqui no forum.

 

A saída de novo:

 

Tabela Vazia:
[13 slots x max 2 valores por chave, 0 elementos agora (0.00% ocupada)]

00      [#00]
01      [#00]
02      [#00]
03      [#00]
04      [#00]
05      [#00]
06      [#00]
07      [#00]
08      [#00]
09      [#00]
10      [#00]
11      [#00]
12      [#00]


        insere valores ate encher a tabela:

  20.79: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
  39.44: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
  53.83: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
  34.40: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
  58.86: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
  35.93: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
  75.71: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
  81.50: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
  15.75: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
  48.01: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
  65.47: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
  26.00: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
  13.50: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
  86.88: insere() 0 (0 = ok)    busca() 1 (1 = encontrado)
Erro: a tabela esta cheia
  35.54: insere() -1 (0 = ok)   busca() 0 (1 = encontrado)

Tabela ao final [cheia]:
[13 slots x max 2 valores por chave, 14 elementos agora (53.85% ocupada)]

00      [#01]   26.00
01      [#01]   39.44
02      [#00]
03      [#02]   35.93   75.71
04      [#02]   13.50   86.88
05      [#01]   53.83
06      [#01]   48.01
07      [#01]   20.79
08      [#01]   15.75
09      [#01]   81.50
10      [#00]
11      [#01]   65.47
12      [#02]   34.40   58.86

        Busca por 2022.00 retornou 0 (1 = encontrado)

        Busca por 20.7901 retornou 0 (1 = encontrado. Na tabela tem 20.79]


        Tenta apagar 14 elementos

  remove(20.79) = 1 (1 = removido)       Restam 013 na tabela
  remove(39.44) = 1 (1 = removido)       Restam 012 na tabela
  remove(53.83) = 1 (1 = removido)       Restam 011 na tabela
  remove(34.40) = 1 (1 = removido)       Restam 010 na tabela
  remove(58.86) = 1 (1 = removido)       Restam 009 na tabela
  remove(35.93) = 1 (1 = removido)       Restam 008 na tabela
  remove(75.71) = 1 (1 = removido)       Restam 007 na tabela
  remove(81.50) = 1 (1 = removido)       Restam 006 na tabela
  remove(15.75) = 1 (1 = removido)       Restam 005 na tabela
  remove(48.01) = 1 (1 = removido)       Restam 004 na tabela
  remove(65.47) = 1 (1 = removido)       Restam 003 na tabela
  remove(26.00) = 1 (1 = removido)       Restam 002 na tabela
  remove(13.50) = 1 (1 = removido)       Restam 001 na tabela
  remove(86.88) = 1 (1 = removido)       Restam 000 na tabela

Depois de tentar remover todos:
[13 slots x max 2 valores por chave, 0 elementos agora (0.00% ocupada)]

00      [#00]
01      [#00]
02      [#00]
03      [#00]
04      [#00]
05      [#00]
06      [#00]
07      [#00]
08      [#00]
09      [#00]
10      [#00]
11      [#00]
12      [#00]

 

 

  • Curtir 1
  • Obrigado 2
Postado

@Lipeco  Tudo bem você? Comigo, está tudo bem!

Sobre o exercício percebe-se 3 operações () no enunciado e se remover toda a gordura da estrutura de dados tabela de Hash, sobram operações e iterações sobre vetores.

 

Em 28/02/2022 às 22:28, Lipeco disse:

O programa deverá possibilitar que o usuário insira e remova elementos [chave;valor] e possa observar o conteúdo da tabela. Pede-se utilizar um vetor de estruturas Linha_hash, cuja definição é apresentada abaixo

— Tudo se resume em saber operar os vetores.

 

Em 27/02/2022 às 21:38, Lipeco disse:

Estou com duvidas em como usar o vetor dentro da struct

Em 28/02/2022 às 21:37, Lipeco disse:
void imprimeEndereco(){

    int i;
    Linha_hash *end;

    printf("\n\tEndereço\t\t\tElementos  ----  [CHAVE; VALOR]\n\n");
    for(i=0;i<13; i++){
    end = &endereco[i];

        printf("\t | %.2d |",i);
        imprimeElemento(end);
        printf("\n");
    }
}

 

Após 24 horas a dúvida persistiu...

*, vejo que nomeou um apontador de end (não sei o porquê disso), depois declarou uma iteração (certo), imprimiu o código de hash (certo), e por fim chama a função recursivamente (falhou): aproposito, essa função se quer tem argumento.


{C} Escrevi rapidamente uma função semelhante à tua, inclusive a simulação utiliza a mesma lógica do vetor global mais ‘loop’-FOR

int
observar (void) {
  for (long unsigned indice = 0; indice < 13UL; ++indice) {
      Linha_hash *lh = tabela_hash + indice;
      if (lh->tamanho_vet) {
         printf ("%2lu->", indice);
         for (long unsigned e = 0; e < 10UL; ++e) {
             if ((lh->vet + e)->chave) {
                printf ("%.3lu:%s\t", (lh->vet + e)->chave, (lh->vet + e)->usuario);
                }
             }
         puts ("");
         }
      }
   return 0;
   }

itera a matriz  'tabela_hash' de 0 até 12

*, pega a linha do índice

*, verifica se o vetor tem tamanho diferente de zero

*, escreve o índice (código de hash) da matriz

*, itera o vetor de recipientes (ou de elementos) de 0 até 9

*, com operador de acesso de membros através de ponteiro (->) acessa o apontador dos recipientes (vet) adiciona (e) para indexar, usa o operador (->) para acessar seu membro 'chave', verifica se tem valor diferente de 0 e escreve: 'chave' como decimal positivo, 'usuario' como “strings”.

*, escreve uma nova linha e recomeça.

 

Resultado:

image.png.dbabe1cec0e63df90b13be069de13333.png

 

[🙂] — espero que ajude! 

  • Curtir 1
  • Obrigado 2

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

LANÇAMENTO!

eletronica2025-popup.jpg


CLIQUE AQUI E BAIXE AGORA MESMO!