TransWikia.com

Colo alocar uma memória baseado no tamanho do que o usuário digitou

Stack Overflow em Português Asked by Levi Ivanovsk on December 11, 2020

Como eu posso fazer com que logo após o usuário digitar uma string o programa conte o número ‘x’ de caracteres desta e aloque X bytes para ela?

Não estou conseguindo fazê-lo, tentei por exemplo fazer com que após o usuário digitar a string, isso:

ponteiro = (char *) malloc (strlen(string));

No código acima, o usuário dando uma entrada, por exemplo, do tipo: ABCDEF, faria com que a strlen contasse 6 caracteres, assim alocando para a string 6 bytes.

Mas dá errado, já que a alocação de memória estaria acontecendo depois, assim antes da string ser ‘pega’ não existiria bytes alocados para armazená-la.
Alguém sabe de que maneira resolvo isso?

3 Answers

Simples: da mesma maneira que o C++ faz com a std::string. Deixa o buffer de input, lê de chunk em chunk e aloca mais memória conforme preciso.

Usa o seguinte código:

char *getln()
{
    char *linha = NULL, *tmp = NULL;
    size_t tamanho = 0, index = 0;
    int ch = EOF;

    while (ch) {
        ch = getc(stdin);

        /* Checamos se devemos parar. */
        if (ch == EOF || ch == 'n')
            ch = 0;

        /* Checamos se devemos expandir. */
        if (tamanho <= index) {
            tamanho += CHUNK;
            tmp = realloc(linha, tamanho);
            if (!tmp) {
                free(linha);
                linha = NULL;
                break;
            }
             linha = tmp;
        }

        /* Finalmente adicionamos o caractere */
        linha[index++] = ch;
    }

    return linha;
}

Lembrando que CHUNK é um valor cabível a você. A maneira sem falhas é #define CHUNK 1. O método pode alterar os resultados de tempo (input muito mais demorado).

Answered by user2692 on December 11, 2020

Há uma maneira de fazer o que você quer, mas o desempenho do código é questionável dependendo da situação.

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

#define ALLOCATION_STEP 5

int main(void)
{
    int mem_left = ALLOCATION_STEP;
    int mem_size = mem_left + 1;
    int length = 0;
    int realloc_cnt = 0;
    char *buff = (char *)malloc(mem_left);
    char c = 0;
    while((c = getchar()) != 'n') {
        if(mem_left == 0) { // precisamos de mais memoria
            char *tmp = (char *)realloc(buff,mem_size + ALLOCATION_STEP);
            if(tmp == NULL) {
                printf("Falha na aquisicao de memoria!n");
                free(buff);
                return -1;
            }
            buff = tmp;
            mem_left = ALLOCATION_STEP;
            mem_size += mem_left;
            ++realloc_cnt;
        }
        buff[length++] = c;
        --mem_left;
    }

    buff[length] = ''; // fecha a string

    printf("Tamanho: %dn",length);
    printf("Memoria: %dn",mem_size);
    printf("Padding: %dn",mem_size - (length+1));
    printf("Realocacoes: %dn",realloc_cnt);
    printf("String: %sn",buff);

    free(buff);
    buff = NULL;

    return 0;
}

O código acima aloca uma quantidade inicial de memória dada pelo valor de ALLOCATION_STEP, então ele começa a ler a stream de entrada até encontrar uma quebra de linha n, ao longo do processo ele vai copiando os caracteres da stream para a variável buff, se a quantidade de memória restante indicada por mem_left chegar a 0 ele realoca mais ALLOCATION_STEP bytes de memória para continuar o processo até o fim ou até que um erro de alocação ocorra.

O problema é: Um ALLOCATION_STEP muito baixo pode fazer com que muitas realocações aconteçam tornando o desempenho do código questionável, também há o uso abusivo da função realloc que pode trazer problemas.

Answered by Cahe on December 11, 2020

Não existe muitas opções. Basicamente ou você aloca antes uma quantidade suficiente para o que precisa ou pergunta quanto quer alocar.

A segunda opção realmente é terrível. Do ponto de vista de usabilidade não faz o menor sentido.

A primeira opção tem a desvantagem de possivelmente alocar mais memória do que o realmente necessário. Mas quem se importa. Isso não é um problema mais em nenhum tipo de dispositivo a não ser que queira entrar com texto realmente muito longo, mas você não faria isto de forma simples de qualquer forma, em um texto longo teria um estrutura de dados para gerenciar isto.

Alguém pode pensar que também há a desvantagem de impor um limite máximo. Mas isto é uma vantagem. Deixar um usuário qual é o limite máximo de texto a digitar é a última coisa que o programa deveria fazer. Novamente, se precisa de algo mais complexo, precisará de um programa mais complexo.

Então a solução é fazer isto:

#include <stdio.h>
#include <stdlib.h>
int main(){
    char *ponteiro = malloc(100); //um caractere será ocupado pelo terminador 
    printf("String: ");
    scanf("%99s", ponteiro); //permite digitar um caractere a menos que o alocado 
    free(ponteiro);
    return 0;
}

Veja funcionando no ideone. E no repl.it. Também coloquei no GitHub para referência futura.

Agora apareceram respostas que mostram algoritmos criativos mas que provavelmente serão overkill. Minha solução desperdiça memória, as outras apresentadas desperdiçam tempo de alocação e no comentário apareceu uma que desperdiça ou dois mas em quantidades moderadas.

Percebeu que não há almoço grátis? Você tem que decidir o que pretende desperdiçar. Em um exemplo simples qualquer pode ser desperdiçada sem problemas. Eu usaria a navalha de Occam e ficaria com a mais simples. Se eu preferisse a mais complicada eu teria postada ela.

Eu poderia usar uma versão otimizada e melhor tratada em problemas mais complexos onde eu realmente estivesse com problemas de memória e não soubesse o tamanho do que preciso em entrada de dados. Eu não tenho nenhum desses problemas nunca então nunca me preocupei com isso. Acho que você também não deveria.

Answered by Maniero on December 11, 2020

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP