адресная арифметика и выравнивание
Так, типа, выравнивание разве распространяется на динамическую память?
такое компиляется, по крайней мере:
struct a_s {
int q;
};
struct b_s {
char z;
};
#define max(a,b) (a>b?a:b)
struct padding {
char padding[max(sizeof(struct a_s sizeof(struct b_s];
};
#undef max
struct padding* array;
...
void * buf = malloc(sizeof(struct a_s) + sizeof(struct b_s;
struct a_s *a;
a = (struct a_s *)buf
.......
b = (struct b_s *buf + sizeof(struct a_s;
Так будет надежнее.
union padding {
struct a_s a;
struct b_s b;
};
union padding* array;
...
a = (struct a_s*array + 12);
b = (struct b_s*array + 21);
struct c_s {
struct a_s a;
struct b_s b;
};
struct c_s *c;
c = (struct c_s *)malloc(sizeof(struct c_s;
a = &c->a;
b = &c->b;
И мне нужно доказать человеку, с которым я спорю, что у этого способа есть достоинства перед адресной арифметикой.
Вариант с padding-ом может быть сильно неоптимальным, если размер одной структуры сильно отличается от размера второй структуры.
1. Лучше читается
2. Меньше вероятность ошибки.
Минусы следующие:
1. если величина выравнивания стоит довольно большая, то будут соответсвующие потери
2. При разных величинах выравнивания будут получаться разные варианты совместного размещения структур, что приведет к проблемам, если совместный блок сохранется на винт, передается по сети.
1. если величина выравнивания стоит довольно большая, то будут соответсвующие потериАллоцируется не в таких уж и больших количествах. Скорость важнее расходов памяти.
При разных величинах выравнивания будут получаться разные варианты совместного размещения структур, что приведет к проблемам, если совместный блок сохранется на винт, передается по сети.Ни того не другого не происходит.
Есть еще недостаток из-за которого спор и возник: struct c_s {} объявляется в инклюднике включаемом большим числом программ, а struct a_s {} в другом, не имеющем к большинству из этих программ никакого отношения. Получается, что для того что бы скомпилиться им нужно включить второй инклюдник тоже.
А собственно зачем все это надо, может компилятор лучше знает как надо?
Речь идет о языке Це, если кто-то не догадался.
А если серьезно, то я повторюсь, надо писать так, чтобы все это была не твоя проблема, а компилятора.
$ cat a.c
#include <stdio.h>
struct a{
char fa[3];
} sa;
struct b{
int fb;
} sb;
struct c{
struct a fa;
struct b fb;
} sc;
int main
{
printf ("a=%d b=%d c=%d (c.b=%d)\n",
sizeof(sa
sizeof(sb
sizeof(sc
char*&sc.fb-char*≻
}
$ gcc a.c
$ ./a.out
a=3 b=4 c=8 (c.b=4)
$
Маза, если расположить структурки a и b в памяти друг за другом,
то обращение к int в struct b будет по невыровненному адресу.
Не каждая архитектура такое позволит.
Правила раздела ты уже прочел?
Маза, если расположить структурки a и b в памяти друг за другом,На i386 все в порядке. Так как тип a правильный, то (a + 1) увеличивает указатель с учетом выранивания, то есть на 4 в твоем примере. Поэтому меня интересует на каких архитектурах это будет не так? И будет ли?
то обращение к int в struct b будет по невыровненному адресу.
> с учетом выранивания, то есть на 4 в твоем примере.
не на 4, а на 3=sizeof(struct a)
можешь проверить
struct a{
char fa[3];
};
struct b{
int fb;
};
int main
{
struct a *sa;
struct b *sb;
sa = (struct a *)malloc(sizeof(struct a)+sizeof(struct b;
sb = (struct b *sa + 1);
printf ("a=%d b=%d (&fa - &fb=%d)\n",
sizeof(sa
sizeof(sb
char*&sb->fb - char*&sa->fa;
}
a=4 b=4 (&fa - &fb=3)
printf ("*sa = %u, *sb = %u\n", (unsigned int) sa, (unsigned int) sb);
Спасибо!
printf ("a=%d b=%d (&fa - &fb=%d)\n",
sizeof(sa
sizeof(sb
char*&sb->fb - char*&sa->fa;
sa, и sb - это указатели, и sizeof от них == 4
надо писать sizeof(*sa)
printf ("a=%d; b=%d; (&fa-&fb)=%d\n",
sizeof(struct a
sizeof(struct b
char*&sb->fb - char*&sa->fa;
a=3; b=4; (&fa-&fb)=3
*sa = 134524976, *sb = 134524979
Ошибка в сложении ищется? Её там не будет.
> *sb = 134524979
А вот корявое выравнивание -- запросто.
Такие архитектуры и вправду встречаются?
Мне казалось, что к невыровновнем данным обращаться можно всегда, но такое обращение будет тормозить, т.к. требует больше циклов шины.
В ARM кажется нельзя вообще.
> Такие архитектуры и вправду встречаются?
Если речь именно об архитектуре, то встречаются.
Пример, скоторым я недавно столкнулся -- на оптероне нужно выравнивать
стек на границу 16 байт, иначе вызов sse'шной инструкции, которая предполагает
такой alignment (movaps внутри вызова printf для печати double, например валит прогу
с SEGV. Тут, правда, это было на моей совести, т.к. стек я делал руками.
В общем случае это на совести компилера, который подобную инструкцию
позовёт только тогда, когда уверен, что косяков не будет, или на совести
писателей оптимизированных под архитектуру асмовых макросов.
> Мне казалось, что к невыровновнем данным обращаться можно всегда,
> но такое обращение будет тормозить, т.к. требует больше циклов шины.
В лучшем случае -- больше циклов, в худшем -- обработчик исключительной
ситуации для эмуляции кривой инструкции.
Возможности проверить что именно происходит нету,
но доступ к невыровненным данным проходит успешно на оптероне
(alignment check похоже вылючен) и ppc.
Можно попытаться замерить падение производительности.
Ошибка в сложении ищется? Её там не будет.Ищутся возможные последствия такой адресной арифметики.
> ситуации для эмуляции кривой инструкции.
Если есть такой обработчик.
Внутри ядра к примеру -- скорее всего нет.
void * buf ...
buf + sizeof(struct a_s)
На С++ void * с int-ом нельзя сложить, только на чистом C.
MSVC это делает без особых проблем в Си++ -проектах
И мне нужно доказать человеку, с которым я спорю, что у этого способа есть достоинства перед адресной арифметикой.
Так вроде же у выравнивания, а не у его отсутствия есть достоинства, иначе зачем бы его делали?
Такие архитектуры и вправду встречаются?
Palm
MSVC это делает
Может быть...
xenon:~$ cat > a.c
int main(int argc, char * argv[])
{
void * x = 0;
void * y = x + 1;
}
xenon:~$ g++ a.c
a.c: In function `int main(int, char**)':
a.c:4: error: pointer of type `void *' used in arithmetic
xenon:~$
Можно попытаться замерить падение производительности.
На низкий alignment кстати офигенно реагирует механизм когерентности кешей на SMP. До 10 раз можно встрять!
Приношу свои извинения за дезу
sizeof(char) == 1?
По-моему, по стандарту - да. Но далеко не все компиляторы и не все платформы живут по стандартам.
для любых чаров (и только для них) стандарт гарантирует размер 1(пункт 5.3.3 стандарта-98)
Ты путаешь ОС с процессорной архитектурой ARM, которая используется в некоторых (современных) устройствах на этой ОС.Такие архитектуры и вправду встречаются?Palm
иначе вызов sse'шной инструкции, которая предполагает
такой alignment (movaps внутри вызова printf для печати double, например валит прогу
с SEGV
А ты уверен, что невыровненный movaps приводит именно к SEGV? Скорее уж какой-нибудь SEGILL или еще чего... У меня под виндой вообще в Privileged instruction свалился В доступе под unix-ом у меня только Athlon есть, там вообще SSE нету , но из общих соображений -- что movaps-у в printf-е делать? Может ты просто в чем другом напутал?
стек я делал руками
movaps - move aligned packed single-precision floating poing
privilege level ей нужен 3 (то есть любой)
при нарушении выравнивания вызывает general protection exception
короче, иди читай доки до просветления
http://www.amd.com/us-en/assets/content_type/DownloadableAssets/dwamd_26568.pdf
Че мне доки читать, я взял и запустил, мне отладчик окошко выдал... Ладно, во вторник пойду на работу, запущу под Linux-ом, расскажу...
Оставить комментарий
sergey_m
Есть две структурыstruct a_s;
struct b_s;
о выравнивании которых ничего не известно. Возможно оно есть, возможно его нет. Хочется их аллоцировать и хранить вместе, одну за другой. Есть следующий код:
Вопрос: будет ли теперь b указывать на начало struct b_s на любых архитектурах при любом выравнивании?