Представитель Шуры Люберецкого в ЖЖ (brat_luber) wrote,
Представитель Шуры Люберецкого в ЖЖ
brat_luber

Нашел багу

Немного поковырял китайский U-Boot, адаптированный для процессоров Ingenic. Помните, я писал, что там некорректно отрабатывает утилита bmp_logo?

Так вот, дело – в типично китайском коде. Как вы думаете, что напечатает программа?

int main(void){
    int l;
    FILE *in;
/* skipped some code */
    fread(&l, sizeof(uint16_t), 1, in);
    printf("l = %d\n", l);
/* skipped some code */
    return 0;
}

Естественно, что на всех более-менее современных машинах sizeof(int) > sizeof(uint16_t), то есть переменная l окажется просто неопределена – проинициализируются только первые два байта. На little-endian системах программа будет “корректно” работать, если l “автоматически” инициализируется нулем.

Несмотря на то, что в стандарте C ничего не сказано про инициализацию нулем локальных переменных, иногда такое происходит. Когда ОС выделяет память для новой программы, назначенные для нее страницы обнуляются – чтобы никакие вирусы-трояны не искали там логины-пароли. Как бы не было больно знатокам стандарта языка C, main – далеко не та функция, с которой начинается выполнение программы. Сначала вызывается библиотечная функция _start или аналогичная ей по назначению, специфичная для каждого компилятора и инициализирующая необходимые для работы стандартной библиотеки вещи (подумайте, например, как будет работать без инициализации malloc()).

Так вот, в Linux-системах можно надеяться на то, что локальные для main переменные окажутся на еще нетронутой части стека. Происходящее в Windows – гораздо более неясно, но там локальные переменные main “инициализируются” чем-то непонятным.

Кто виноват – ясно. А что делать? Нет, не надо писать int l = 0;. Хочу лишний раз напомнить о существовании big-endian систем. В них после вызова fread() шестнадцатибитное значение запишется не в два младших, а в два старших байта – и нетрудно догадаться, что мы хотели несколько другого.

К сожалению, описанные у Кернигана и Ритчи типы int, short и long имеют нерегламентированную длину. Гарантируются какие-то минимальные значения, и ничего более. int может запросто оказаться двух-, четырех- и даже восьмибайтовым. И как тут жить? Керниган и Ритчи придумали один возможный подход. Например, во всех Unix-системах принято, что “время” – это 32-битовое целое без знака. В time.h с помощью typedef определяется тип time_t, который в реальности может быть int (на 32-битных машинах) или long (на 16-битных). На 64-битной экзотике он вполне может оказаться и short. Определено несколько таких “стандартных типов” – и все.

Естественно, если подходить таким образом, то очень скоро мы начнем определять типы наподобие bmp_file_data_length_t – целое, соответствующее тому типу, в который помещается “длина данных” из bmp-файла. Не очень весело, правда? В современных реализациях C, стандарте C99 и C++0x предусмотрен заголовочный файл stdint.h, в котором определяются целые типы фиксированного размера. Например, uint16_t – это 16-битное беззнаковое целое.

Хорошим тоном считается использовать именно тот тип, который нужен в конкретном случае, особенно, когда речь идет о полях структур или других данных, размер которых четко определен. При переносе на другую платформу не придется биться с тем, что int внезапно стал вдвое длиннее :)

В общем, если бы безвестный китаец знал про типы фиксированного размера, он не стал бы использовать четырехбайтный int для хранения 16-битного значения и все были бы счастливы.

PS А вот еще вопрос. Что произойдет, если l объявить не в теле функции main(), а вне нее, как глобальную для данного файла?

Запись опубликована в блоге Шуры Люберецкого. Вы можете оставлять свои комментарии там, используя свое имя пользователя из ЖЖ (вход по OpenID).

Tags: pavo, программирование
Subscribe

  • Таджикоанглийский

    Один ли я, читая Release Notes на этой картинке, вспоминаю Равшана и Джамшута? Запись опубликована в блоге Шуры Люберецкого. Вы можете…

  • Вдогонку истории про Кинопоиск

    Почитал комментарии к “перезапуску” Яндексом Кинопоиска. Что хочу сказать? Яндексовцы сделали просто офигенный и современный “сайт…

  • И еще вдогонку

    Вот обсуждают все пресловутый флешмоб “про 90-е”. Кто-то честно выкладывает фоточки “из детства”, кто-то – истории про…

Comments for this post were disabled by the author