LINUX.ORG.RU

В чём разница между alignof в C и в C++?

 , , ,


2

2

По мотивам Неверное выравнивание начатого pavlick

Возьмём такой код на C:

#include <stdio.h>
#include <stdalign.h>

int main() {
	printf("alignof(double) == %u\n", alignof(double));
}

Возьмём компилятор:
% gcc --version
gcc (Ubuntu 4.9.2-0ubuntu1~14.04) 4.9.2
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Скомпилируем и запустим (ВНИМАНИЕ! 32 БИТА!):
% gcc -march=i686 -m32 -std=c11 -o test-c test-c.c

% ./test-c
alignof(double) == 4

Теперь возьмём такой код на C++:
#include <iostream>

int main() {
	std::cout << "alignof(double) == " << alignof(double) << '\n';
}

Компилятор:
% g++ --version
g++ (Ubuntu 4.9.2-0ubuntu1~14.04) 4.9.2
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Запускаем:
% g++ -march=i686 -m32 -std=c++11 -o test-cpp test-cpp.cc

% ./test-cpp
alignof(double) == 8

Итак, почему выравнивание разное?

P.S. В x86_64 в обоих случаях будет 8.

★★★★★

-malign-double -mno-align-double Control whether GCC aligns double, long double, and long long variables on a two word boundary or a one word boundary. Aligning double variables on a two word boundary will produce code that runs somewhat faster on a `Pentium' at the expense of more memory. Warning: if you use the `-malign-double' switch, structures containing the above types will be aligned differently than the published application binary interface specifications for the 386.

это оно?

asaw ★★★★★
()
Ответ на: комментарий от asaw

-malign-double -mno-align-double
это оно?

Если бы это было оно, то скомпилировав код на C++ с флагом -mno-align-double, я бы получил вывод 4, а этого не происходит, вывод всё равно 8.

Однако если скомпилировать код на чистом C с флагом -malign-double, то он действительно выводит 8 вместо 4.

proud_anon ★★★★★
() автор топика
Последнее исправление: proud_anon (всего исправлений: 1)
Ответ на: комментарий от anonymous

Я внимательно прочитал весь раздел 2.1.1, но ответа на свой вопрос не нашёл. Там написано, что выравнивание double — 4 байта. gcc так и делает. g++ выдаёт 8. Почему?

proud_anon ★★★★★
() автор топика
Ответ на: комментарий от anonymous

вообще вырванивание для double - 4 байта, но компиляторам рекомендуется предоставлять опцию для выравнивания в 8 байт. видимо у g++ эта опция включена по умолчанию

anonymous
()
Ответ на: комментарий от anonymous

вообще вырванивание для double - 4 байта, но компиляторам рекомендуется предоставлять опцию для выравнивания в 8 байт. видимо у g++ эта опция включена по умолчанию

В чём разница между alignof в C и в C++? (комментарий)

Если она и включена по умолчанию, то таким образом, что выключить нельзя вообще.

Поэтому я предполагаю, что тут какая-то загвоздка в стандарте C++, а не странное поведение g++. Потому что это уже на грани бага, особенно если учесть, что структура, содержащая double, всё равно имеет выравнивание 4, даже в g++.

proud_anon ★★★★★
() автор топика
Ответ на: комментарий от proud_anon

Потому что:

Warning: if you use the `-malign-double' switch, structures containing the above types will be aligned differently than the published application binary interface specifications for the 386.

asaw ★★★★★
()

Нашёл небольшую статейку: http://www.wambold.com/Martin/writings/alignof.html. Процитирую кусок:

Amazing facts about alignment

On modern x86 processors, the type double is most efficiently aligned at multiples of 8. And in fact, gcc aligns «free» doubles at multiples of 8 within stack frames. Unfortunately, ancient ABIs require that the alignment of a double within a struct is 4. The unexpected consequence is that, on x86 Linux, gcc has

struct Double { double d; }; __alignof__ (double) == 8 __alignof__ (Double) == 4;

The same ancient ABIs specify that the size of long double is 12. Because the alignment must be a factor of the size, we have the curious situation that:

__alignof__ (double) == 8 __alignof__ (long double) == 4;

even though long double «wants» to be at least as aligned as double.

All of our alignof implementations have embedded types within structs. Is there any way to detect the difference in alignment between types like double and Double at compile time? I don't know of any way. Fortunately, it doesn't matter very much, because we never care about the alignment of types that aren't struct members.

pavlick ★★
()
Ответ на: комментарий от pavlick

Is there any way to detect the difference in alignment between types like double and Double at compile time? I don't know of any way.

Конечно есть, static_assert называется.

asaw ★★★★★
()
Последнее исправление: asaw (всего исправлений: 1)
Ответ на: комментарий от asaw

Можно в сишечке и на макросах наваять. Достаточно, чтобы невалидный код пытался создать массив с -1 элементом. Как-то так:

char is_crap_abi[__alignof__ (double) == 8 ? 0 : -1];
То есть, можно даже что-то такое сделать
define static_assert(expression, text) \
do { \
    char *check[expression ? 0 : -1]; \
} while (0)

Gorthauer ★★★★★
()
Последнее исправление: Gorthauer (всего исправлений: 1)
Ответ на: комментарий от pavlick

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52023
Fixed for 4.9. The semantics of __alignof__ and C++11 alignof remain, deliberately, the alignment used outside structures.

Спасибо, это ты как раз нашёл то, что нужно. Теперь становится понятно, что у этих операторов в C и C++ разная семантика, по крайней мере в GCC. В C мы получаем минимальное необходимое выравнивание, зависящее от платформы. В C++ мы получаем предпочтительное выравнивание, то же, что выдаёт встроенный в GCC оператор __alignof__ и которое, в частности, не действует внутри структур.

Например:

#include <stdio.h>
#include <stdalign.h>

int main() {
        printf("alignof(double) == %u\n", alignof(double));
        printf("__alignof__(double) == %u\n", __alignof__(double));
}

% gcc -march=i686 -m32 -o test-c test-c.c
% ./test-c
alignof(double) == 4
__alignof__(double) == 8

proud_anon ★★★★★
() автор топика
Последнее исправление: proud_anon (всего исправлений: 1)
Ответ на: комментарий от pavlick

наоборот: $? == 0, у меня выдаёт 1.

Так и должно быть начиная с GCC 4.9.

proud_anon ★★★★★
() автор топика

А какая разница какое выравнивание?
Оно внутри программы, а если нужны какие то совместимости то pragma pack или сериализация/десериализация но уже другими способами.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.