Ir ao conteúdo

Posts recomendados

Postado

E aí pessoal, beleza?

 

Alguém poderia me explicar por que o malloc não aloca a quantidade de memória que eu mando?

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

int main() {
    char a[100];
    printf("%li \n", sizeof(a));

    char *b1 = malloc(100 * sizeof(char));
    printf("%li \n", sizeof(b1));

    char *b2 = (char *) malloc(100 * sizeof(char));
    printf("%li \n", sizeof(b2));

    char *c1 = malloc(100);
    printf("%li \n", sizeof(c1));

    char *c2 = (char *) malloc(100);
    printf("%li \n", sizeof(c2));

    return EXIT_SUCCESS;
}

Compilado com GCC 7.5.0 no Linux Mint 19.3, eu tenho essa saída:

100 
8 
8 
8 
8

No meu entendimento, todas deverias ser 100... Desde já agradeço.

  • Curtir 1
Postado
27 minutos atrás, AdrianoSiqueira disse:

No meu entendimento, todas deverias ser 100... Desde já agradeço.

100 é o tamanho do vetor,

 

Já nos outros casos como se trata de ponteiros, você só está pegando o tamanho do ponteiro. E esse valor é fixo independente de alocação dinâmica.

  • Curtir 1
Postado

Eu postei um programa aqui que fazia isso mas não sei como procurar.

 

A resposta curta é NÃO. Não dá para saber quanto alocou ou quando pode alocar.

 

Vai depender claro da memória livre em sua máquina no momento do malloc(), e isso vai variar conforme o que está rodando num dado momento. O programa que eu tinha feito você pode refazer em minutos: era algo como uma busca binária tentando alocar um valor. 

 

Mas claro de você pode alocar de 1K em 1K ou 128K ou algo que ache relevante, Ou mesmo byte a byte. As máquinas são muito rápidas hoje em dia: O que eu tinha feito era alocar 1K e ir dobrando até estourar e a partir daí alocando de novo a partir da metade, e assim por diante. Em uma fração de segundo você tem o valor em K bytes( ou como preferir) que pode alocar no momento.

 

Se precisar eu escrevo outro exemplo. Me avise.

 

Mas isso vai contra o próprio sentido de alocação dinâmica: você aloca o quanto precisa e libera assim que puder. Quando você passa a não conseguir mais parte para uma máquina com mais memória ou muda a sua lógica, por exemplo paginando seus dados e usando o disco. Nada original. Todos os sistemas fazem isso.

  • Curtir 2
Postado

Exatamente o valor que você pediu não é possível, mas é possível saber quanto de memoria que a implementação de malloc que você usou allocou, o valor está nos 64 bits anteriores ao ponteiro para um arquitetura 64 bits, e 32 para a arquitetura 32 bits, pode existir implementações de malloc que não funcionem dessa maneira.

 

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

size_t malloc_size(void* addr)
{
    size_t* size_addr = (size_t*)addr;
    return size_addr[-1];
}

int main()
{
    for (size_t i = 1; i <= 1000; i++)
    {
        void* ptr = (void*)malloc(sizeof(char) * i);
        size_t ptr_size = malloc_size(ptr);

        printf("Pedido: %lu, Allocado: %lu\n", i, ptr_size);
        free(ptr);
    }

    return 0;
}

Note que você pede um valor e ele te entrega outro.

  • Curtir 1
Postado
35 minutos atrás, herbertbahia disse:

A função malloc reserva um espaço na memória do tamanho sizeof. No caso 
de um inteiro, sabemos que C considera-o com 4 bytes

 

Não entendi o que quer dizer. sizeof() é um operador. malloc() recebe um parâmetro, do tipo size_t

malloc() reserva um espaço em bytes igual ao parâmetro. 

 

Em muitos casos não se quer alocar em bytes, mas sim em unidades de seja lá o que for que estamos alocando, e assim se escreve por exemplo 
 

    Coisa* coisa = (Coisa*) malloc( N * sizeof(Coisa));


para alocar uma área equivalente a N vezes o tamanho de coisa. E parece java escrito assim ;) 


Mas se pode escrever 

size_t tamanho = N * sizeof(Coisa);
Coisa* coisa = (Coisa*) malloc(tamanho);

 

image.png.47a06478fd1c1471bfafcd1a58462477.png

 

size_t em geral é um unsigned int no meu caso definido em vcruntime.h
 

image.png.bf07dd5d25a5d458cc27d648e46222d6.png

 

 

image.png

  • Curtir 1
  • Obrigado 1
Postado
17 horas atrás, arfneto disse:

Se precisar eu escrevo outro exemplo. Me avise.

Obrigado, mas não precisa 🤗

 

17 horas atrás, arfneto disse:

A resposta curta é NÃO

Isso já respondeu a pergunta 🙂

 

@Matheus Maldi Muito interessante, parece que o compilador já reserva memória a mais para caso precise, não ter que ficar mexendo toda hora.

 

@arfneto Tem como você explicar o cast antes do malloc? Sempre vejo escrito assim e nunca entendi o por que. Eu escrevo sem e funciona sem problema, e o compilador não dá nenhuma mensagem de aviso... 🤨

adicionado 11 minutos depois

Vi essa thread um tempo atrás e só ajudou a ficar mais em dúvida ainda: Stack Overflow.

Postado
56 minutos atrás, AdrianoSiqueira disse:

 

 

@Matheus Maldi Muito interessante, parece que o compilador já reserva memória a mais para caso precise, não ter que ficar mexendo toda hora.

 

 

Note que ele aloca pelo menos 8 bytes a mais (64bits) para incluir o valor que foi alocado, o restante alocado a mais deve ser para manter algum alinhamento bom na memoria., por este motivo a função free não tem valor a ser deletado. Pode saber um pouco mais em:

https://www.geeksforgeeks.org/g-fact-88/?ref=rp

 

 

adicionado 17 minutos depois
17 horas atrás, arfneto disse:

 

 

size_t em geral é um unsigned int no meu caso definido em vcruntime.h
 

image.png.bf07dd5d25a5d458cc27d648e46222d6.png

 

 

size_t é um unsigned tamanho do ponteiro, mude o compilador para x64 e nos ajude a matar binarios de 32 bits.

  • Curtir 1
  • Solução
Postado
2 horas atrás, AdrianoSiqueira disse:

Tem como você explicar o cast antes do malloc? Sempre vejo escrito assim e nunca entendi o por que. Eu escrevo sem e funciona sem problema, e o compilador não dá nenhuma mensagem de aviso... 🤨

adicionado 11 minutos depois

Vi essa thread um tempo atrás e só ajudou a ficar mais em dúvida ainda: Stack Overflow.

 

Veja na resposta julgada como melhor lá no artigo
 

Citação

 

Conclusão:

Não é como se o typecast não fosse recomendado e nem viável, não é necessário (em certas ocasiões). Usar o typecast para passar valores de float para int, e unsigned para signed é usado e não usá-lo, pode acabar trazendo problemas para seu código, mas também pode mascarar alguns erros como diferença de tamanho de estruturas e coisas do tipo, mas são alguns problemas causados por descuido do próprio programador

 

 

Ou seja: democracia é isso. Cada voto vale um e não sei como votaram nisso como a melhor resposta. E a gente tem essas surpresas nas eleições também, claro.

Não me surpreende que você tenha ficado confuso. O sujeito não conseguiu se expressar de modo algum. Não é que não fosse recomendado? viável? necessário? Vírgulas fora de posição, "Certas ocasiões" é sacanagem. "Pode acabar trazendo problemas" mas são "causados por descuido do próprio programador" Sério?

 

Esqueça

 

Veja uma fonte mais séria no C FAQ que é uma compilação http://c-faq.com/malloc/cast.html e vai ler por exemplo
 

On the other hand, some programmers prefer to make every conversion explicit, 
to record that they have considered each case and decided exactly what should happen
 (see also question 17.5). Also, the casts are typically seen in C code which
 for one reason or another is intended to be compatible with C++, 
where explicit casts from void * are required
Citação

Por outro lado, alguns programadores preferem tornar toda conversão explícita, para deixar claro que eles consideraram cada caso e decidiram exatamente o que deveria acontecer na chamada. E também é típico ver essas conversões em programas que por uma razão ou outra se quer compatíveis com C++ onde a conversão explícita é requerida.


malloc() retorna void* e todos os ponteiros tem o mesmo tamanho então não faz diferença. Mas...

 

Do mesmo C FAQ
 

Citação

To some extent, whether you use these casts or not is a matter of style; see section 17.

 

Eu postei ontem ou antes um código em C C++ e java para calcular os DV do CPF e você pode ver lá como é parecido. Eu peguei um exemplo em C que eu tinha postado aqui e copiei e colei no editor em java e C++ e mudei umas poucas linhas. Pode ver lá. malloc() pode ser usada ao invés de new em C++ e quando você está portando um programa grande vai gostar de não ter que se preocupar em alterar cada chamada a malloc(). E dificilmente vai  trocar todos malloc() por new e tal. Assim parece que o cara que escreveu no FAQ tinha razão.

 

Eu sempre uso por questão de disciplina, pra deixar explícito o que estou fazendo. E com o hábito você não precisa ficar pensando em que linguagem está programando apenas para ter que inserir uma coisa tão simples como um cast. Eu sempre uso então se o próximo programa for em C++ não preciso pensar nisso. Eu nunca penso nisso ;) 


Na resposta longa lá no C FAQ Mark Brader, o autor, cita no item (b) a minha opinião:

image.png.bd542d30ffa8043d108d6db0f21d773c.png

 

Não gosto de conversões implícitas. De nada implícito. Não uso em geral comandos com efeitos colaterais, como atribuições dentro de comandos como printf() ou while() por exemplo. Não gosto de misturar operadores em expressões, tipo || > e + sem parenteses contando com a prioridade dos operadores. Coisas assim eu acho que levam a uma vida mais tranquila. Mas é claro uma opinião apenas.

 

1 hora atrás, Matheus Maldi disse:

Note que ele aloca pelo menos 8 bytes a mais (64bits) para incluir o valor que foi alocado, o restante alocado a mais deve ser para manter algum alinhamento bom na memoria., por este motivo a função free não tem valor a ser deletado. Pode saber um pouco mais em:

https://www.geeksforgeeks.org/g-fact-88/?ref=rp

 

Entendo. Mas isso não é garantido. Vai depender da implementação e até hoje não vi uma razão para contar com isso. Nem para saber um pouco mais, na verdade. Códigos fonte de malloc() estão disponíveis, mas nunca tentei ler, confesso.

 

1 hora atrás, Matheus Maldi disse:

size_t é um unsigned tamanho do ponteiro, mude o compilador para x64 e nos ajude a matar binarios de 32 bits

 

size_t é um unsigned int ou o que estiver lá no header. Nada tem a ver com o ponteiro diretamente. Se o ponteiro for do tamanho de um int size_T será um unsigned desse tamanho. Compilar para 64 bits é uma opção de compilação e eu uso se tiver uma razão objetiva para usar. Não sabia da campanha para "matar binários de 64 bits". Porque alguém se preocupa com isso?

  • Curtir 1
Postado
16 minutos atrás, arfneto disse:

size_t é um unsigned int ou o que estiver lá no header. Nada tem a ver com o ponteiro diretamente. Se o ponteiro for do tamanho de um int size_T será um unsigned desse tamanho. Compilar para 64 bits é uma opção de compilação e eu uso se tiver uma razão objetiva para usar. Não sabia da campanha para "matar binários de 64 bits". Porque alguém se preocupa com isso?

 

Mas o que DEVE estar no header é o exato tamanho que o ponteiro tem, no caso de x64 é 8 bytes e nos 32 bits 4 bytes, isso porque a função de malloc recebe como paramentro um size_t, se na arquitetura de 64 bits o size_t fosse de 32 bit, teriamos como valor maximo 4294967295, e isso nos iria impedir de alocar mais que 4GB para um endereço.

 

Queremos matar binarios de 32 bits devido a necessidade de manter bibliotecas iguais para as 2 arquiteturas.

  • Curtir 1
Postado
28 minutos atrás, Matheus Maldi disse:

Mas o que DEVE estar no header é o exato tamanho que o ponteiro tem, no caso de x64 é 8 bytes e nos 32 bits 4 bytes, isso porque a função de malloc recebe como paramentro um size_t, se na arquitetura de 64 bits o size_t fosse de 32 bit, teriamos como valor maximo 4294967295, e isso nos iria impedir de alocar mais que 4GB para um endereço

 

Você só repetiu o que está escrito lá. No entanto não é porque malloc() recebe um parâmetro size_t. Esse header nada tem a ver com malloc(). Como sabe, inúmeras funções esperam ou retornam size_t, para simplificar o uso de um valor unsigned com o tamanho adequado. memcpy() recebe size_t, strftime() retorna size_t,por exemplo. Estendendo um pouco o código de vcruntime.h:

 

image.png.1913199ce46a685075f854f3711371c0.png

 

e você vê que size_t é simplesmente uma constante unsigned do tamanho adequado e você pode usar sem pensar. Se fossem 128 bits size_t estaria lá definida de acordo... Ou 256 :) #ifdef _WIN256...

 

38 minutos atrás, Matheus Maldi disse:

Queremos matar binarios de 32 bits devido a necessidade de manter bibliotecas iguais para as 2 arquiteturas

 

Quem somos os "nós" de "queremos"? Apenas bibliotecas que tivessem necessidade de gerar instruções 64 bits seriam afetadas afinal. 

  • Curtir 1
Postado
18 minutos atrás, arfneto disse:

 

Você só repetiu o que está escrito lá. No entanto não é porque malloc() recebe um parâmetro size_t. Esse header nada tem a ver com malloc(). Como sabe, inúmeras funções esperam ou retornam size_t, para simplificar o uso de um valor unsigned com o tamanho adequado. memcpy() recebe size_t, strftime() retorna size_t,por exemplo. Estendendo um pouco o código de vcruntime.h:

 

image.png.1913199ce46a685075f854f3711371c0.png

 

e você vê que size_t é simplesmente uma constante unsigned do tamanho adequado e você pode usar sem pensar. Se fossem 128 bits size_t estaria lá definida de acordo... Ou 256 :) #ifdef _WIN256...

 

 

Quem somos os "nós" de "queremos"? Apenas bibliotecas que tivessem necessidade de gerar instruções 64 bits seriam afetadas afinal. 

 

Claro que tem tudo a ver, size_t é um tipo que pode conter qualquer índice de um array, todas as funções que você citou receber um size_t devido a trabalharem como endereços de memoria que podem conter tamanhos de até size_t, não foi simplesmente criado para facilitar o uso de um unsigned int ou long ou long long. Veja que é unsigned justamente devido ao tamanho do array não poder ser negativo,e ptrdiff_t é signed devido a diferença entre endereços de memoria poderem ser negativos, veja que como você mostrou ai, a declaração esta perto de ptrdiff_t e intptr_t por estar relacionado, não simplesmente porque um programador da microsoft resolveu colocar ele ai um tipo para facilitar. Que alias, não deveria estar neste header e sim no stddef.h conforme a ISO.

Quando precisarmos mudar a arquitetura de 64 para 128 bits, será porque foi necessário endereçar mais que 18.4 exabytes, o que não cabe em um ponteiro de tamanho de 8 bytes, e por isso será necessário mudar o tamanho do size_t.

 

  • Curtir 1
Postado
1 hora atrás, Matheus Maldi disse:

todas as funções que você citou receber um size_t devido a trabalharem como endereços de memoria que podem conter tamanhos de até size_t

 

essa discussão não está levando a nada. Você não parece ter uma duvida, o autor do tópico não se manifestou, e eu não tenho muito mais pra dizer.

Preste atenção: size_t não tem nada a ver com tamanhos de memória. É apenas uma quantidade, um tamanho: um size. em memcpy é um contador: o total de bytes copiado. Mesmo caso de strftime() onde é o tamanho de bytes da string de retorno.
E na verdade é o mesmo caso de malloc(): não faria sentido alocar um valor negativo de bytes. O parâmetro de malloc é o total de BYTES. Só isso.


Se você tem um livro sobre isso, ou mesmo se escreveu algum, deve estar assim, como em 
https://docs.microsoft.com/pt-br/cpp/c-runtime-library/reference/malloc?view=vs-2019
ms.png.be829c585ac9c34b9942c20f20c78360.png

Ou

https://www.tutorialspoint.com/c_standard_library/c_function_malloc.htm
tutori.png.bbdd9107a575c20a4a3c7c66cf65b41b.png

Ou talvez

http://www.cplusplus.com/reference/cstdlib/malloc/

 

cplusplus.thumb.png.05fff2abcd38da87f00f13755aaef755.png

 

E você vê que size_t é, como está escrito bem aqui acima, um tipo inteiro sem sinal, um size. 

  • Curtir 1
Postado
35 minutos atrás, arfneto disse:

E você vê que size_t é, como está escrito bem aqui acima, um tipo inteiro sem sinal, um size. 

 

Sim, é um size que foi criado com intuito de manter a compatibilidade dos códigos para compiladores de 32 e 64 bits, principalmente nas funções de memoria, endereço de memoria e aritmética de ponteiros, se fosse simplesmente um size, poderias ter outros tipos como size8_t, size_16_t, size32_t ...

Se você tem que guardar um size de alguma coisa que nunca vai passar de 255, não faz sentido usar size_t e sim uint8_t.

E por fim, qual o unico motivo que mudo a tamanho do size_t? A mudança do tamanho maximo da memoria. Em nenhum outros caso.

 

  • Curtir 1
Postado
25 minutos atrás, Matheus Maldi disse:

Sim, é um size que foi criado com intuito de manter a compatibilidade dos códigos para compiladores de 32 e 64 bits

 

Isto está na biblioteca desde os compiladores de 16 bits. É uma quantia positiva apenas. Integral type size. Mas deve ser como você está dizendo então...

adicionado 4 minutos depois
27 minutos atrás, Matheus Maldi disse:

E por fim, qual o unico motivo que mudo a tamanho do size_t

 
E você deve ter uma referência séria disso, imagino. O único motivo? Onde leu isso? Ou escreveu isso?

Então nunca foi ter um tamanho positivo limitado a INT_MAX e que a gente pudesse usar nas nossas rotinas sem ter que criar um a cada programa? Eu tenho usado isso em incontáveis programas desde o inçio dos anos 80 para ler valores positivos. Ainda bem que o pessoal de alocação de memória tem mantido essa variável lá.

  • Curtir 2
Postado

Polêmicas... 😱

 

Minha nossa, era só uma dúvida sobre ponteiros 😶. Não achei que fosse virar uma discussão tão acalorada 😲. Enfim, vou encerrar esse tópico antes que termine em uma discussão "menos civilizada". Obrigado @herbertbahia@Matheus Maldi@arfneto , aprendi muito com essa discussão, são coisas que não se ensinam na faculdade... 🤔

adicionado 2 minutos depois

@Midori Esqueci de te citar... 😅 Obrigado pela participação 😀.

  • Haha 1
Postado
1 hora atrás, arfneto disse:

E você deve ter uma referência séria disso, imagino. O único motivo? Onde leu isso? Ou escreveu isso?

Então nunca foi ter um tamanho positivo limitado a INT_MAX e que a gente pudesse usar nas nossas rotinas sem ter que criar um a cada programa? Eu tenho usado isso em incontáveis programas desde o inçio dos anos 80 para ler valores positivos. Ainda bem que o pessoal de alocação de memória tem mantido essa variável lá.

 

Este é um bom link de referencia: https://www.viva64.com/en/a/0050/

 

Referente ao uso do size_t para guardar tamanhos, muito improvável que terá problema em utilizar para isso, mas recomendaria usar as definições do stdint.h, int**SIZE**_t ou uint**SIZE**_t. Pois vai saber exatamente o tamanho. Veja o exemplo deste link https://primesieve.org/segmented_bit_sieve.cpp

 

Ele usa uint64_t, se tivesse usado size_t o programa funcionaria normalmente em x64, mas não funcionaria em 32bit.

 

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