Замечание для тех, кто не живет во Франции: в нашей прекрасной стране есть велогонщик, который несколько месяцев делал вид, что не принимал допинг, в то время как другие члены его команды признали это. Он утверждает, что если он и принимал допинг, то не знал об этом. Поэтому известное кукольное шоу использовало французское предложение "on m'aurait menti !", которое и подало мне идею, что написать в этом заголовке.
Давайте начнем с того, чему нас всех учили в руководствах по программированию: большинство функций ввода/вывода в Си используют форматирование данных - это значит, что надо не только предоставлять данные для чтения/записи, но также и информацию как они будут отображены. Следующая программа показывает это:
/* display.c */
#include <stdio.h>
main() {
int i = 64;
char a = 'a';
printf("int : %d %d\n", i, a);
printf("char : %c %c\n", i, a);
}
Выполняя ее мы получаем :
>>gcc display.c -o display
>>./display
int : 64 97
char : @ a
Первый printf() выводит значение целой переменной
i и символьной переменной a, как значение типа
int(это делается при помощи %d) - это приводит к
выводу ASCII-значения этого символа. С другой стороны, второй
printf() переводит целое значение i в соответствующий
код символа ASCII, а именно - 64.
Ничего нового - все одинаково для многих функций, прототипы которых похожи на
функцию printf():
один аргумент - строка символов (const char *format), которая
используется для указания выбранного формата;
один или более необязательных аргументов, содержащих переменные, в которых
значения форматированы в соответствии с указаниями предыдущей строки.
Большинство уроков по программированию останавливаются на этом, предоставляя
неполный список возможных форматов (%g, %h,
%x, использование символа точки . для указания
точности...). Однако, есть еще один, о котором никогда не упоминают:
%n. Вот что говорит страница man по printf() об
этом:
Число символов, выведенных до этого момента, сохраняется по адресу целого числа, указанному аргументом-указателем типа int * (или variant). Преобразование аргументов не происходит.
Здесь - самая важная вещь в этой статье: данный аргумент позволяет производить запись в переменную указатель, даже если она используется в функции для вывода!
Перед тем как продолжить, заметим, что данный формат также присутствует в функциях семейств scanf() и syslog().