Ir para conteúdo
Fórum Script Brasil
  • 0

(Resolvido) Qnt indeterminada de dados


j4m35_b0nd

Pergunta

Olá pessoal.

Estou aprendendo C e cheguei em um problema que não sei como resolver.

Meu programa irá receber dados de uma tabela com duas colunas. Uma coluna com valores de X e outra com o valor Y correspondente.

A minha intenção era usar matriz, só que não sei quantos valores de X o usuário irá digitar.

O que posso fazer para armazenar uma quantidade indeterminada de números, dos quais todos serão usados pelo programa, sem usar mais ou menos espaço do que o necessário?

Por favor, me ajudem com isso.

Link para o comentário
Compartilhar em outros sites

19 respostass a esta questão

Posts Recomendados

  • 0
A forma mais fácil seria perguntar ao usuário quantos elementos ele vai querer, mas creio que não queira isso. Nesse caso, você deve utilizar as funções malloc, realloc e free para alocar, realocar e liberar memória, respectivamente.

Essas funções parecem ser realmente o que eu estava procurando. Com elas, eu posso inserir novas posições em um vetor sempre que for necessário?

Exemplo:

Tenho um vetor de 1 posição inicial.

Se o usuário for digitar um segundo valor, elas alocam uma segunda posição no vetor.

Se o usuário for digitar um terceiro valor, elas alocam uma terceira posição no vetor.

E assim sucessivamente, enquanto o usuário precisar.

Obrigado.

Link para o comentário
Compartilhar em outros sites

  • 0

Opa, claro...

Vai aí um exemplo:

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

int main(int argc, char *argv[])
{
    //Pede os inteiros
    printf("Digite numeros inteiros\nPara parar, envie o valor 0 (zero)\n");
    //Inicia o loop
    int valor=1;
    int *inteiros=0;
    int inteiros_qtd=0;
    while(true){
        printf("\t"); //Tabulação
        scanf("%d",&valor); //Lê o inteiro
        //Verifica se é zero
        if(!valor) break;
        //Aumenta o tamanho de nosso array de inteiros
        inteiros_qtd++;
        if(inteiros){
            inteiros=(int*)realloc(inteiros,inteiros_qtd*sizeof(int)); //Redimensionamos o array
        }else{
            inteiros=(int*)malloc(1); //Alocamos pela primeira vez a memória
        }
        inteiros[inteiros_qtd-1]=valor;
    };
    //Mostra os valores digitados
    printf("Valores digitados:\n");
    int i;
    for(i=0;i<inteiros_qtd;i++){
        printf("\t%d\n",inteiros[i]);
    }
    //Fim...
    return 0;
}

Acho que já dá pra entender o uso das funções.

Link para o comentário
Compartilhar em outros sites

  • 0
Opa, claro...

Vai aí um exemplo:

.
                .
                .
                inteiros=(int*)realloc(inteiros,inteiros_qtd*sizeof(int)); //Redimensionamos o array

                    inteiros=(int*)malloc(1); //Alocamos pela primeira vez a memória
                .
                .
                .
Acho que já dá pra entender o uso das funções.
Seu exemplo foi excelente. Era exatamente isso que eu estava tentando fazer e não conseguia. Eu só não entendi uma coisa nas linhas acima. O que significa o "(int*)". Você poderia, por favor, me explicar? Muito obrigado. Como prometido, segue o que eu estava tentando fazer. Agora está funcionando. Usei o que você me ensinou e também algumas coisas que li na internet sobre alocação dinâmica de memória.
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <math.h>
void erro(){

     printf("Memória insuficiente para continuar\nO programa será encerrado\n");
     printf("\nPressione uma tecla para encerrar.");
     getch();
     exit(1);

}

int main(int argc, char *argv[]){
    
    char op;
    
    short int ver;
    
    long long int qnt=0;
    
    double *x=0,
           *y=0,
           valor_x,
           valor_y;
           
    do{//Laço do while infinito
       
       //Início de "Le valores X e Y"
       printf("X%d = ",qnt);
       scanf("%lf",&valor_x);
       printf("Y%d = ",qnt);
       scanf("%lf",&valor_y);
       qnt++;//Qnt de linhas que tem minha tabela
       //Fim  de "Le valores X e Y"
              
       printf("Deseja inserir mais dados?\nDigite:\n");
       do{
          printf("[S] - Sim\n");
          printf("[N] - Não\n");
          op = getche();
          printf("\n");
          ver=((op!='s')&&(op!='S')&&(op!='n')&&(op!='N'));
          if (ver){
             printf("Opção inválida. Digite novamente.\n");
          }
       }while(ver);
       //Início da rotina que aumenta uma posição nos vetores
       if (x){
          x=(double*)realloc(x,qnt*sizeof(double));
          y=(double*)realloc(y,qnt*sizeof(double));
          if (!(x||y))
             erro();
       }
       else{
            x=(double*)malloc(1);
            y=(double*)malloc(1);
            if (!(x||y))
               erro();
       }
       //Fim  da rotina que aumenta uma posição nos vetores
       x[qnt-1]=valor_x;
       y[qnt-1]=valor_y;
    }while((op=='s')||(op=='S'));
    printf("\nPressione uma tecla para encerrar o programa.\n");
    getch();
    return 1;
}

Editado por j4m35_b0nd
Link para o comentário
Compartilhar em outros sites

  • 0

Como o seu código está em C puro, aquilo não é necessário. Vou explicar o porquê.

Ao contrário do que muitos pensam, C++ não é um superconjunto de C. Existem alguns códigos que funcionarão somente em C. Observe o protótipo da função malloc:

void *malloc (unsigned int num)

Sim, o retorno é um ponteiro para void.

Em C ao tentar atribuir uma variável que foi declarada como int* (ponteiro para inteiro, como no código que postei) para void* (ponteiro para void, que é o retorno da função malloc) o compilador faz uma conversão implícita. Ou seja, ele converte de um tipo para o outro sem que o programador tenha que fazer nada.

Mas em C++ não há essa conversão implícita e o programador precisa fazê-la de forma explícita, colocando um casting. Portanto, na instrução (int*) estávamos simplesmente convertendo um void* para um int*.

Como você está compilando em C não há problemas, mas eu gosto sempre de fazer a conversão explícita.

Vou marcar o tópico como Resolvido. Qualquer coisa posta aí !

Edit:

Achei um pequeno problema no seu programa. Olhe essa linha:

if (!(x||y))
Você fez isso para verificar se a memória pôde ser realmente alocada. No entanto, o operador or irá ter resultado verdadeiro se pelo menos um valor for verdadeiro, ou seja, se alguma das duas alocações teve êxito. Mas esse valor é invertido pelo not e a função erro será chamada se e somente se nenhuma das alocações tiver êxito, mas o ideal é que a função seja chamada se qualquer uma alocação não for feita. Então o correto é assim:
if (!(x&&y))
Ou:
if ((!x) || (!y))

Mas não cheguei a analisar seu código completo.

Link para o comentário
Compartilhar em outros sites

  • 0
Como o seu código está em C puro, aquilo não é necessário. Vou explicar o porquê.

Ao contrário do que muitos pensam, C++ não é um superconjunto de C. Existem alguns códigos que funcionarão somente em C. Observe o protótipo da função malloc:

void *malloc (unsigned int num)

Sim, o retorno é um ponteiro para void.

Em C ao tentar atribuir uma variável que foi declarada como int* (ponteiro para inteiro, como no código que postei) para void* (ponteiro para void, que é o retorno da função malloc) o compilador faz uma conversão implícita. Ou seja, ele converte de um tipo para o outro sem que o programador tenha que fazer nada.

Mas em C++ não há essa conversão implícita e o programador precisa fazê-la de forma explícita, colocando um casting. Portanto, na instrução (int*) estávamos simplesmente convertendo um void* para um int*.

Como você está compilando em C não há problemas, mas eu gosto sempre de fazer a conversão explícita.

Vou marcar o tópico como Resolvido. Qualquer coisa posta aí !

Edit:

Achei um pequeno problema no seu programa. Olhe essa linha:

if (!(x||y))
Você fez isso para verificar se a memória pôde ser realmente alocada. No entanto, o operador or irá ter resultado verdadeiro se pelo menos um valor for verdadeiro, ou seja, se alguma das duas alocações teve êxito. Mas esse valor é invertido pelo not e a função erro será chamada se e somente se nenhuma das alocações tiver êxito, mas o ideal é que a função seja chamada se qualquer uma alocação não for feita. Então o correto é assim:
if (!(x&&y))
Ou:
if ((!x) || (!y))

Mas não cheguei a analisar seu código completo.

Muito obrigado por toda a ajuda.

Link para o comentário
Compartilhar em outros sites

  • 0
De nada cara. Qualquer coisa posta aí !

Já voltei. Não consegui usar a função free() que você mencionou. Pelo menos acho que não. Eu observo o Gerenciador de tarefas do Windows para ver o uso de memória RAM do programa.

No código que mandei, coloquei o seguinte.

free(x);

free(y);

Após a execução dessas instruções, o uso de memória continua o mesmo.

Será que eu estou usando a função de forma errada, ou estou vendo errado o gerenciador de tarefas?

Link para o comentário
Compartilhar em outros sites

  • 0
Pelo que eu vi, a chamada está certa. Para observar alguma diferença no gerenciador de tarefas, você tem que possuir no mínimo 125 elementos no array. (1 KB/8 B = 125)

Quantos elementos você está colocando?

Abraços.

Desculpe-me a demora para responder. Meu computador está apresentando problemas. Para testar, eu inseri 100.000 (cem mil) elementos no array. Após usar a função free(), pelo menos através do gerenciador de tarefas do windows, o uso de memória não diminuiu.

Link para o comentário
Compartilhar em outros sites

  • 0
Pode postar como ficou seu código ?

Obs.:

Removi a marcação Resolvido.

Obrigado por reabrir o tópico Jonathan.

O código ainda é o mesmo que está postado. Também fiz umas alterações para testar a função free();

Com as alterações que fiz para teste, ficou assim

#include <stdio.h>
#include <conio.h>
void erro(){

     printf("Memória insuficiente para continuar\nO programa será encerrado\n");
     printf("\nPressione uma tecla para encerrar.");
     getch();
     exit(1);

}

int main(int argc, char *argv[]){ //Aplicativa inicia usando 384KB de mémoria
    
    char op;
    
    short int ver;
    
    long long int qnt=0;
    
    double *x=0,
           *y=0,
           valor_x,
           valor_y;
          
    do{//Laço do while infinito
      
       //Início de "Le valores X e Y"
       printf("X%d = ",qnt);
       scanf("%lf",&valor_x);
       printf("Y%d = ",qnt);
       scanf("%lf",&valor_y);
       qnt++;//Qnt de linhas que tem minha tabela
       //Fim  de "Le valores X e Y"
       /*       
       printf("Deseja inserir mais dados?\nDigite:\n");
       do{
          printf("[S] - Sim\n");
          printf("[N] - Não\n");
          op = getche();
          printf("\n");
          ver=((op!='s')&&(op!='S')&&(op!='n')&&(op!='N'));
          if (ver){
             printf("Opção inválida. Digite novamente.\n");
          }
       }while(ver);
       */
       //Início da rotina que aumenta uma posição nos vetores
       if (x){
          x=(double*)realloc(x,qnt*sizeof(double));
          y=(double*)realloc(y,qnt*sizeof(double));
          if (!(x&&y))
             erro();
       }
       else{
            x=(double*)malloc(1);
            y=(double*)malloc(1);
            if (!(x&&y))
               erro();
       }
       //Fim  da rotina que aumenta uma posição nos vetores
       x[qnt-1]=valor_x;
       y[qnt-1]=valor_y;
    //}while((op=='s')||(op=='S'));
    }while(qnt<50000); //Após 100.000 posições (50.000 para X e 50.000 para Y), a memória vai para 1.520KB
    getch();
    free(x);
    free(y); //Após uso dessas funções, o uso cai para 1.128KB
    printf("\nPressione uma tecla para encerrar o programa.\n");
    getch();
    return 0;
}

Eu achei que, após liberar a memória com free(), o uso voltaria para algo próximo dos 384KB iniciais. Mesmo assm, diminuiu um pouco o uso após a função liberar memória

Link para o comentário
Compartilhar em outros sites

  • 0

A função free não libera a memória para o sistema operacional. Quando você requisita memória, aí o sistema operacional aumenta a quantidade de memória requisitada na quantidade de memória reservada para o seu processo, o chamdo pool. Mas quando você chama free você só vai até o pool e diz que parte dele está sem uso, sem interagir com o SO. Assim o processo sempre vai manter alocada a quantidade máxima alocada em algum momento (o seu pico), isso é assim por compatibilidade com o UNIX na época em que free foi descrita, ele não tinha capacidade de diminuir o pool, só aumentar.

Note que free não é inútil. Se você não usá-la você vai sempre alocar mais memória, independente de não estar usando todo o pool, e em algum momento usar toda a memória disponível.

Eu não sou um expert, mas pelo que sei é assim que as coisas são...

Link para o comentário
Compartilhar em outros sites

  • 0

O que o Trevas falou está certo, a memória continua com o mesmo valor até que o SO ou outro programa o altere, mas o SO já "marca" aquela memória como inutilizada, e portanto é possível, sim, obter o valor pelo gerenciador de tarefas.

Não consegui compilar aquele seu código acima, deu alguns erros. Mas para exemplificar criei outro programa que aloca 10 milhões de inteiros (o programa gastou cerca de 40mb na memória até ela ser desalocada). Se quiser fazer o teste, vai aí o código:

#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

void sai(int);

int main(int argc, char *argv[]){ //Aplicativa inicia usando 384KB de mémoria
    //Variáveis
    int *x;
    int qtd=10000000; //10 milhões
    int tamanho_bytes=qtd*sizeof(int);
    int a=0,b=0,c=0,d=0,e=0;
    int i;
    //Informa o que vamos fazer
    printf("Vamos tentar alocar %d inteiros (%d bytes)\n",qtd,tamanho_bytes);
    //Aloca a memória
    x=(int*)malloc(tamanho_bytes);
    if(!x){
        printf("Erro na alocacao !\n");
    }else{
        for(i=0;i<qtd;i++){
            *(x+i)=i;
        }
        printf("Alocamos com sucesso\n");
        printf("Aguarde, vamos desalocar em 10seg\n");
        Sleep(10000);//Espera 10seg
        free(x);
        printf("Desalocamos\n");
    }
    //Sai
    sai(5);
    //Retorna
    return 0;
}
void sai(int tempo){
    const int tempo_ms=tempo*1000;
    do{
        printf("Sairemos em %d segundos\n",tempo);
        Sleep(1000); //Essa função faz o windows "pausar" o programa por X ms
    }while(--tempo);
}

Note que aquele faço que percorre a memória alocada não serve pra muita coisa. O único objetivo era preencher a memória. Se estiver compilando em modo debug (pelo menos no VS) não haveria problemas, a memória seria consumida. Mas ao compilar em release (no VS, novamente) o compilador "percebe" que a memória não é utilizada pra nada e simplesmente não aloca.

Portanto, o objetivo do laço é utilizar a memória.

Obs.:

O tópico foi marcado como Resolvido.

Link para o comentário
Compartilhar em outros sites

Participe da discussão

Você pode postar agora e se registrar depois. Se você já tem uma conta, acesse agora para postar com sua conta.

Visitante
Responder esta pergunta...

×   Você colou conteúdo com formatação.   Remover formatação

  Apenas 75 emoticons são permitidos.

×   Seu link foi incorporado automaticamente.   Exibir como um link em vez disso

×   Seu conteúdo anterior foi restaurado.   Limpar Editor

×   Você não pode colar imagens diretamente. Carregar ou inserir imagens do URL.



  • Estatísticas dos Fóruns

    • Tópicos
      152,3k
    • Posts
      652,4k
×
×
  • Criar Novo...