*** ВНИМАНИЕ: Блог переехал на другой адрес - demin.ws ***

четверг, 22 октября 2009 г.

Коварный printf()

Вчера подорвался на ерунде как ребенок.

Сижу, отлаживаю новый онлайновый ассемблер в своем эмуляторе Радио-86РК. Под отладкой понимается ёрзанье с применением html'я.

Для сборки финального html-файла из кучи мелких у меня написана примитивная программа. Вот ее фрагмент:
...
while (!feof(f)) {
char line[1024];
*line = 0;
fgets(line, sizeof(line), f);
printf(line);
}
...
Подразумевается, что данный код прострочно копирует данные из файла f на стандарный вывод.

Даже если отставить в сторону использование буфера с константной длиной и прочих "штучек" языка С, этот код имеет одну проблему, которая стоила мне сомнений в наличии сознания. До каких-то пор все работало отлично, но как только я начал использовать процентные значения для широт и высот в html, начались странности. Получалось, что вместо, например:
<table width="100%">
на выходе было:
<table width="100">
Вы, наверное, уже догадались, в чем тут дело. Но, признаюсь, я искал проблему минут тридцать.

Вместо:
printf(line);
надо писать:
printf("%s", line);
А иначе все процентные символы будут расцены как указатели форматов, ибо первый параметр printf() - это не просто строка, а формат, и в случае их неэкранирования будут уделены, что и происходило в моем случае.

Вывод (который следует после начального "сам дурак"): Лучше писать на С++ и использовать потоки для форматного вывода.

Лирическое отступление. Кстати, онлайновый ассемблер очень огранично вписался в эмулятор. Спасибо Вячеславу Славинскому за оригинальный код этого ассемблера. Особенно меня радует возможность автоматической фоновой компиляции. Теперь можно, прямо не отходя от эмулятора, переключиться в ассемблер, написать что-нибудь на диалекте Intel 8080 (КР580), скомпилировать и загнать прямо в эмулятор.

4 комментария:

  1. Плюс ещё ошибки будут, если в файле есть нулевые символы ('\0') или спецсимвол конца файла.

    ОтветитьУдалить
  2. А компилировали бы с флагом -Wall, сразу бы увидели предупреждение.

    ОтветитьУдалить
  3. Для вывода строки можно использовать просто puts. Оно быстрее должно работать, ведь puts не нужно разбирать формат.

    По поводу спецсимвола конца файла (0x1A - ^Z), он применим только для текстового режима - "t". В этом случае в вендах при встрече 0x1A, feof действительно возвращает EOF(-1). Кроме того, текстовые преобразования при режиме "t" зависят от платформы, на некоторых системах режим "t" игнорируется, и всегда используется режим "b". Поэтому лучше "t" не использовать, открывать файлы в бинарном режиме "b".

    -Wall не покажет варнинг. Как будто не может быть динамических форматов.

    ОтветитьУдалить