Что такое sizeof (char) в 32-битных компиляторах C

(sizeof) char всегда возвращает 1 в 32-битном компиляторе GCC.

Но поскольку размер базового блока в 32-битном компиляторе равен 4, как char занимает один байт, если базовый размер составляет 4 байта ???

Учитывая следующее:

struct st 
{
int a;
char c;
};

sizeof (st) возвращается как 8 в соответствии с размером блока по умолчанию 4 байта (поскольку выделено 2 блока)

Я никогда не могу понять, почему sizeof (char) возвращается как 1 , когда ему выделяется блок размера 4.

Может кто-нибудь объяснить это ???

Я был бы очень благодарен за любые ответы, объясняющие это !!!

РЕДАКТИРОВАТЬ: Опечатка «бит» была изменена на «байты». Прошу извинить человека, сделавшего первую правку. Я откатил РЕДАКТИРОВАТЬ, так как не заметил изменения U. Спасибо всем, кто указал, что это должно быть изменено, особенно @Mike Burton за то, что он проголосовал против этого вопроса, и @jalf, который, казалось, поспешил с выводами по поводу моего понимания концепций !!

11
задан sbi 13 November 2011 в 20:47
поделиться

8 ответов

Прежде всего, sizeof возвращает число байтов , а не биты. sizeof (char) == 1 сообщает вам, что char имеет длину восемь бит (один байт). Все основные типы данных в C имеют длину не менее одного байта.

Ваша структура возвращает размер 8. Это сумма трех вещей: размера int , размера char (который, как мы знаем, равен 1 ), а также размер любого дополнительного заполнения, которое компилятор добавил в структуру.Поскольку во многих реализациях используется 4-байтовый int , это будет означать, что ваш компилятор добавляет 3 байта заполнения в вашу структуру.Скорее всего, это добавлено после char , чтобы сделать размер структуры кратным 4 (32-битные данные доступа ЦП наиболее эффективны в 32-битных порциях, а 32 бита - это четыре байта) .

Изменить: Тот факт, что размер блока составляет четыре байта, не означает, что тип данных не может быть меньше четырех байтов. Когда ЦП загружает однобайтовый char в 32-битный регистр, значение будет автоматически расширено по знаку (аппаратно), чтобы оно заполнило регистр. ЦП достаточно умен, чтобы обрабатывать данные с шагом в N байтов (где N - степень двойки), если он не превышает размер регистра. При хранении данных на диске или в памяти нет причин хранить каждый char как четыре байта. char в вашей структуре выглядел так, как будто он был длиной в четыре байта из-за добавленного после него дополнения. Если вы изменили свою структуру, чтобы иметь две char переменных вместо одной, вы должны увидеть, что размер структуры такой же (вы добавили дополнительный байт данных, а компилятор добавил на один меньше байт заполнения).

3
ответ дан 3 December 2019 в 01:12
поделиться

sizeof (char) всегда 1. Всегда. «Размер блока», о котором вы говорите, - это просто собственный размер слова машины - обычно размер, который обеспечивает наиболее эффективную работу. Ваш компьютер по-прежнему может адресовать каждый байт индивидуально - это то, о чем вам говорит оператор sizeof . Когда вы выполняете sizeof (int) , он возвращает 4, чтобы сообщить вам, что int составляет 4 байта на вашем компьютере. Точно так же ваша структура имеет длину 8 байтов. Нет информации из sizeof о том, сколько битов находится в байте.

Причина, по которой ваша структура имеет длину 8 байт, а не 5 (как вы могли ожидать), заключается в том, что компилятор добавляет отступ в структуру, чтобы все было хорошо выровнено по этой исходной длине слова, снова для большей эффективности. Большинство компиляторов дают вам возможность упаковать структуру с помощью директивы #pragma или другого расширения компилятора, и в этом случае вы можете заставить структуру принимать минимальный размер, независимо от длина слова вашей машины.

char имеет размер 1, так как это наименьший размер доступа, который может обработать ваш компьютер - для большинства машин это 8-битное значение. Оператор sizeof дает вам размер всех других величин в единицах того, сколько объектов char будут того же размера, что и все, о чем вы спрашивали. Заполнение (см. Ссылку ниже) добавляется компилятором в вашу структуру данных из соображений производительности, поэтому на практике оно больше, чем вы можете подумать, просто взглянув на определение структуры.

В википедии есть статья под названием Выравнивание структуры данных , в которой есть хорошее объяснение и примеры.

23
ответ дан 3 December 2019 в 01:12
поделиться

Это выравнивание структуры. c использует 1 байт, 3 байта не используются. Подробнее здесь (с картинками!)

8
ответ дан 3 December 2019 в 01:12
поделиться

Sizeof возвращает значение в байтах.Вы говорили о битах. 32-битные архитектуры выравниваются по словам и ссылаются на байты. Не имеет значения, как архитектура хранит символ, но для компилятора вы должны ссылаться на символы по 1 байту за раз, даже если они занимают менее 1 байта.

Вот почему sizeof (char) равно 1.

int - 32-битные, следовательно sizeof (int) = 4, double - 64-битные, следовательно sizeof (double) = 8 и т. Д.

1
ответ дан 3 December 2019 в 01:12
поделиться

Из-за оптимизации добавлено заполнение, поэтому размер объекта составляет 1, 2 или n * 4 байта (или что-то в этом роде, если говорить о x86). Вот почему добавляется дополнение к 5-байтовому объекту, а к 1-байтовому - нет. Одиночный char не требует заполнения, он может быть размещен на 1 байте, мы можем сохранить его в пространстве, выделенном с помощью malloc (1) . st не может быть сохранен в пространстве, выделенном с помощью malloc (5) , потому что при копировании структуры st копируются целые 8 байтов.

1
ответ дан 3 December 2019 в 01:12
поделиться

Все размеры объектов в C и C ++ определены в байтах , а не в битах . Байт - это наименьшая адресуемая единица памяти на компьютере. Бит - это одна двоичная цифра, 0 или 1 .

На большинстве компьютеров байт составляет 8 бит (поэтому в байте могут храниться значения от 0 до 256), хотя существуют компьютеры с другими размерами байтов.

Адрес памяти идентифицирует байт даже на 32-битных машинах. Адреса N и N + 1 указывают на два последующих байта.

int , который обычно составляет 32 бита, покрывает 4 байта, что означает, что существует 4 разных адреса памяти, каждый из которых указывает на часть int .

В 32-битной машине все 32 на самом деле означает, что ЦП разработан для эффективной работы с 32-битными значениями, а длина адреса - 32 бита. Это не означает, что к памяти можно обращаться только блоками по 32 бита.

ЦП по-прежнему может адресовать отдельные байты, что полезно, например, при работе с char s.

Что касается вашего примера:

struct st 
{
int a;
char c;
};

sizeof (st) возвращает 8 не потому, что все структуры имеют размер, кратный 4, а из-за выравнивания . Чтобы ЦП мог эффективно читать целое число, оно должно быть расположено по адресу, который делится на размер целого числа (4 байта). Таким образом, int можно разместить по адресу 8, 12 или 16, но не по адресу 11.

char требует, чтобы его адрес делился на размер char (1), поэтому его можно разместить на любом адресе.

Теоретически компилятор мог дать вашей структуре размер 5 байтов ... За исключением того, что это не сработает, если вы создадите массив из st объектов.

В массиве каждый объект помещается сразу после предыдущего, без заполнения. Таким образом, если первый объект в массиве размещен по адресу, кратному 4, то следующий объект будет размещен по адресу, большему на 5 байтов, который , а не будет делиться на 4, и поэтому вторая структура в массиве не будет правильно выровнен.

Чтобы решить эту проблему, компилятор вставляет заполнение внутри структуры, поэтому ее размер становится кратным требованию выравнивания.

Не потому, что невозможно создать объекты, размер которых не кратен 4, а потому, что для одного из членов вашей st структуры требуется 4-байтовое выравнивание, и поэтому каждый когда компилятор помещает int в память, он должен убедиться, что он размещен по адресу, который делится на 4.

Если вы создадите структуру из двух char s , он не получит размер 4. Обычно он будет иметь размер 2, потому что, когда он содержит только char s, объект может быть размещен по любому адресу, и поэтому выравнивание не является проблемой .

2
ответ дан 3 December 2019 в 01:12
поделиться

Пример кода, демонстрирующий выравнивание структуры:

struct st 
{
int a;
char c;
};

struct stb
{
int a;
char c;
char d;
char e;
char f;
};

struct stc
{
int a;
char c;
char d;
char e;
char f;
char g;
};

std::cout<<sizeof(st) << std::endl; //8
std::cout<<sizeof(stb)  << std::endl; //8
std::cout<<sizeof(stc)  << std::endl; //12

Размер структуры больше, чем сумма ее отдельных компонентов, поскольку он был задан делимым на 4 байта 32-битным компилятором. Эти результаты могут отличаться на разных компиляторах, особенно если они работают на 64-битном компиляторе.

6
ответ дан 3 December 2019 в 01:12
поделиться

Это работает так же, как использование половины листа бумаги. Вы используете одну часть для символа, а другую - для чего-то еще. Компилятор скроет это от вас, поскольку загрузка и сохранение символа в 32-битном регистре процессора зависит от процессора.

У некоторых процессоров есть инструкции для загрузки и сохранения только части 32-битных, другие должны использовать бинарные операции для извлечения значения char.

Обращение к char работает, поскольку это AFAIR по определению наименьшая адресуемая память. В 32-битной системе указатели на два разных целых числа будут разделены по крайней мере на 4 адресные точки, а адреса символов - только на 1.

0
ответ дан 3 December 2019 в 01:12
поделиться