2 вопроса о нижеприведенном коде:
1 Почему размер struct b
12 байтов (строка 2 вывода)? Я могу понять, почему 12 байт (для выравнивания a
k
на границе 4 байта), однако не должно b
быть 9 байт, а не 12?
2 Почему используется оператор &
для получения адреса членов char
не отображающего действительный адрес? (средний вывод в последних двух строках)
#include<iostream>
using namespace std;
struct a
{
int i;
char j;
int k;
};
struct b
{
int i;
int k;
char j;
};
int main()
{
a s1;
b s2;
cout<<sizeof(a)<<endl;
cout<<sizeof(b)<<endl;
cout<<sizeof(int)<<endl;
cout<<sizeof(char)<<endl;
cout<<&s1.i<<'\t'<<&s1.j<<'\t'<<&s1.k<<endl;
cout<<&s2.i<<'\t'<<&s2.j<<'\t'<<&s2.k<<endl;
}
Вывод:
12
12
4
1
0x28ff14 - 0x28ff1c
0x28ff08 4A 0x28ff0c
Почему размер struct b 12 байтов (строка 2 вывода)?
Он имеет int
поля и поэтому имеет такое же требование выравнивания, как int
four bytes, в вашем случае. В конце добавляются три байта заполнения, поэтому размер равен кратным четырем, чтобы обеспечить требуемое выравнивание.
Это применяется даже в конце структуры, так что следующий элемент массива правильно выровнен.
Почему используется оператор
&
для получения адреса членов char, не отображающего действительный адрес?
Поскольку <<
интерпретирует указатель на тип символа как указатель на строку стиля C и печатает содержимое памяти, пока не найдет нулевое значение. Чтобы напечатать адрес, введите его в несимвольный указатель:
cout << (void*)&j;
Если b
было 9 байтов, и у вас был массив
b foo[2];
то foo[1].i
не будет выровнена с четырьмя байтовыми границами.
Что касается членов char, их адрес имеет тип char *
, а std::ostream
имеет operator<<
для тех, которые интерпретируют их как строку C-стиля. Если вы хотите увидеть их адрес, static_cast<void*>(&s1.j)
- ваш друг.
EDIT: Полное раскрытие яйца на моем лице: это изначально утверждало, что некоторые компиляторы определили минимальные размеры выравнивания структуры; при расследовании это оказывается неверным. Ну, это может быть правдой, так как размер типов структур не указан в стандарте до такой степени, но обычные компиляторы, похоже, не делают этого. Извини за это.
char
-char *
. Задумайтесь об этом на мгновение. Возможно, существуетoperator <<
перегрузкиoperator <<
для потоков, который записывает такие указатели в виде строк с нулевым символом в конце, а не адресных значений. Хммм ....char*
рассматривается как C_String.