Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
28
Добавлен:
17.04.2015
Размер:
78.15 Кб
Скачать

23. Роль операции sizeof в управлении памятью

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

ватьсядля корректного размещения данных различных типов в памяти.

Сказанное проиллюстрируем простым примером размещения переменных типа double в массиве типа char:

double *d;

#define N 40

char A[N];

for (i=0, d=A; i < N / sizeof(double); i++)

d[i] = (double)i;

Заметим, что использование операции sizeof позво-

ляет сделать программу переносимой, то есть нечувствительной

к разрядности представления данных в различных трансляторах.

24. Указатель типа void*.

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

Именно такими свойствами обладает указатель типа void* - указатель на пустой тип void. Наличие его в данном месте программы говорит о том, что она не имеет достаточных

оснований для работы с адресуемой областью памяти. Наиболее часто тип void* является формальным параметром или результатом функции. Приведем несколько примеров:

extern void *malloc(int);

int *p;

p = malloc(sizeof(int)*20);

...

p[i] = i;

Функция malloc возвращает адрес зарезервированной области динамической памяти в виде указателя void*. Это означает, что функцией выделяется память как таковая, безотноситель-но к размещаемым в ней переменным. Вызывающая функция неявно преобразует тип указателя void* в требуемый тип int* для работы с этой областью как с массивом целых переменных.

extern int fread(void *, int, int, FILE *);

int A[20];

...

fread(A, sizeof(int), 20, fd);

Функция fread выполняет чтение из двоичного файла n записей длиной по m байтов, при этом структура записи для функции неизвестна. Поэтому начальный адрес области памяти передается формальным параметром типа void*. При подстановке фактического параметра A типа int* производится неявное преобразование его к типу void*.

Как видно из примеров, преобразование типа указателя void* к любому другому типу указателя соответствует "смене точки зрения" программы на адресуемую память от "данные вообще" к "конкретные данные" и наоборот.

Операция приведения типа чаще всего используется для преобразования указателей. Например, стандартная функция захвата динамической памяти malloc возвращает указатель общего типа void* (см. раздел 3.7.3). Значение указателя обобщенного типа нельзя присвоить указателю на конкретный тип (язык C++ запрещает такие присвоения, Си-компиляторы иногда разрешают преобразования указателей по умолчанию, выдавая предупреждения, - но в любом случае это дурной стиль!). Для преобразования указателей разного типа нужно использовать операцию приведения типа в явном виде. В следующем примере в динамической памяти захватывается участок размером в 400 байт, его адрес присваивается указателю на массив из 100 целых чисел:

int *a; // Описываем указатель на массив типа int

. . .

// Захватываем участок памяти размером в 400 байт

// (поскольку sizeof(int) == 4), приводим указатель

// на него от типа void* к типу int* и присваиваем

// приведенное значение указателю a:

a = (int*) malloc(100 * sizeof(int));

Отметим, что допустимо неявное преобразование любого указателя к указателю обобщенного типа void*. Обратное, как указано выше, считается грубой ошибкой в C++ и дурным стилем (возможно, сопровождаемым предупреждением компилятора) в Си:

int *a;       // Указатель на целое число

void *p;      // Указатель обобщенного типа

. . .

a = p;        // Ошибка! В C++ запрещено неявное

              // приведение типа от void* к int*

a = (int*) p; // Корректно: явное приведение типа

 

p = a;  // Корректно: любой указатель можно

        // неявно привести к обобщенному