Мы начинаем изучение использования и поведения данного формата при помощи маленьких программ. Первая, printf1, демонстрирует очень простое использование:
Первый вызов printf() выводит строку "0123456789",
которая содержит 10 символов. Следующий формат %n записывает данное
значение в переменную n:
>>gcc printf1.c -o printf1
>>./printf1
0123456789
n = 10
Давайте немного изменим нашу программу заменяя инструкцию
printf() строки 7 на:
7: printf("buf=%s%n\n", buf, &n);
Запуск новой программы подтверждает нашу идею: переменная n сейчас равна 14 (10 символов из строковой переменной buf добавляются к 4 символам строки-константы "buf=", содержащейся в самой строке форматирования).
Итак, мы знаем, что формат %n подсчитывает каждый символ, который появляется в строке форматирования. Более того, как мы продемонстрируем программой printf2, он подсчитывает еще кое-что:
/* printf2.c */
#include <stdio.h>
main() {
char buf[10];
int n, x = 0;
snprintf(buf, sizeof buf, "%.100d%n", x, &n);
printf("l = %d\n", strlen(buf));
printf("n = %d\n", n);
}
Мы используем здесь функцию snprintf(), чтобы не допустить переполнение буфера. Переменная n должна быть равна 10:
>>gcc printf2.c -o printf2
>>./printf2
l = 9
n = 100
Странно? Фактически, формат %n учитывает количество символов, которые должны быть выведены. Этот пример показывает, что обрезание из-за указания размера буфера игнорируется.
Что происходит на самом деле? Строка форматирования полностью расширяется перед тем, как она урезается и затем копируется в буфер назначения:
точность в строке форматирования теперь установлена в 5;
в конце выводится содержимое буфера.
Мы получаем следующее:
>>gcc printf3.c -o printf3
>>./printf3
l = 4
n = 5
buf = [0123] (5)
В первых двух строках нет ничего удивительного. Последняя показывает нам поведение функции printf():
Строка форматирования расширяется, согласно командам1 в ней содержащихся, из-за чего получаем строку "00000\0";
Переменные записываются куда и как надо, что показано копированием x в нашем примере. Строка теперь выглядит "01234\0";
последнее, sizeof buf - 1 байт2 из этой строки копируется в строку назначения buf, что дает нам "0123\0"
Это все происходит не точно так как описано, однако описание отражает основные процессы. Для подробностей читатель должен обратиться к исходникам GlibC, конкретно к vfprintf() в директории ${GLIBC_HOME}/stdio-common.
Перед тем как закончить эту часть, добавим, что возможно получить тот же результат записывая строку форматирования немного в другой форме. Мы использовали формат, называемый точность (точка '.'). Другая комбинация инструкций форматирования приводит к тому же результату: 0n, где n - обозначает ширину, а 0 обозначает, что лишние позиции будут заполнены 0, если вся ширина не будет заполнена.
Теперь, когда вы знаете почти все о строках форматирования, а более конкретно о формате %n, мы изучим их поведение.