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

Alocação dinâmica de memória


Math_naVeia

Pergunta

9 respostass a esta questão

Posts Recomendados

  • 0

sim exato! O ponteiro está na pilha da função, ele guarda a direção de memória onde foi reservado o espaço dinamicamente.
Em outras palavras
C é um linguagem "amigável" porem "agressivo", amigável porque permite você dar apelidos para espaços de memória que se encontram na pilha da função para não ter que estar "recordando" a direção da memória. É bem mais fácil você lembrar o nome de uma pessoa que seu numero de inscrição no registro de nascimento não é? Porem o linguagem também é agressivo porque realmente estamos lidando com a memória física do computador, no heap não podemos dar nomes a variáveis, teríamos que recordar sim ou sim o numero da direção de memória, mas como C nos brinda o mecanismo de ponteiros que nada mais são que variáveis que guardam a direção de outras variáveis, por esse motivo ainda assim não precisamos lembrar da direção de certa variável alocada no heap, acessamos desde o ponteiro. Toda variável alocada no heap tem que ser apontada por um ponteiro porque não podemos dar nomes para elas, só podemos dar nomes para as que estão na stack(pilha) das funções ou as globais que são um tipo de caso especial, digamos que as globais são declaradas em um espaço especial da memória.

 

 

Link para o comentário
Compartilhar em outros sites

  • 0

quando você cria uma função ela vai parar no stack, cada função pode conter até 2 megabytes mais ou menos dependendo do compilador utilizado, para serem criado variaveis, se eu crio uma função tipo assim:
int foo(){
int a;
int b;
int *c;
}

a pilha toma a aparência de uma pilha de pratos dentro do stack(uma zona especial da memória): A variavel é a primeira que aparece na sua função então o programa pega essa int a que é o primeiro prato e bota ele "em cima da mesa", logo pega a int b e bota em cima do int a, e o mesmo acontece com int *c, "pilha" fica algo assim:
int *c; ultimo variável declarada na sua função(ultimo prato), é o de em cima da pilha, será o primeiro a ser liberado quando a função deixar de existir
int b; segundo prato que apareceu
int a; prato de abaixo, foi o primeiro que apareceu na sua função.

Essas variáveis ficarão no stack durante toda a vida da função, quando fechar o programa essa parte da memória será limpada automaticamente começando por liberar a ultima variável que foi jogada na pilha, ou seja int *c. Logo depois libera b e depois a. Lógico... pense que em uma pilha de pratos você não pode tirar o prato de abaixo, o normal é você tirar o prato de acima.
O que tem que ver tudo isso? Como falei... a vida dessas variáveis que estão na pilha(stack) tem sua "vida util" durante a existência da função, depois disso ela deixa de existir porque como falei, o programa libera ela. O problema é que no heap(também conhecido como free storage) isso não acontece, se você reservar memória no heap, você terá que liberar esse espaço com free(), si não esse espaço vai ficar aí para sempre até que você apague o computador causando o borrado de dados por ausência de corrente elétrica, isso se conhece como fuga de memória, é uma espécie de bug. você tem controle absoluto sobre a pilha, mas o stack é um "espaço de memória compartilhado" entre todos os programas que estão rodando no seu pc. já pensou o que passaria se 2 programas tivessem uma variável chamada de nomes iguais? Um rolo né >_<. Por isso você deve apontar desde sua pilha a direção reservada no heap, porque na pilha nenhum outro programa tem acesso, porem no heap é memória compartilhada entre vários programas, dai o sistema de dar nomes a variáveis vai por agua abaixo. Porem como falei antes você não pode abusar da pilha já que ela é limitada, você dispõe mais o menos de 2 mega bytes, esse tamanho não é fixo, e pode ser aumentado ou variar de um compilador a outro, mas não aconselho tocar nisso, isso faria seu código menos portável.

Link para o comentário
Compartilhar em outros sites

  • 0

Nossa! Muito obrigada pela explicação. Eu não sabia dessa questão do Heap ser memória compartilhada. Agora...

1) Se eu não apagar essa memória alocada dinamicamente ela não é liberada nem quando acaba a execução do programa ?

 

2) E mais uma coisa... Aproveitando que você falou da quantidade de memória do stack, tenho uma dúvida sobre isso. Por exemplo, se eu faço esse programa :

int main ()

{

   int function() 
   {
     return function();
   }

return 0;

}

Vai dar segmentation fault na hora da execução pela questão da limitação do stack como você falou. Mas a minha dúvida é : no meu compilador tem uma quantidade de memória padrão no stack para qualquer código ? E sendo SIM como resposta, na hora da execução vai sendo colocada uma função em cima de outra (caracterizando uma pilha) até que alguma hora chega o limite dessa memória padrão ?

 

Link para o comentário
Compartilhar em outros sites

  • 0

1 Essa variável dinâmica é visível em todos os escopos, ou seja, em um programa você pode ter varias funções, quando você invocar uma função é nesse momento que é reservado a memória para as variáveis que estão declaradas nessa função, incluindo os ponteiros, quando a função chegar no return, ou sair sem mais, todas as variáveis serão liberadas automaticamente, porem as variáveis que você reservar dinamicamente vão a continuar até que você use free ou apague o pc. Por que isso é assim? Vamos pensar da seguinte forma... se eu falei que você precisa de um ponteiro no stack  para manipular a uma variável que está no heap, imagine que você cria uma função e a invoca, dentro dessa função você supostamente criaria um ponteiro e alocaria a memória dinamicamente, dai sua função terminou e você não liberou com free, o que vai acontecer é que seu ponteiro será destruído, deixando a memória dinâmica "abandonada", resultado é que você perdeu o ponteiro que apontava para o heap, agora você não saberá onde está a variável, a menos que antes de sair da funão você tenha passado por return ou outro sistema a direção dela, lá no heap tem muitas outras variáveis, porem nenhum programa vai poder usar esse pedaço de memória, porque o seu computador vai ter marcado essa zona como ocupada, justo quando você apagar o pc, por ausência de luz, ele vai "esquecer" que isso tava reservada, todos os bits se colocam a zero(ausência de de corrente elétrica=0). Mesmo seu programa finalizado essa variável vai continuar aí, só que você perdeu o controle sobre ela a causa de ter destruído o ponteiro que a apontava ao finalizar seu programa.

2

17 horas atrás, Math_naVeia disse:

no meu compilador tem uma quantidade de memória padrão no stack para qualquer código ?

Sim exatamente!

 

18 horas atrás, Math_naVeia disse:

E sendo SIM como resposta, na hora da execução vai sendo colocada uma função em cima de outra (caracterizando uma pilha) até que alguma hora chega o limite dessa memória padrão ?

Eu fiz um pequeno programa para demonstrar o que acontece. No meu computador dentro de main eu declarei um vetor de exatamente 521132 posições, esse foi o maior vetor que consegui declarar sem que meu programa soltasse os fogos artificiais >_<, se tentar declarar um numero maior salta o aviso(int n[521133];). Dai criei outra função e tentei declarar a mesma quantidade de inteiros, para minha surpresa... porque eu também nunca usei tanta memória, é que não foi possível. Na verdade o programa me permitiu declarar algo bem mais modesto, ele somente me permitiu declarar um vetor de int n[997]  posições na função foo sem explodir XD. Isso me leva a crer, com "demonstrada evidencia" que a pilha é de aproximadamente 2 megas pois 521132 de inteiros equivale a 2,084528 megabytes, isso inclui todas as funções.

int foo(){
    int n[996]; // com 997 explodi >_<

    return ;
}

int main(void) {
  int n[521132]; // com 521133 faz boom!
  foo();
  return ;
}

Faça provas depois e comente.

Claro que isso pode variar de um sistema a outro e de um compilador a outro... eu por exemplo to usando um sistema operativo windows 7 de 32 bits com o compilador gcc 5 e codeblocks, realmente o programa deixou de trabalhar cerca dos 2 megabytes que falei antes.

Agora respondendo a sua pergunta...
O tal erro de segmentação é o seguinte... um segmento é um pedaço de memória de um X tamanho(ponho X porque não sei exatamente o tamanho que tem um segmento), no qual vai alojado seu programa. Se seu programa excede o tamanho do segmento, então serão designados 2 segmentos a seu programa, pode que sobre espaço do segundo segmento, e assim por diante, a coisa consiste em que ele fique alojado em um ou mais seguimentos até ele "caber". Na verdade nem lembro muito bem de todos esses conceitos, mas é mais ou menos isso mesmo.

O erro de segmentação é quando você está tentando acessar fora do seu segmento, certamente você está tentando acessar memória pertencente a outro programa que está em outro segmento, por isso dá esse erro. Como falei antes, na pilha você não pode acessar a memória de outra aplicação, porque windows está vigiando isso. Se não seria muito fácil hackear outras aplicações não acha? Se você tiver dentro da sua função uns 3 vetores de 100 posições certamente você poderá ler os 3. vamos fazer uma prova?
 

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

int main (){
    char a[10] = "Alo\0";
    char b[10] = "mundo";
    int i;
    for (i = ; i<20; i++) {
        printf("%c", b[i]);
    }
    getchar();
    return ;
}

esse programa você pode imprimir os 2 vetores com um só loop e não da erro de segmentação, somente sai ao contrario as palavras pela disposição da pilha, o a será o prato de abaixo, e b o de acima, se imprimo a primeiro não sairá b porque esta em cima, porem se imprimo b sairão as 2. Por quê? Fácil.. você não está saindo do seu segmento, você esta dentro dele, e C permite você acessar qualquer parte da sua função.
Quando o programa diz que tem problemas com outro segmento é justo quando nós fazemos isso:
 

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

int main (){
    char a[10] = "Alo\0";
    char b[10] = "mundo";
    int i;
    for (i = ; /*i<20*/; i++) {  //<-note o comentario!!! Não tem condição, é um loop infinito!BUG!! >_<
        printf("%c", b[i]);
    }
    getchar();
    return ;
}

Certamente o programa imprime um montão de lixo(letras estranhas) porque a memória contem resíduos de outros programas, mas certamente serão esses 2 megabytes o que serão imprimidos, até que o sistema operativo se de conta que você esta tentando saltar o segmento, que é quando monstra o bendito mensagem do erro de segmentação. Costuma acontecer muito quando usamos funções recursivas que de não ter um limite de chamadas pode dar esse tipo de erros.

Agora fique tranqüila que a pilha terá 2 megabytes mais ou menos para cada programa que executar, certamente esses 2 megabytes serão repartido entre todas as funções como vimos antes, e que se o programa não for alterado o tal parâmetro que aumenta a pilha parâmetro já que sei que pode ser aumentada a pilha.

Eu acho que é mais que suficiente 2 megabytes para a pilha para a maioria das suas aplicações, porem sabendo que pode ser ampliado não temos motivos para alarmarmos, somente você deve saber que memória excessivamente grande deve ser reservada no heap para não saturar seu programa. Também acho que deve ficar tranqüila porque quando você fazer chamadas a funções, você deve saber que MAIS memória será reservada, porem ao terminar essa função será liberada, não abuse de muitas chamadas a funções pesadas e nada acontecerá, tudo é questão de um bom desenho do soft.

Alguns conselhos são o reaproveitamento de variáveis, tipo em vez de declarar 3 variáveis, tente reutilizar uma só delas. A não ser que tenha que ter 2 ocupadas ao mesmo tempo. Não faça funções muito saturadas... separe por temas, tipo lerDados, porem não a utilize para alterar, para isso faça outra tipo alterarDados(). Isso fará que em cada função seja declarado somente o conjunto justo de variáveis que sejam necessárias para realizar a tarefa, você a invoca e se reserva, se cumpre com o requerido e se libera. Se você fizer tudo isso dentro de main essas variáveis não estariam sendo utilizadas o tempo todo, porem sim ocupando espaço, por isso dividir entre "subfunções" traz muito beneficio em rendimento. Use memória dinâmica sempre que for usar vetores muito grandes, e/ou que você quiser que tenha uma longa vida, você só precisará ir passando a direção dela de uma função a outra, e sempre terá ela num lugar que não conta como sobrecarga para a pilha, não se esqueça de libera-la, ela pode ser utilizada em qualquer escopo se esse receber o ponteiro nos parâmetros da função.

Certamente tem muitos mais truques para melhorar o rendimento dos seus programas, conforme você for aumentando sua experiência você irá adquirindo esses conhecimentos, não deixe de visitar fóruns e ler problemas e soluções de outros programadores, você aprenderá muito, um fórum não é só para consulta, mas também para referencia.

Conhecer como funciona e como é a estrutura da memória certamente fará com que sejas uma melhor programadora, muitos cursos não abarcam isso.

ok? =D Fui!

Link para o comentário
Compartilhar em outros sites

  • 0

1) Não entendi essa questão de desligar o computador e ele "esquecer" da memory leak, o que seria "esquecer" ? Quando o computador é ligado, essa memória é realmente devolvida ?

 

2) Veja se eu eu entendi o que você disse. Funciona assim... conforme, por exemplo, eu vou chamado mais funções, vão sendo colocados mais escopos na pilha ( mais segmentos?! ) e quando chega no limite dessa memória padrão do stack, o sistema estaria pegando memória de outro segmento (outro escopo) se continuasse. É isso, né ?

Agora o que não entendi é o seguinte : Eu li em algum site que na hora da compilação, o compilador já calcula toda a memória necessária a ser armazenada. Então, o erro não deveria aparecer na hora da compilação ? Ou seja, não deveria aparacer o erro de segmentação na hora da compilação do programa que tem uma função recursiva ? Esse programa que eu mostrei na mensagem anterior (da função recursiva) compilou normalmente, o erro só apareceu quando eu tentei executar o programa.

 

3) A dúvida principal mesmo de erro de segmentação é essa 2 acima, mas aconteceu algo estranho aqui kkkkkk Eu usei esse número

521133

e não deu problema. Só deu problema quando eu coloquei isso :

int main() {

  int n[536870849];
  return 0;

}

Desculpa deixar esse post longo haha mas é que eu estou aprendendo programação através de uma apostila, aí quando chegou nessa parte de como funciona e como é a estrutura da memória a apostila só ficou mostrando tópicos sem explicá-los.

 

Link para o comentário
Compartilhar em outros sites

  • 0
2 horas atrás, Math_naVeia disse:

 

1) Não entendi essa questão de desligar o computador e ele "esquecer" da memory leak, o que seria "esquecer" ? Quando o computador é ligado, essa memória é realmente devolvida ?

 

a memória RAM é volátil, se apagar o computador tudo o que está na RAM se apaga por falta de corrente elétrica, é como se pegasse um formatado instantâneo, todos os bits se colocam a zero, isso faz com que o pedaço(assim como toda a memória) que estava ocupado fique disponível novamente. O ruim da memory leak é que se você tiver um programa que cause muita fuga de memória, você pode acabar por saturar a RAM, dai você vai precisar fazer um reinicio do sistema para que toda a RAM esteja disponível novamente tal como comentei.

 

2 horas atrás, Math_naVeia disse:

2) Veja se eu eu entendi o que você disse. Funciona assim... conforme, por exemplo, eu vou chamado mais funções, vão sendo colocados mais escopos na pilha ( mais segmentos?! ) e quando chega no limite dessa memória padrão do stack, o sistema estaria pegando memória de outro segmento (outro escopo) se continuasse. É isso, né ?

A ver... vou tentar explicar um pouco melhor, faz tempo que não toco no tema e já estou um pouco enferrujado, certamente algumas coisas nem lembro mais.

Quando executamos nosso programa é criado um processo no nosso computador. Uma porção de memória é reservada para o tal processo. Nessa porção de memória é criado 3 zonas (resumindo algo o tema).
As zonas são:
-A zona de memória estática(por assim dizer), ela é a zona que se encarrega de manter as variáveis globais e static.
-A zona da pilha, onde se armazena as variáveis locais, e o heap que já conhecemos.
Na parte da pilha suponho... existe uma zona chamada stack frame, onde se reserva espaço para as variáveis da função main tais como parâmetros, variáveis locais, etc, mas não é reservado as variáveis para outras funções, quando fazemos a chamada de outra função é adicionado um novo quadro, ou como já falei o tal frame, será apilhado um frame encima de outro, tendo o comportamento de uma pilha(ultimo a entrar, primeiro em sair).
Tanto a pilha como o heap são expansíveis, ou seja que podem ser aumentadas, e também diminuir, porem claro que tem limitações. A pilha por exemplo já vimos que chega cerca de 2 megabytes mais ou menos(ao menos no meu sistema sim), claro que disso depende vários fatores como já falei antes(compilador, sistema operativo, etc), pode variar de pc a pc, por isso você poderá declarar mais que eu, talvez seu computador tenha muita mais RAM e o pc designe mais para sua pilha, realmente não faço a mínima idéia o por quê, o fato é de que tem um limite.
-O heap por outro lado é possível reservar muita mais RAM, se não me engano você pode reservar toda a memória disponível do sistema se fizer falta, mas realmente não é o problema.

A wikipedia diz o seguinte sobre o erro de segmentos:
Falha de segmentação (segmentation fault, também referido por segfault) é um erro que ocorre em programa quando este tenta acessar (para leitura ou escrita) um endereço na memória RAM que está reservado para outro programa (ou o próprio sistema operativo) ou que não existe.

Se fizer a pilha crescer muito está claro que você vai acabar invadindo outras zonas da memória, por isso deve ter um limite, e isso acontece quando saimos desse limite, no caso do heap o limite é toda a memória, porque a memória é finita. O importante é você saber onde estão esses limites para cuidar que seu programa não pule a cerca. Consulte a documentação do seu compilador para saber.

Acho que já fica claro, porem vou deixar algo de leitura para que tire suas conclusões.
Essa é bem clara e resumida:
http://cpp.drgibbs.com.br/cursos/1-c-para-iniciantes/16-ponteiros/01-a-memoria

Essa é muito mais técnica, nem eu entendi muitas coisas, porem a imagem explica bem a estrutura de um processo.
http://www.revista-programar.info/artigos/programacao-insegura-transbordo-de-memoria/

c-processo-memoria.png

Nessa imagem você pode apreciar que tem segmento de texto, esse será usado para estipular o tamanho das funções como você falou. Os segmentos de.BSS e .DATA ao parecer é o que eu falei sobre a zona donde se guarda as globais e estáticas, logo você tem o segmento do heap, um espaço livre, certamente é o espaço permitido para que tanto o segmento de heap como da pilha se expandam, e por ultimo o segmento da pilha do outro lado. Na imagem podemos apreciar também 2 flechas que apontam para o espaço de segmento em branco(sem nome), isso quer dizer que se crescerem muito, no final acho que devem chocar. Não acha?

O melhor é buscar por internet algumas ou varias matérias para aprender sobre o tema, uma sempre complementa a outra.

Até logo e muita sorte.

 

Link para o comentário
Compartilhar em outros sites

  • 0

Beleza! XD
Aqui encontrei um vídeo muito interessante que vai tirar suas duvidas sobre a memoria que é criada para o processo do seu programa:

O vídeo está em inglês, porem você aperte na engrenagem e escolha traduzir ao português que e vai lendo as explicações do rapaz que são muito boas.
Nesse vídeo você pode ter uma idéia do por quê dos falhos de segmentação, toda a memória é alojada umas junto a outras, se saltamos os segmentos... BOOM! >_<
Existe também outro tema de paginação que é interessante ver, se encontro algo deixo aqui, mas não se preocupe que esse não vai lhe afetar, somente ampliaria seus conhecimentos sobre a estrutura da memoria.
Sorte!

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,5k
×
×
  • Criar Novo...