02 - Representação de dados em RAM
Experimentos
Vamos trabalhar com os arquivos experimento0-4.c
. Compile e execute cada um deles, execute e analise suas saídas. Comente os resultados comparando os valores das constantes no código em C e a saída dos programas. Tente entender o que está acontecendo.
Para compilar cada .c
, utilize:
Exercise 1
Answer
No exercício é definido uma union
, que se parece com uma struct
,
mas os campos compartilham o mesmo espaço de memória. Ou seja os compos
variavel.num
e variavel.letra
ocupam o mesmo endereço, e a estrutura
ocupa 4 bytes, o tamanho do maior campo (char 1 byte e int 4 bytes).
acima é atribuído o valor 130 os campos, mas quando é impresso o tipo char (printf("%c\n", variavel.letra);
),
o valor 130 (0x82 em hexadecimal) não é ASCII imprimível padrão, então pode sair como um símbolo estranho (ou nada visível).
Na saída do print printf("%#08x\n", variavel.letra);
(print no formato hexadecimal) é impresso o valor 0xffffff82
pois como o campo variavel.letra
é do tipo char (signed), temos que 130
=0x82
(unsigned) e
-126
= 0xffffff82
(unsigned).
Exercise 2
Answer
Os números do experimento1.c são inicializados na notação hexadecimal (por exemplo int num1 = 0x0a0c0b0d;
).
Na memória os bytes da variável num1
são armazenados do byte menos significativo para o mais significativo,
ou seja, 0d0b0c0a
. Isso mostra que a arquitetura do seu processador é little-endian (x86-64).
Exercise 3
Answer
No exercício é declarado um array de short
com 3 elementos, cada elemento com 2 bytes. Note que na saída a ordem dos elementos no array é preservada, mas cada short individual é armazenado em little-endian -- LSB (byte menos significativo) primeiro.
declaração: short arr[] = {0x1122, 0x3344, 0x5566};
saída: 22 11 44 33 66 55
Exercise 4
Answer
Nesse exercício é declarado uma string na linguagem C
char *string = "Oi C :-)";
essa string ocupa ocupa 8 caracteres visíveis + 1 terminador \0
= 9 bytes. Na saída são impressos
os caracteres e seu código ASCII em hexadecimal.
'O'(4f) 'i'(69) ' '(20) 'C'(43) ' '(20) ':'(3a) '-'(2d) ')'(29) ''(00)
Exercise 5
Representação de struct
em RAM
A utilização de struct
junta tudo que já vimos sobre representação de todos os tipos de dados na memória. Não se esqueça de levar em conta as questões de alinhamento mostradas na parte expositiva da aula.
Atenção!
Você não deverá abrir o código de parte1.c
. Ele está no repositório para você poder conferir suas respostas.
Vamos compilar?!
Compile e execute parte1.c.
Exercise 6
Answer
No exemplo não é possível identificar o tamanho “real” de cada campo na memória por causa do alinhamento de tipos na memória, mas podemos identificar o tamanho máximo ocupado pelos endereços dos campos consecutivos, por exemplo:
Campo icon_id
Endereço: 0x596b2fdac020
Próximo campo (level
) começa em 0x596b2fdac028
Diferença: 0x28 - 0x20 = 0x8 = 8 bytes
Exercise 7
Exercise 8
Hora de conferir!
Agora confira suas respostas no arquivo parte1.c.
Examinando a execução de programas usando gdb
Podemos examinar um programa durante sua execução usando o gdb. Podemos parar em qualquer instrução do programa, examinar conteúdo de registradores e da memória e listar todos os símbolos disponíveis (que podem ser funções ou variáveis globais).
Dica! 1
Para compilar um programa com suporte a debugging usamos a flag -g
do gcc
Siga os passos para para carregar um programa usando o gdb:
-
Lance o
gdb
e passe para ele seu programa: -
Para sair, utilize:
Tip 2
O gdb é uma ferramenta poderosa que possui muitas opções. Sua documentação está online e pode ser acessada em: https://sourceware.org/gdb/onlinedocs/gdb/index.html#Top.
Uma explicação detalhadas dos comandos do gdb pode ser encontrada nesse link: https://diveintosystems.org/book/C3-C_debug/gdb_commands.html.
Nesta primeira parte iremos abrir o arquivo parte2.c e olhar seu conteúdo. Também executaremos o programa compilado parte2.
Exercise 9
Atenção, tarefa dupla!
Para os próximos exercícios, compile o arquivo parte2.c
com e sem debugging e repita cada exercício com ambas as versões do executável. Exemplo de compilação:
Exercise 10
Exercise 11
Muita informação!
Muitos nomes são estranhos. Eles fazem parte do padrão de arquivos executáveis ELF, que contém informações específicas do sistema operacional usado. Você não precisa se preocupar com estes nomes.
Exercise 12
Answer
A primeira coluna do comando info variables no GDB mostra os endereços de memória onde as variáveis estão armazenadas.
Exercise 13
Exercise 14
Answer
De forma geral, o comando x
aceita até três argumentos de formatação
f: o formato de exibição exemplos: s = string, i = instrução, x = hexadecimal, d = decimal, t = binário, a = endereço, etc.
u: a unidade de exibição (quantos bytes cada item ocupa) exemplos: b = byte (1 byte), h = 2 bytes, w = 4 bytes, g = 8 bytes
Considerando que o endereço da varíavel global_array
seja 0x0000000000004010
teríamos o comando:
Exercise 15
Exercise 16
Answer
Como a variável global_str
é um ponteiro para uma string char *
, o primeiro passo é descobrir
o endereço armazendo na variável global_str
:
Agora basta usar o endereço para acessar a string armazenada na memória