offsetof()

1. offsetof()

doom load
doomload

(usa Ubuntu)

Enviado em 13/11/2016 - 12:03h

Ao executar um exercício sobre offsetof() eu obtive um resultado inesperado, eu fiz o código:


#include <stdio.h>
#include <stddef.h>

typedef struct
{
int campo1;
double campo2[100];
char campo3[20];
} REG;

void main()
{
int i = sizeof(int);
printf("sizeof int %d\n", i);

i = sizeof(double);
printf("sizeof int %d\n", i);

i = sizeof(char);
printf("sizeof int %d\n", i);

printf("Campo1 comeca no offset %d\n", offsetof(REG, campo1));

printf("Campo2 comeca no offset %d\n", offsetof(REG, campo2));

printf("Campo3 comeca no offset %d\n", offsetof(REG, campo3));
}


E obtive com resultado:

sizeof int 4

sizeof int 8

sizeof int 1

Campo1 comeca no offset 0

Campo2 comeca no offset 8

Campo3 comeca no offset 808

Aí eu pergunto:
Se o tamanho do int é 4, não era para o campo2 começar em 5?

Para o double esta coerente, se o campo2 começa em 8, e tem 100 posições na matriz, o campo3 começa em 808...

Obrigado




  


2. Re: offsetof()

Enzo de Brito Ferber
EnzoFerber

(usa FreeBSD)

Enviado em 14/11/2016 - 08:09h

Olá,

O problema que você encontrou chama-se alinhamento de memória.
O processador lê dados da memória em "pacotes", normalmente de 32bits ou 64bits (dependendo da sua arquitetura).
Isso acontece pois o processador tem que ler os dados da memória e armazena-los em um registrador, pois um CPU só pode realizar operações em dados nos registradores.
Os compiladores C normalmente alinham os dados de variáveis e estruturas para refletir isso, de forma que quando você acessar os dados, nenhuma operação de shift seja necessaria para manipular os dados.

No GCC existe um atributo chamado packed que possiblita compactar a estrutura, e você verá exatamente o que estava esperando:



#include <stdio.h>
#include <stddef.h>

typedef struct __attribute__((packed)){
int campo1;
double campo2[100];
char campo3[20];
} REG;

void main()
{
int i = sizeof(int);
printf("sizeof int %d\n", i);

i = sizeof(double);
printf("sizeof double %d\n", i);

i = sizeof(char);
printf("sizeof char %d\n", i);

printf("Campo1 comeca no offset %d\n", offsetof(REG, campo1));

printf("Campo2 comeca no offset %d\n", offsetof(REG, campo2));

printf("Campo3 comeca no offset %d\n", offsetof(REG, campo3));
}



A saída:


$ ./align
sizeof int 4
sizeof double 8
sizeof char 1
Campo1 comeca no offset 0
Campo2 comeca no offset 4
Campo3 comeca no offset 804


Esse site fala bastante coisa sobre compactação de estruturas (em inglês):
http://www.catb.org/esr/structure-packing/

Qualquer coisa posta denovo,
Enzo Ferber
[]'s


$ indent -kr -i8 src.c

"(...)all right-thinking people know that (a) K&R are _right_ and (b) K&R are right."
- linux/Documentation/CodingStyle - TORVALDS, Linus.



3. Re: offsetof()

Paulo
paulo1205

(usa Ubuntu)

Enviado em 14/11/2016 - 13:34h

Como o Enzo disse, tudo tem a ver com a arquitetura particular que você estiver usando.

Algumas arquiteturas obrigam todos os acessos em memória a estarem alinhados com o tamanho da palavra do processador (e.g. numa máquina com palavras de 32 bits, ou 4 bytes, todos os dados, independentemente de seus tamanhos individuais, devem estar dispostos em endereços que sejam múltiplos de 4 bytes). Outras permitem dispor dados de diferentes tamanhos em endereços que sejam múltiplos do tamanho daquele dado. Outras ainda permitem que qualquer dado possa estar em qualquer endereço, mas podem ter desempenho degradado se os dados estiverem com alinhamento diferente do esperado.

Nossos PCs até permitem o uso de dados desalinhados, com possível degradação de desempenho. No entanto, os compiladores de linguagens de alto nível preferem evitar o desempenho degradado, e para tanto forçam um realinhamento dos dados. Esse realinhamento é particularmente mais visível quando você colocar diversos membros de tamanhos diferentes dentro de uma estrutura.

A julgar pelos números que você apresentou, você deve estar usando uma arquitetura de 64 bits. Em arquiteturas assim, dados de até oito bytes podem ser lidos ou escritos em memória com uma única operação, desde que estejam devidamente alinhados num endereço que seja múltiplo de oito bytes. Se você os dispuser de outra forma, uma operação de leitura de 64 bits teria de ser feita em duas ou mais etapas (por exemplo: duas operações de 32 bits).

Uma dica para trabalhar com estruturas, de modo a não precisar de atributos como packed (até porque esses atributos podem não ser portáveis entre diferentes compiladores) nem desperdiçar muita memória com padding, é procurar dispor os membros da estrutura numa ordem que vá do maior para o menor.

Veja o seguinte exemplo:

#include <stddef.h>
#include <stdio.h>

struct dic { double d; int i; char c; };
struct dci { double d; char c; int i; };
struct idc { int i; double d; char c; };
struct icd { int i; char c; double d; };
struct cdi { char c; double d; int i; };
struct cid { char c; int i; double d; };

#define PRINT_INFO(x, a, b, c) \
printf( \
"sizeof(" #x ")=%zd\n" \
"\toffset of " #a ": %zd\n" \
"\toffset of " #b ": %zd\n" \
"\toffset of " #c ": %zd\n", \
sizeof(x), offsetof(x, a), offsetof(x, b), offsetof(x, c) \
)

int main(void){
printf(
"sizeof(char)=%zd; sizeof(int)=%zd; sizeof(double)=%zd\n",
sizeof(char), sizeof(int), sizeof(double)
);
PRINT_INFO(struct dic, d, i, c);
PRINT_INFO(struct dci, d, c, i);
PRINT_INFO(struct idc, i, d, c);
PRINT_INFO(struct icd, i, c, d);
PRINT_INFO(struct cdi, c, d, i);
PRINT_INFO(struct cid, c, i, d);
}


Compilando-o e rodando numa máquina de 64 bits (com opções default na compilação), eis a saída.

sizeof(char)=1; sizeof(int)=4; sizeof(double)=8
sizeof(struct dic)=16
offset of d: 0
offset of i: 8
offset of c: 12
sizeof(struct dci)=16
offset of d: 0
offset of c: 8
offset of i: 12
sizeof(struct idc)=24
offset of i: 0
offset of d: 8
offset of c: 16
sizeof(struct icd)=16
offset of i: 0
offset of c: 4
offset of d: 8
sizeof(struct cdi)=24
offset of c: 0
offset of d: 8
offset of i: 16
sizeof(struct cid)=16
offset of c: 0
offset of i: 4
offset of d: 8


A título de comparação, veja o que acontece quando eu compilo e rodo o mesmo programa em modo de 32 bits.

sizeof(char)=1; sizeof(int)=4; sizeof(double)=8
sizeof(struct dic)=16
offset of d: 0
offset of i: 8
offset of c: 12
sizeof(struct dci)=16
offset of d: 0
offset of c: 8
offset of i: 12
sizeof(struct idc)=16
offset of i: 0
offset of d: 4
offset of c: 12
sizeof(struct icd)=16
offset of i: 0
offset of c: 4
offset of d: 8
sizeof(struct cdi)=16
offset of c: 0
offset of d: 4
offset of i: 12
sizeof(struct cid)=16
offset of c: 0
offset of i: 4
offset of d: 8







Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts