C вопросом: почему символ на самом деле занимает 4 байта в памяти? - PullRequest
4 голосов
/ 01 марта 2011

Я написал небольшую программу, чтобы проверить, сколько байтов char занимает в моей памяти, и показывает, что char фактически занимает 4 байта в памяти. Я понимаю, что это в основном из-за выравнивания слов и не вижу преимущества в том, что символ только 1 байт. Почему бы не использовать 4 байта для символа?

int main(void)
{
  int a;
  char b;
  int c;
  a = 0;
  b = 'b';
  c = 1;
  printf("%p\n",&a);
  printf("%p\n",&b);
  printf("%p\n",&c);
  return 0;
}

Выход: 0x7fff91a15c58 0x7fff91a15c5f 0x7fff91a15c54

Обновление: Я не верю, что malloc выделит только 1 байт для char, хотя sizeof (char) передается в качестве аргумента, потому что malloc содержит заголовок, который гарантирует, что заголовок выровнен по слову. Есть комментарии?

Update2: Если вас просят эффективно использовать память без заполнения, единственный ли способ - создать специальный распределитель памяти? или можно отключить заполнение?

Ответы [ 6 ]

6 голосов
/ 01 марта 2011

Выравнивание

Давайте посмотрим на ваш вывод для печати адресов a, b и c:

Вывод: 0x7fff91a15c58 0x7fff91a15c5f 0x7fff91a15c54

Заметьте, что b не находится на той же 4-байтовой границе?А что а и с рядом друг с другом?Вот как это выглядит в памяти: каждая строка занимает 4 байта, а самый правый столбец занимает 0-е место:

| b | x | x | x | 0x5c5c
-----------------
| a | a | a | a | 0x5c58 
-----------------
| c | c | c | c | 0x5c54 

Это способ компиляции оптимизировать пространство и поддерживать выравнивание слов.Даже если ваш адрес b равен 0x5c5f, на самом деле он не занимает 4 байта.Если вы возьмете тот же код и добавите короткий d, вы увидите это:

| b | x | d | d | 0x5c5c
-----------------
| a | a | a | a | 0x5c58 
-----------------
| c | c | c | c | 0x5c54 

Где адрес d равен 0x5c5c.Шорты будут выровнены на два байта, поэтому у вас останется один байт неиспользуемой памяти между c и d.Добавьте еще один символ, и вы получите:

| b | e | d | d | 0x5c5c
-----------------
| a | a | a | a | 0x5c58 
-----------------
| c | c | c | c | 0x5c54 

Вот мой код и вывод.Обратите внимание, что мои адреса будут немного отличаться, но это наименее значимая цифра в адресе, которая нас действительно беспокоит:

int main(void)
{
  int a;
  char b;
  int c;
  short d;
  char e;
  a = 0;
  b = 'b';
  c = 1;
  printf("%p\n",&a);
  printf("%p\n",&b);
  printf("%p\n",&c);
  printf("%p\n",&d);
  printf("%p\n",&e);
  return 0;
}

$ ./a.out 
0xbfa0bde8
0xbfa0bdef
0xbfa0bde4
0xbfa0bdec
0xbfa0bdee

Malloc

На странице руководства malloc сказано, чтоон " выделяет размер байтов и возвращает указатель на выделенную память ."В нем также говорится, что он « вернет указатель на выделенную память, который соответствующим образом выровнен для любой переменной ».Из моего тестирования повторные вызовы malloc (1) возвращают адреса с приращением «двойного слова», но я бы не стал рассчитывать на это.

Предостережения

Мой код был запущен на x8632-битная машина.Другие машины могут немного отличаться, и некоторые компиляторы могут оптимизировать по-разному, но идеи должны быть верными.

6 голосов
/ 01 марта 2011

У вас есть int, char, int

См. Изображение здесь под заголовком "Зачем ограничивать выравнивание байтов?"http://www.eventhelix.com/realtimemantra/ByteAlignmentAndOrdering.htm

          Byte 0 Byte 1 Byte 2  Byte 3
0x1000               
0x1004  X0     X1     X2      X3
0x1008               
0x100C         Y0     Y1      Y2

Если бы он хранил их в 4-байтовой, 1-байтовой и 4-байтовой форме, потребовалось бы 2 такта процессора для получения int c и некоторый сдвиг битов для полученияфактическое значение c выровнено правильно для использования в качестве целого.

5 голосов
/ 01 марта 2011

Сама переменная не занимает 4 байта памяти, она занимает 1 байт, а затем за ней следуют 3 байта заполнения, поскольку следующая переменная в стеке - это int, и поэтому должна быть выровнена по слову.

В случае, подобном приведенному ниже, вы обнаружите, что адрес переменной anotherChar на 1 байт больше, чем у b. Затем они сопровождаются 2 байтами заполнения перед int c

int main(void)
{
  int a;
  char b;
  char anotherChar;
  int c;
  a = 0;
  b = 'b';
  c = 1;
  printf("%p\n",&a);
  printf("%p\n",&b);
  printf("%p\n",&anotherChar);
  printf("%p\n",&c);
  return 0;
}
2 голосов
/ 01 марта 2011

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

1 голос
/ 01 марта 2011

Чтобы ответить на последнюю часть вашего вопроса: почему бы не использовать 4 байта для символа?

Почему бы не использовать 4 миллиона байтов для char[1000000]?

0 голосов
/ 01 марта 2011

Это связано с ограничениями выравнивания.Размер символа составляет всего 1 байт, однако целое число выравнивается по кратному 4 байтам.За символом также могут следовать другие символы (или, скажем, короткие), которые могут иметь более мягкие ограничения выравнивания.В этих случаях, если размер символа был действительно 4 байта, как вы предлагаете, мы будем использовать больше места, чем необходимо.

...