Правила программирования на Си и Си++

       

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


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

double  a[1000], *p = a;

// ...

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

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

g()

{

    doesnt_work( 0 );

}

doesnt_work( 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. Microsoft и другие поставщики компиляторов стараются помочь вам писать переносимые программы, предусматривая макросы типа:

#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);

Содержание раздела