Wielkość struktury

0

Witam, wszystkich. Dawno tu nie byłem. Mam nadzieję, że są tu jeszcze starzy dobrzy wyjadacze (młodzi też mile widziani).
Natrafiłem na intrygujący kłopot ze strukturą i jej wielkością:

#include <cstdio>
#include <conio.h>
#include <iostream>

using namespace std;

struct S1
{
    char a;
    char b;
	char c[5];
	char d;
    short e;
	char f;
	char g;
	char h[4];
	char i[4];
	char j;
} ;

struct S2
{
    char a;
    char b;
	char c[5];
	char d;
//    short e;
	char f;
	char g;
	char h[4];
	char i[4];
	char j;
} ;

int main(int argc, char *argv[])
{
    cout << sizeof(S1) << " " << sizeof(S2);
    getch();
    return 0;
}
 

Gdzie tkwi problem? Policzcie co powinien pokazać w konsoli jako wynik. Testowałem to na G++ i wynik mam zarówno pod windowsem jak i linuksem różnicę 3 bajtów. A przecież to różni się tylko o short, który też sprawdziłem i ma 2 bajty. Gdzie tkwi problem i skąd ta różnica?
Zrobiłem również drugi test na GCC i to samo. Wrzuciłem zgodność z ANSI C i ten sam wynik. Ale już na AVR-GCC jest wynik różnicy o 2 bajty.

No i nic nie rozumiem. Jeszcze możliwa jest tu jakaś optymalizacja, żeby było jakieś wyrównywanie, ale niby do czego ma to być wyrównywane, skoro jest 19 bajtów i 22 bajty a 21 być nie chce z shortem ale bez problemów jest kiedy są same char.

No więc coś mi tu nie gra, bo to wydawało mi się do tej pory zawsze możliwe i proste do policzenia, a tu zdziwienie.

Problem jest to, że wielkość tablicy się różnić może nawet o kilka bajtów a nie tylko jeden, chociaż elementy ułożone są tak jak wynika to z ich wielkości. Więc niby można przekopiować 1:1 ale tylko w przypadku jednej zmiennej, podczas gdy tablice struktur różnią się wielkością tablicy źródłowej, więc trzeba kopiować element po elemencie, co trochę jest dziwne.

Może jakiś parametr kompilacji aby się tego pozbyć?

Obecnie testowałem tylko
-Os i -O2 w celu sprawdzenia, w obu przyp. tak samo.

0

A z floatem jest jeszcze gorzej. Różnica jest jest 9 bajtów. O co biega?

0

rozwiązanie:
attribute((packed))

rozumiem tłumaczenie, wyrównywaniem, ale nie sądziłem, że przez to może legnąć w gruzach przenośność podstawowych typów w strukturze. Nie mniej jednak już rozwiązałem problem.

0

Obiegłeś mnie, sam znalazłem.

PS. osobiście mi na PC to nie przeszkadza. Ale problem z przenośnością. Jak wiadomo avr-gcc to nie ta sama historia jeśli chodzi o podejście do programowania, gdzie można robić rozrzut, bo ilość pamięci jest mocno ograniczona, a więc skopiowanie danych ze struktury w pamięci najlepiej jest bajt po bajcie (bo najszybciej i najprościej). I fajnie byłoby gdyby struktura na PC miała takie same ustawienie :).

Nie mnie jednak dzięki za zainteresowanie.

1

To jest tzw. padding. Każdy typ ma pewną wielkość (tzw. alignment) do której jego początek musi być dopasowany. Pamiętaj, że kompilator nie ma prawa poprzesuwać elementów w strukturze aby uzyskać jak najmniejszą wielkość.

Zapewne u Ciebie short ma alignment 2-bajtowy, więc

struct x{
char a;
char b;
char c;
char d;
short e;
char f;
}; // sizeof = 6
// i
struct y{
char a;
char b;
char c;
char d;
char f;
}; // sizeof = 4

zachowają się zgodnie z Twoimi przewidywaniami.
Ułożenie pól w w/w strukturach (zakładam sizeof short == 2):

0 a
1 b
2 c
3 d
4 e
5 e

oraz

0 a
1 b
2 c
3 d

Jednak gdyby usunąć element d to w przypadku pierwszym niewiele by się zmieniło:

struct x{
char a;
char b;
char c;
short e;
}; // sizeof = 6
// i
struct y{
char a;
char b;
char c;
}; // sizeof = 3

Ułożenie:

0 a
1 b
2 c
3 [nic]
4 e // wyrównanie short jest do 2 bajtów
5 e

oraz

0 a
1 b
2 c

Żeby było ciekawiej, cała struktura musi być wyrównana do alignmentu jej elementu o największym alignmencie (niekoniecznie wielkości, bo tablica 100 charów ma wciąż alignment 1), więc gdyby w początkowej strukturze x przenieść element d na koniec, to jej wielkość będzie równa 8:

struct x{
char a;
char b;
char c;
short e;
char d;
}; // sizeof = 8

Rozmieszczenie elementów:

0 a
1 b
2 c
3 [nic]
4 e
5 e
6 d
7 [nic]

Po co wyrównanie na końcu? Aby w przypadku tablic kolejne ich elementy miały zagwarantowany alignment.

0

dlatego attribute((packed)) w tym przypadku jest najlepszym rozwiązaniem, bo w obu przypadkach wtedy mam te same dane. Fakt, że pewnie wolniej się przetwarzają na PC, ale w tym przypadku to korzystniejsze, niż ręczne przerabianie 50 pól struktury.

O tym, że jest jakieś wyrównywanie to tak myślałem właśnie, bo to nie pierwszy procesor (x86) gdzie taka sztuczka byłaby zrobiona, jednak trochę mi to w tym przyp. narobiło bałaganu. Dobrze, że wszystko sprawdzam dokładnie zawsze i dla formalności sprawdziłem sizeof struktury na avr-gcc i zwykłym gcc na x86

0

Na x86 to nie ma aż takiego znaczenia, ale na innych architekturach niepoprawny alignment może nawet uniemożliwić wykonanie programu. Zamiast używać pakowania lepiej poprzestawiaj pola swojej struktury w taki sposób, abyś jak najmniej tracił na paddingu. W przypadku struktur składających się tylko z charów i shortów, będziesz tracił max 1 bajt.

0

Tak, też można i tak, ale jak struktura zawiera struktury, to problem się znaczenie bardziej komplikuje. A główna idea to oszczędność czasu. Szybciej jest kopiuj/wklej + attribute((packed)) niż przestawianie i testowanie zwłaszcza, że mówimy o zaledwie 1kB danych konfiguracyjnych :P. A to oznacza, że nawet najgorszy PC wyśmieje taki narzut obliczeń związanych z koniecznością życia z attribute((packed))

0

No i jeszcze jedna rzecz: zobacz, czy avr-gcc nie ma w defaultowym wywołaniu czegoś w stylu -fpack-struct, to raz. Dwa - tam podstawowa długość to 8 bitów, także nie wiem czy w ogóle jest tam konieczny alignment do czegoś innego niż bajtu...

1 użytkowników online, w tym zalogowanych: 0, gości: 1