Правила программирования на С и С++. Главы 1-6
Страница 76. Не делайте предположений о размерах


 

69. Не делайте предположений о размерах.

Классической проблемой является код, исходящий из того, что тип int имеет размер 32 бита. Следующий фрагмент не работает, если у вас 32-битный указатель и 16-битный тип int (что может быть при архитектуре Intel 80x86):

double a[1000], *p = a;

// ...

dist_from_start_of_array_in_bytes = (int)p - (int)a;

расстояние_от_начала_массива_в_байтах = (int)p - (int)a;

Более трудно уловима такая проблема в С (но не в С++): g()

{

не_работает( 0 );

}

не_работает( char *p )

{

if( !p ) // вероятно не работает

// ...}Компилятор соглашается с этим вызовом, потому что в С разрешены ссылки вперед (и не разрешены в С++, так что там это не проблема). 0 это тип int, поэтому в стек помещается 16-битовый объект. Но функция ожидает 32-битный указатель, поэтому она использует 16 бит из стека и добавляет к ним еще 16 бит всякого мусора для создания 32-битного указателя. Вероятнее всего, что if( !p ) даст ложный результат, так как только 16 бит из 32 будут равны 0.

Традиционное решение состоит в использовании typedef :

typedef int word; // всегда 16 бит

typedef long dword; // всегда 32 бита.

После чего вы можете поменять операторы typedef в новой операционной среде, чтобы гарантировать, что word по прежнему имеет размер 16 бит, а dword - 32 бита. Для 32-разрядной системы предыдущее может быть переопределено как: typedef short word; // всегда 16 бит

typedef int dword; // всегда 32 бита.

Другая связанная с размерностью часовая бомба спрятана в том способе, которым в ANSI С обеспечивается работа с иностранными языками. ANSI С определяет тип wchar_t для работы с расширенными наборами символов типа Unicode - нового 16-битного многонационального набора символов. Стандарт ANSI С также утверждает, что перед строкой с расширенными символами должен стоять символ L. Micrisoft и другие поставщики компиляторов стараются помочь вам писать переносимые программы, предусматривая макросы типа: #ifdef _UNICODE

typedef wchar_t _TCHAR

# define _T(x) L##x

#else

typedef char _TCHAR

# define _T(x) x

#endif

Если константа _UNICODE не определена, то оператор: _TCHAR *p = _T("делай_что_нужно");имеет значение: char *p = "делай_что_нужно";Если константа _UNICODE определена, тот же самый оператор получает значение: wchar_t *p = L"делай_что_нужно";Пока все хорошо. Вы теперь можете попробовать перенести вашу старую программу в среду Unicode, просто используя свой редактор для замены всех экземпляров char на _TCHAR и помещения всех строковых констант в скобки _T(). Проблема состоит в том, что такой код, как ниже (в котором все _TCHAR первоначально были типа char), более не работает: _TCHAR str[4];

// ...

int max_chars = sizeof(str); // предполагает, что тип char имеет размер 1 байт

Тип _TCHAR будет иметь размер 2 байта при определенной константе _UNICODE, поэтому число символов у вас будет определено в два раза большим, чем есть на самом деле. Для исправления ситуации вы должны воспользоваться следующим вариантом: int max_chars = sizeof(str) / sizeof(*str) ;

 

 
« Предыдущая статья