|
Программирование >> Решение нетривиальных задач
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); 70. Опасайтесь приведения типов (спорные вопросы Си) Оператор приведения типов часто понимается неправильно. Приведение типов не указывает компилятору считать эту переменную принадлежащей к этому типу . Оно должно рассматриваться как операция времени выполнения, которая создает временную переменную типа, определенного для приведения, затем инициализирует эту временную переменную от операнда. В Си++, конечно, эта инициализация может обернуться очень большими накладными расходами, так как возможен вызов конструктора. Первое место, где неверное понимание приведения типов может навлечь на вас неприятности, находится в Си, где не требуются прототипы функций. Когда компилятор находит вызов функции без предшествующего прототипа, то полагает, что эта функция возвращает тип int. В следующем фрагменте не говорится malloc() на самом деле возвращает указатель, а не тип int : int *p = (int *) malloc( sizeof( int) ); а, скорее, код говорит я полагаю, что malloc() возвращает тип int, так как тут нет предшествующего прототипа, и преобразую этот int в указатель для присваивания его значения p . Если тип int имеет размер 16 бит, а указатель 32-битовый, то вы теперь в глубокой луже. Вызов malloc() может вернуть и 32-битовый указатель, но так как компилятор полагает, что malloc() возвращает 16-битовый int, то он игнорирует остальные 16 бит. Затем компилятор обрезает возвращенное значение до 16-бит и преобразует его в 32-битовый тип int принятым у него способом, обычно заполняя старшие 16 бит нулями. Если указатель содержал адрес больше, чем 0xffff, что вероятно для большинства компьютеров, то вы просто теряете старшие биты. Единственным способом урегулирования этой проблемы является указание для malloc() соответствующего прототипа, который подскажет, что malloc() возвращает указатель (обычно путем включения файла <stdlib.h>). Следующий проблема состоит в том, что предыдущее приведение типа может быть вставлено в программу просто для того, чтобы заставить заткнуться компилятор, который наверняка будет выдавать предупреждение о несоответствии типов, если оператор приведения отсутствует. Приведением типов часто злоупотребляют подобным образом - чтобы заглушить компилятор, вместо того, чтобы в самом деле обратить внимание на предупреждение. Многие компиляторы, например, выдают предупреждение о возможном округлении, встретив следующий код: f( int x ); ... unsigned y; f( y ); и многие программисты заглушат такой компилятор при помощи f(( int)y) . Несмотря на это, приведение типа не изменит того факта, что тип unsigned int может содержать такое значение, которое не поместится в int со знаком, поэтому результирующий вызов может не сработать. Вот сходная проблема, связанная с указателями на функции. Следующий код, случается, работает отлично: some object array[ size ]; int my cmp( some object *p1, some object *p2 ); qsort( array, size, sizeof(some object),( (*)( void*, void*)) my cmp );
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |