Memória em C - C - Portugal-a-Programar
Memória em C
Por Localhost, 23 de Dezembro de 2009 em C
Olá pessoal, isto não é uma pergunta técnica de programação mas eu gosto sempre de saber como é que as coisas funcionam. Alguém me podia explicar como é que a memória funciona num programa escrito em C? Tipo stack e isso?
Queres saber a que nível? Saber em que zona da memória ficam armazenadas as variáveis, ou queres mesmo perceber toda a lógica de instruções que existe na memória? É que a 1ª é uma tarefa exequível, a 2ª obriga a dar-te umas 2 cadeiras de Arquitectura de Computadores.
Gostava de perceber as coisas de uma maneira muito simples, só mesmo para compreender o que acontece e isso... Se for possível é claro.
Pah é um bocado difícil estar a lembrar de todos os exemplos que podem aparecer. Será mais fácil tu colocares um pouco de código e pedires que te expliquem. Porque existem variáveis locais e globais, constantes ou não, static ou não static, etc...
Hmm deixa-me ver, por exemplo:
//próxima instrução
Neste exemplo como é que a memória funciona?
Acho que é: A função main armazena no stack o endereço da próxima instrução, correcto?
O Baderous tem razão, mas vou tentar dar uma explicação rápida e concisa, e não cometer muitos erros. Não sou especialista em arquitectura de computadores, e por aqui haverá sempre quem possa corrigir-me e discutir o que eu disser
A memória está "separada" em 3 "tipos": estática, dinâmica e automática. Notem-se as aspas, porque a distinção é feita unicamente pelo gestor de memória, não havendo mais nenhuma diferença entre os "tipos".
Na memória estática são armazenadas as variáveis globais e as variáveis estáticas. As variáveis aqui armazenadas têm sempre como valor inicial "tudo a zeros", assegurado pelo gestor de memória no início da aplicação, a menos que sejam inicializadas com outro valor.
A memória dinâmica é onde as funções malloc e afins vão buscar a memória quando são invocadas. É onde reside o heap.
A memória automática é onde reside o stack, e consequentemente onde são alojadas as variáveis locais.
Hmm, entendi! O Stack é também onde ocorrem operações rápidas de entrada e saída correcto?
E quanto àquele exemplo que eu dei, estava correcto?
Eu dividiria ainda mais a memória:
- Rodata: constantes read-only
- Code/Text Segment: código (instruções). Também é read-only
- Data: variáveis globais não inicializadas a 0. Tem uma parte read-only e outra read-write.
- BSS: variáveis globais e static que são inicializadas a 0 (pelo programador ou não)
- Heap: conteúdo das alocações dinâmicas de memória
- Stack: variáveis locais
Agora há N situações em que se poderia ver em que zona da memória vai ficar guardada determinada variável e em que zona fica o seu conteúdo.
Parece-me a mim que estás a querer entrar no domínio da stack frame, isso já é demasiado low-level para estas horas. x)
Era mesmo isso, stack frame, eu tive a ver aqui algumas cenas. É o seguinte xD: Eu tive a dar aqui uma revisão nos ponteiros porque não tinha percebido muito bem e agora que até percebo como funciona e isso tudo envolvi-me em mais e mais coisas xD. Isto tudo levou-me ao estado comum do: "oh não, eu não sei nada de nada" ahahahah, e queria sair deste estado de ignorância
Btw & off-topic: Vocês sabem em que faculdades se dá C em programação? Eu estava a pensar na FEUP, o que me dizem? É que estou no 10º ano e as dúvidas são grandes (:
P.S. É só um pequeno off-topic
Há muitas situações a considerar, muitas vezes ocorrem erros como o tentar alterar algo que é read-only, e só nos apercebemos disso após pesquisarmos um bocado acerca do assunto e, eventualmente, aterramos num fórum qualquer onde alguém já teve a mesma dúvida e aprende-se com a explicação da mesma. Mas uma coisa é certa, dá muito jeito ter noção da "arrumação da casa" num programa.
Dá-se C em todas as faculdades.
Mas uma coisa é certa, dá muito jeito ter noção da "arrumação da casa" num programa.
. Exacto por isso mesmo é que eu vim aqui perguntar. Eu gosto de saber como as coisas funcionam e isto está a dar comigo em doido
EDIT: Quanto ao off-topic, não tinha o conhecimento disso, pensava que cada faculdade dava diferentes linguagens :s
Olá pessoal. Hoje com aquilo das conversas da memória e isso tudo lembrei-me de trabalhar ainda mais os ponteiros. Resolvi fazer um programa e obtive um resultado muito interenssante e tirei uma conclusão dele, no entanto não sei se está correcta. O code:
int fun(int *num2)
printf("In fun function: %d\nAddress: %p\n", *num2,num2);
sqr(num2);
int chama(int *numero, int(*func)(int *num))
printf("In chama funtion: %d\nAddress: %p\n", *numero,numero);
int sqr(int *num)
int (*p2)(int *num4);
printf("In sqr function: %d\nAddress: %p\n", *num,num);
p2 = fun;
chama(num,p2);
int (*p)(int *num2);
int nmr = 10;
printf("In main function: %d\n", nmr);
p = sqr;
number = &nmr;
chama(number,p);
Se compilarem e executarem vão ver que ao fim de algum tempo dá um segmentation fault. O quê que eu fiz? Mudei a variável nmr para o heap (ahahahah) e vi que já não dava o erro! Então conclui que a variável nmr é destruída no final de algum tempo. Será assim?
O segmentation fault acontece por stack overflow. Acontece quando entras numa função recursiva, e nunca retornas: eventualmente o espaço no stack fica preenchido.
As variáveis locais só são "destruídas" quando o bloco que as contém termina. No caso, só quando a função main termina é que a variável nmr é libertada.
Não vejo em que é que criares dinamicamente o espaço para o inteiro influencia o erro. Aqui continua a acontecer. Mostra o código modificado, sff.
LOL, que vergonha :-[ Eu confundi, pensei que o heap era onde residiam as variaveis globais. O que eu queria dizer era que declarei nmr como o variável global!
Hoje tive 2horas a perceber isto de ponteiros para funções e fiquei com uma dúvida... Qual é a utilidade disto sem ser quando existe um stack overflow conseguir-mos mudar o EIP (isto já falando noutra coisa que tive a ver, atençao! Apenas por motivos de aprendizagem e para perceber melhor como trabalha a memória)!
Mesmo declarando como variável global, não resolve. Sempre que fazes uma chamada a uma função, estás a ocupar um pouco do stack, para guardares, entre outras coisas, as variáveis locais e o endereço de onde foi chamada a função, para que quando a função termina, possas lá voltar. Quando a função retorna, esse espaço no stack é libertado. Se chamas uma função recursiva repetidamente, ela nunca vai terminar, e eventualmente todo o espaço do stack é utilizado. Nesse momento, ocorre um stack overflow.
Uma possível utilização de apontadores para função, e a 1ª que me vem à memória, é por exemplo em funções de ordenação de listas, em que passas à função de ordenação um apontador para uma função que compara dois elementos do tipo da lista que queres ordenar. Por exemplo, se queres ordenar de forma ascendente, passas como parâmetro uma função que diz se o 1º valor é superior ao 2º; se queres ordenar de forma descendente, passas uma função que diz se o 1º valor é inferior ao 2º.
Mas a mim resolveu... Eu declarei como variável global, deixei a fazer o loop infinito durante algum tempo e não se deu o stack overflow.
Só uma coisa, denomina-se como stack overflow quando existe um "transboradamento" de dados ou quando se explora esse "transboradamento" de dados ao mudar o EIP da função ou seja o endereço do próximo comando da função?
EDIT: Atenção que a minha ideia naquele programa que mostrei não era fazer um loop infinto mas sim fazer um conjunto tão grande que me confundi-se o cerebro de maneira a eu ter de pensar sobre aquilo e finalmente perceber o que era aquilo!
Hmm, buffer overflow é baseado em stack overflow ou vice-versa?
Pode ser ou não: http://en.wikipedia.org/wiki/Buffer_overflow
Sim, já tinha visto isso. Mas o buffer não é o mesmo que stack? O stack é o que armazena todos os dados de uma determinada função. Dentro do stack existe o stack frame que está dividido em várias partes. O buffer é a string em si correcto? Ou seja nós ao passar os limites do buffer estamos realmente a também a ter um stack overflow porque vai sobreescrever outras parte do stack correcto?
Buffer = string.
Stack inclui o Buffer.
Ao passarmos os limites do buffer temos um buffer overflow mas também um stack overflow.
Foi isto que percebi :s
Stack overflow ocorre quando preenches o stack.
Buffer overflow ocorre quando escreves para além da capacidade de um array, seja ele de caracteres (string) ou outra coisa qualquer. Além de que o buffer de que falas tanto pode estar no stack como no heap, como na memória estática.
Mas no caso de estar no stack. Ele ao passar os limites do buffer vai consequentemente lançar dados para o stack ou seja pode ser dados maliciosos como mais uma vez alterar o EIP (eu insisto com o EIP ) correcto?
Senão não dá para fazer nada com buffer overflow, maliciosamente (não me interpretem mal, por favor. Sou apenas curioso).
Sim, potencialmente isso pode acontecer, se souberes exactamente onde escrever e o que escrever. Mas isso não causa por si só um stack overflow.
Ok, vou dar um exemplo prático agora para esclarecer aqui algumas coisas:
printf("Vulnerável\n");
p = vuln;
Isso aí é um stack e um buffer overflow ao mesmo tempo correcto? Estou a passar os limites do buffer e sobreescrever valores no stack.
Compila, executa e passa os valores para veres o que acontece...
EDIT: Eu acho que o que acontece no exemplo anterior é o seguinte: Ele passa os valores e vai-se deslocar até ao EIP, já no EIP ele lê o ponteiro que aponta para a outra função ou seja ele sobreescreve o endereço do EIP para o conteudo do ponteiro p, e porquê? Porque ele se encontra lá, no EIP. Acho que é mais ou menos isso mas ainda estou muito verde nestas andanças de baixo nível