Понимание sizeof (char) в 32-битных компиляторах C - PullRequest
11 голосов
/ 10 августа 2010

(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, который, похоже, сделал поспешные выводы из моего понимания концепций !!

Ответы [ 8 ]

23 голосов
/ 10 августа 2010

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

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

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

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

8 голосов
/ 10 августа 2010

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

6 голосов
/ 10 августа 2010

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

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

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

3 голосов
/ 10 августа 2010

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

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

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

2 голосов
/ 13 августа 2010

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

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

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

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

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

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

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

struct st 
{
int a;
char c;
};

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

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

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

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

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

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

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

1 голос
/ 10 августа 2010

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

1 голос
/ 10 августа 2010

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

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

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

0 голосов
/ 13 августа 2010

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

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

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

...