размер
sizeof — унарный оператор в языках программирования C и C++ . Он генерирует размер хранилища выражения или типа данных , измеряемый в количестве единиц измерения размера char . Следовательно, конструкция sizeof (char) гарантированно равна 1 . Фактическое количество бит типа char указывается макросом препроцессора. CHAR_BIT , определенный в стандартном включаемом файле limit.h . На большинстве современных вычислительных платформ это восемь бит. Результат sizeof имеет беззнаковый целочисленный тип, который обычно обозначается size_t .
Оператор имеет единственный операнд, который является либо выражением, либо приведением типа данных, заключенного в круглые скобки. Типы данных могут быть не только примитивными типами , такими как целочисленные типы и типы с плавающей запятой , но также типами указателей и составными типами данных ( объединениями , структурами и классами C++ ).
Цель
[ редактировать ]Многие программы должны знать размер хранилища определенного типа данных. Хотя для любой конкретной реализации C или C++ размер определенного типа данных является постоянным, размеры даже примитивных типов в C и C++ могут определяться по-разному для разных платформ реализации. Например, при выделении пространства массива во время выполнения можно использовать следующий код, в котором оператор sizeof применяется к приведению типа int :
int *pointer = malloc(10 * sizeof (int));
В этом примере функция malloc выделяет память и возвращает указатель на блок памяти. Размер выделенного блока равен количеству байтов для одного объекта типа int , умноженному на 10, что обеспечивает место для десяти целых чисел.
Обычно небезопасно предполагать размер любого типа данных. Например, хотя большинство реализаций C и C++ в 32-битных системах определяют тип int как четыре октета, этот размер может измениться при переносе кода в другую систему, что приведет к нарушению кода. Исключением является тип данных char , который всегда имеет размер 1 в любой реализации C, соответствующей стандартам. Кроме того, часто бывает трудно предсказать размеры составных типов данных, таких как struct или Union , из-за заполнения. Использование sizeof повышает читабельность, поскольку позволяет избежать безымянных числовых констант ( магических чисел ).
Эквивалентный синтаксис для выделения того же пространства массива получается в результате использования разыменованной формы указателя на адрес хранилища, на этот раз применяя оператор к переменной-указателю:
int *pointer = malloc(10 * sizeof *pointer);
Использовать
[ редактировать ]Оператор sizeof создает необходимое пространство памяти для своего операнда при компиляции кода. Операнд записывается после ключевого слова sizeof и может быть символом пространства хранения, например, переменной, выражением или именем типа. Круглые скобки для операнда необязательны, за исключением случаев указания имени типа. Результатом операции является размер операнда в байтах или размер требуемой памяти. Для выражений он оценивает размер представления для типа, который возникнет в результате вычисления выражения, которое не выполняется.
Например, поскольку sizeof (char) определен как 1 [1] и предполагая, что целочисленный тип имеет длину четыре байта, печатается следующий фрагмент кода 1,4 :
char c;
printf ("%zu,%zu\n", sizeof c, sizeof (int));
Некоторые стандартные файлы заголовков, такие как stddef.h , определяют size_t для обозначения целочисленного типа без знака результата выражения sizeof . Спецификатор printf ширины z предназначен для форматирования этого типа.
sizeof нельзя использовать в выражениях препроцессора C , таких как #if , потому что это элемент языка программирования, а не синтаксиса препроцессора, который не имеет типов данных.
В следующем примере на C++ оператор sizeof используется с шаблонами с переменным числом аргументов.
template <typename... Args>
std::size_t GetSize (Args&&... args)
{
/* Get size of parameter pack.*/
std::size_t Count = sizeof... (Args);
return Count;
}
sizeof можно использовать с шаблонами с переменным числом аргументов в C++11 и более поздних версиях в пакете параметров для определения количества аргументов.
Приложение к массивам
[ редактировать ]Когда sizeof применяется к имени массива, результатом является количество байтов, необходимое для хранения всего массива. Это одно из немногих исключений из правила, согласно которому имя массива преобразуется в указатель на первый элемент массива, и возможно только потому, что фактический размер массива фиксирован и известен во время компиляции, когда sizeof оператор оценивается. Следующая программа использует sizeof для определения размера объявленного массива, избегая переполнения буфера при копировании символов:
#include <stdio.h>
int main(int argc, char **argv)
{
char buffer[10]; /* Array of 10 chars */
/* Copy at most 9 characters from argv[1] into buffer,
* null-terminate the buffer. */
snprintf(buffer, sizeof buffer, "%s", argv[1]);
return 0;
}
Здесь, sizeof буфер эквивалентен 10 * sizeof buffer [0] , что равно 10, поскольку размер типа char определен как 1.
C99 добавляет в структуры поддержку гибких членов массива. Эта форма объявления массива разрешена только в качестве последнего элемента в структурах и отличается от обычных массивов тем, что компилятору не указывается длина. Для структуры с именем s, содержащей гибкий элемент массива с именем a , sizeof s поэтому эквивалентен offsetof (s, a) :
#include <stdio.h>
struct flexarray {
char val;
int array[]; /* Flexible array member; must be last element of struct */
};
int main(int argc, char **argv)
{
printf("sizeof (struct flexarray) == %zu\n", sizeof (struct flexarray));
return 0;
}
В этом случае оператор sizeof возвращает размер структуры, включая любые дополнения, но без какого-либо места для хранения, разрешенного для массива. Большинство платформ выдают следующий результат:
- sizeof (struct flexarray) == 4
C99 также позволяет использовать массивы переменной длины, длина которых указана во время выполнения. [2] хотя эта функция считается необязательной реализацией в более поздних версиях стандарта C. В таких случаях оператор sizeof частично оценивается во время выполнения, чтобы определить объем памяти, занимаемый массивом.
#include <stddef.h>
size_t flexsize(int n)
{
char b[n + 3]; /* Variable length array */
return sizeof b; /* Execution time sizeof */
}
int main(void)
{
size_t size = flexsize(10); /* flexsize returns 13 */
return 0;
}
sizeof можно использовать для определения количества элементов в массиве путем деления размера всего массива на размер одного элемента. Это следует использовать с осторожностью; При передаче массива в другую функцию он «распадется» до типа указателя. На этом этапе sizeof вернет размер указателя, а не общий размер массива. В качестве примера с правильным массивом:
int main (void)
{
int tab [10];
printf ("Number of elements in the array: %zu\n", sizeof tab / sizeof tab [0]); /* yields 10 */
return 0;
}
Неполные типы
[ редактировать ]sizeof может применяться только к «полностью» определенным типам. В случае с массивами это означает, что размеры массива должны присутствовать в его объявлении и что тип элементов должен быть полностью определен. Для struct и Union это означает, что должен существовать список членов полностью определенных типов. Например, рассмотрим следующие два исходных файла:
/* file1.c */
int arr [10];
struct x {int one; int two;};
/* more code */
/* file2.c */
extern int arr [];
struct x;
/* more code */
Оба файла совершенно легальны на языке C, и код на них file1.c может применять sizeof к arr и структура х . Однако это незаконно для кода в file2.c для этого, поскольку определения в file2.c не завершены. В случае arr код не указывает размерность массива; без этой информации компилятор не сможет узнать, сколько элементов находится в массиве, и не сможет вычислить общий размер массива. Аналогично, компилятор не может вычислить размер struct x, поскольку она не знает, из каких элементов она состоит, и поэтому не может вычислить сумму размеров членов структуры (и заполнения). Если программист указал размер массива в своем объявлении в file2.c или завершил определение struct x, предоставив список членов, это позволит применить sizeof к arr или struct x в этом исходном файле.
Члены объекта
[ редактировать ]В C++11 появилась возможность применять параметр sizeof к конкретным членам класса без необходимости создавать для этого экземпляр объекта. [3] Следующий пример, например, дает 4 и 8 на большинстве платформ.
#include <iostream>
struct foo {
int a;
int b;
};
int main ()
{
std::cout << sizeof foo::a << "\n" << sizeof (foo) << "\n";
}
Пакеты шаблонов Variadic
[ редактировать ]В C++11 представлены шаблоны с переменным числом вариантов ; ключевое слово sizeof, за которым следует многоточие, возвращает количество элементов в пакете параметров.
template <typename... Args>
void print_size (Args... args)
{
std::cout << sizeof... (args) << "\n";
}
int main ()
{
print_size (); // outputs 0
print_size ("Is the answer", 42, true); // outputs 3
}
Выполнение
[ редактировать ]При применении к типу данных или переменной фиксированной длины выражения с оператором sizeof оцениваются во время компиляции программы; они заменяются постоянными значениями результата. Стандарт C99 представил массивы переменной длины (VLA), которые требовали оценки таких выражений во время выполнения программы. Во многих случаях особенности реализации могут быть задокументированы в документе двоичного интерфейса приложения (ABI) для платформы, определяющем форматы, заполнение и выравнивание для типов данных, которым должен соответствовать компилятор.
Заполнение структуры
[ редактировать ]При вычислении размера любого типа объекта компилятор должен учитывать любое необходимое выравнивание структуры данных для обеспечения эффективности или архитектурных ограничений. Многие компьютерные архитектуры не поддерживают многобайтовый доступ, начиная с любого байтового адреса, не кратного размеру слова, и даже если архитектура это позволяет, обычно процессор может получить объект, выровненный по слову, быстрее, чем он может получить объект. который охватывает несколько слов в памяти. [4] Поэтому компиляторы обычно выравнивают структуры данных по крайней мере по границе слова , а также выравнивают отдельные члены по соответствующим границам. В следующем примере структура «студент», скорее всего, будет выровнена по границе слова, где также начинается оценка участника, участника, а возраст скорее всего, начнется с адреса следующего слова. Компилятор выполняет последнее, вставляя байты заполнения между элементами по мере необходимости для удовлетворения требований выравнивания. В конце структуры также может быть дополнение для обеспечения правильного выравнивания в случае, если структура используется как элемент массива.
Таким образом, совокупный размер структуры в C может быть больше суммы размеров ее отдельных членов. Например, во многих системах печатается следующий код: 8 :
struct student {
char grade; /* char is 1 byte long */
int age; /* int is 4 bytes long */
};
printf ("%zu", sizeof (struct student));
См. также
[ редактировать ]Ссылки
[ редактировать ]- ^ «Стандарт C99 (ISO/IEC9899)» (PDF) . ИСО/МЭК . 7 сентября 2007 г. 6.5.3.4.3, с. 80 . Проверено 31 октября 2010 г.
- ^ «Проект ISO/IEC 9899 комитета WG14/N1124» (PDF) . 6 мая 2005 г. 6 мая 2005 г. 6.5.3.4 Оператор sizeof .
- ^ «N2253 Расширение sizeof для применения к нестатическим элементам данных без объекта (версия 1)» .
- ^ Ренцш, Джонатан (8 февраля 2005 г.). «Выравнивание данных: выпрямляйтесь и летите вправо» . ИБМ . Проверено 29 сентября 2014 г.