Первым аргументом printf() является форматная строка. Каждый символ % показывает, что вместо него должно быть подставлено значение аргумента, а следующий за ним символ определяет тип этого аргумента. Вот некоторые из поддерживаемых типов (полное описание см. в [KERNIGHAN88]):
%dцелое число
%gчисло с плавающей точкой
%cchar
%sC-строка
Дополнительные аргументы printf() на позиционной основе сопоставляются со спецификаторами формата, начинающимися со знака %. Все остальные символы в форматной строке рассматриваются как литералы и выводятся буквально.
* Основные недостатки семейства функций printf() таковы: во-первых, форматная строка не обобщается на определенные пользователем типы, и, во-вторых, если типы или число аргументов не соответствуют форматной строке, компилятор не заметит ошибки, а вывод будет отформатирован неверно. Однако у функций printf() есть и достоинство – компактность записи. Получите так же отформатированный результат с помощью объекта класса ostringstream.
* Сформулируйте достоинства и недостатки обоих подходов.
20.9. Состояние формата
Каждый объект класса из библиотеки iostream поддерживает состояние формата, которое управляет выполнением операций форматирования, например основание системы счисления для целых значений или точность для значений с плавающей точкой. Для модификации состояния формата объекта в распоряжении программиста имеется предопределенный набор манипуляторов.1 Манипулятор применяется к потоковому объекту так же, как к данным. Однако вместо чтения или записи данных манипулятор модифицирует внутреннее состояние потока. Например, по умолчанию объект типа bool, имеющий значение true (а также литеральная константа true), выводится как целая ‘1’:
#include iostream.h
int main()
{
bool illustrate = true;
cout "объект illustrate типа bool установлен в true: "
illustrate '\n';
}
Чтобы поток cout выводил переменную illustrate в виде слова true, мы применяем манипулятор boolalpha:
#include iostream.h
int main()
{
bool illustrate = true;
cout "объект illustrate типа bool установлен в true: ";
// изменяет состояние cout так, что булевские значения
// печатаются в виде строк true и false
cout boolalpha;
cout illustrate '\n';
}
Поскольку манипулятор возвращает потоковый объект, к которому он применялся, то допустимо прицеплять его к выводимым данным и другим манипуляторам. Вот как можно перемежать данные и манипуляторы в нашей программе:
#include iostream.h
int main()
{
bool illustrate = true;
cout "объект illustrate типа bool: "
illustrate
"\nс использованием boolalpha: "
boolalpha illustrate '\n';
// ...
}
Вывод данных и манипуляторов вперемежку может сбить пользователя с толку. Применение манипулятора изменяет не только представление следующего за ним объекта, но и внутреннее состояние потока. В нашем примере все значения типа bool в оставшейся части программы также будут выводиться в виде строк.
Чтобы отменить сделанную модификацию потока cout, необходимо использовать манипулятор noboolalpha:
cout boolalpha // устанавливает внутреннее состояние cout
illustrate
noboolalpha // сбрасывает внутреннее состояние cout
Как мы покажем, для многих манипуляторов имеются парные.
По умолчанию значения арифметических типов читаются и записываются в десятичной системе счисления. Программист может изменить ее на восьмеричную или шестнадцатеричную, а затем вернуться к десятичной (это распространяется только на целые типы, но не на типы с плавающей точкой), пользуясь манипуляторами hex, oct и dec:
#include iostream
int main()
{
int ival = 16;
double dval = 16.0;
cout "ival: " ival
" установлен oct: " oct ival "\n";
cout "dval: " dval
" установлен hex: " hex dval "\n";
cout "ival: " ival
" установлен dec: " dec ival "\n";
}
Эта программа печатает следующее:
ival: 16 установлен oct: 20
dval: 16 установлен hex: 16
ival: 10 установлен dec: 16