Правила программирования на С и С++. Главы 1-6 - Не делайте предположений о размерах
ОГЛАВЛЕНИЕ
Страница 76 из 93
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 _UNICODEtypedef 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) ;