В качестве аргументов списка вывода могут выступать константы, переменные, выражения целого, вещественного, символьного, строкового и булевского типов.
Возвращает функция количество выведенных символов или отрицательное значение, в случае ошибки. Для ввода-вывода в стиле С необходимо подключать заголовочный файл <stdio.h>. Вывод осуществляется в стандартный поток вывода stdout.
спецификации преобразования – последовательность символов, начинающаяся с символа %.
Формат спецификации преобразования в "управляющей строке" включает: обязательный символ ‘ %’ (1 –я колонка таблицы), необязательные символы (флаги, ширина поля вывода, точность, модификатор вывода) (2 –я колонка таблицы) и обязательныйсимвол тип преобразования (3-я колонка таблицы).
Таблица 7. Формат спецификации преобразования в управляющей строке для функции printf()
обязательный символ
необязательный символ
флаги
обязательный символ
тип преобразования
%
+ – для вывода знака перед значением;
‘ ‘ – для вывода пробела вместо знака для положительного числа;
- – для выравнивания влево выводимого числа;
# – для вывода символа системы счисления (0 – для 8-ричной, 0х или 0Х – для 16-ричной);
ширина поля вывода (воздействует только на вывод):
min – для задания минимальной ширины поля вывода в min символов (строка цифр);
0 min – то же самое, но позиции слева для целого числа дополняются нулями;
точность (воздействует только на вывод):
.0 – для d, i, o, u, x – точность по умолчанию; – для e, E, f – отсутствие десятичной точки;
. precision – максимальное число символов, которое надо вывести, или же число цифр после точки в типах float или double;
модификатор вывода
h – для d, i, o, u, x аргумент имеет тип short;
l – для d, i, o, u, x аргумент имеет тип long, для e, f, g аргумент является double
отсутствие «флага» – означает значение «по умолчанию» для спецификации вывода;
c – для вывода символа
d, i – для 10-ичного представления целого знакового значения (signed int)
o – для 8-ичного представления целого беззнакового значения (unsigned int)
u – для 10-ичного представления целого беззнакового значения (unsigned int)
x – для 16-ичного представления целого беззнакового значения (unsigned int)
f – для представления вещественных значений в форме ±mmmm.nnnnnn (float)
e, Е – для представления вещественных значений в форме ±m.nnnnnnE±xx (float)
g, G – для представления вещественных значений в форме e или f (в зависимости от значения и специфицированной точности)
s – для представления строк
p – для представления указателя (адреса)
Ширина поля вывода – это минимальное количество позиций, отводимых под число. Если указанного количества позиций для размещения значения недостаточно, автоматически выделяется требуемое количество позиций. Поэтому для «красивого» вывода необходимо определять значение ширины поля с учетом: точности, формы представления вещественного числа и отводимых для этого позиций, знака числа, предполагаемого количества цифр в его целой части, обязательного перед числом пробела (значение ширины поля должно (как минимум) превышать значение задаваемого модификатора точности).
Смысл модификатора точности precision, задаваемого десятичным числом, зависит от спецификации формата, с которой он используется:
· при выводе строки (например, спецификация %Ns) precision N указывает число символов N для вывода строки;
· при выводе вещественного числа (например, спецификация %Nf или %Ne) precision N указывает количество цифр N, выводимых после десятичной точки;
· при выводе вещественного числа (например, спецификация %Nd или %Ni) precision N указывает минимальное количество N выводимых цифр; если число представляется меньшим количеством цифр, чем указано в precision, выводятся ведущие (начальные) нули;
· при выводе вещественного числа (например, спецификация %Ng или %NG) precision N указывает максимальное количество значащих цифр, которые будут выводиться.
Флаг ‘-‘ указывает на то, что значение выравнивается по левому краю и, если нужно, дополняется пробелами справа. При отсутствии минуса значение выравнивается по правому краю и дополняется пробелами слева.
Модификаторы h и l перед спецификацией (например, %lf, %hu) означают:
· h с типами d, i, o, x и Х указывает на то, что тип аргумента short int, а с типом u – short unsigned int;
· l с типами d, i, o, x и Х указывает на то, что тип аргумента longt int, с типом u – long unsigned int, с типами e, E, f, g, G – double, а не float.
Содержание спецификаций преобразования выглядит так:
%
выводится символ %;
d, i
аргумент преобразуется к десятичному целому;
e, E
аргумент рассматривается как переменная типа float или double, преобразуется в десятичную форму в виде [-]m.nnnnnn[+-]xx, где длина строки из n определяется указанной точностью; точность по умолчанию равна 6;
f
аргумент, рассматриваемый как переменная типа float или double, преобразуется в десятичную форму в виде [-]mmm.nnnn, где длина строки из n определяется указанной точностью; точность по умолчанию равна 6;
g, G
используется формат %e или %f, который короче; незначащие нули не печатаются
о
аргумент преобразуется в форму беззнакового восьмеричного (без лидирующего нуля);
р
вывод указателя в шестнадцатеричном формате (спецификация не входит в стандарт, но существует практически во всех реализациях);
s
аргумент является строкой: символы строки печатаются до тех пор, пока не будет достигнут нулевой символ или не будет напечатано количество символов, указанное в спецификации точности;
u
аргумент преобразуется в форму беззнакового десятичного;
х, Х
аргумент преобразуется в форму беззнакового шестнадцатеричного (без лидирующих 0х).
В С и C++ используется концепция поэлементного ввода-вывода. Это означает, что значения переменных сложных (структурированных) типов не могут быть введены или выведены указанием только имени структурированной переменной в списке ввода-вывода. Ввод-вывод элементов массива и компонентов структуры (с учетом их типа) осуществляется поэлементно, т.е. в списке ввода-вывода указывается идентификатор элемента массива или поля структуры с предшествующим именем переменной-структуры.
С помощью операторов вывода нельзя вывести:
¨ значение переменной типа «перечисление»;
¨ значение переменной типа «массив» (необходимо выводить значения отдельных его элементов);
¨ значение переменной типа «структура» (необходимо выводить значения отдельных полей).
Ввод строк в С выполняет функция int gets(char * str). Функция читает символы из стандартного потока ввода stdin в массив по указателю str, в котором сохраняется строка до тех пор, пока не будет обнаружен символ новой строки ’\n’ или признак конца файла. Символ ’\n’ отбрасывается, и строка завершается ограничителем ’\0’. При обнаружении конца файла или ошибки возвращается NULL.
char str[20];
gets(str); //qwer qwert qwerty
puts(str); //qwer qwert qwerty
Функция gets() не позволяет установить ограничение на количество считываемых символов, поэтому нужно быть осторожными с размером массива, на который указывает параметр str, чтобы избежать переполнения буфера.
Форматный ввод из стандартного потока ввода stdin ("привязан" к клавиатуре) в переменные программы обеспечивает функцияscanf(), в соответствии с «управляющей строкой». При вводе, очевидно, выполняется преобразование из внешнего символьного вида во внутренний двоичный формат. Функция возвращает количество успешно введенных значений. В случае ошибки функция возвращает системную константу EOF (обычно эта константа определена как –1).
Функция scanf(), в отличие от gets(), выполняет ввод строки с пробелами не полностью, а лишь до первого пробела.
Формат оператора ввода:
int scanf("управляющая строка", арг1, арг2, ….);
«Управляющая строка» является первым параметром функции scanf(). Функция считывает символы, интерпретирует их в соответствии с «Управляющей строкой» и запоминает результаты в аргументах. Аргументы представляют собой указатели, ссылающиеся на области памяти, где должны быть сохранены преобразованные введенные данные.
При выполнении оператора форматного ввода программа приостанавливает свою работу и ждет, пока на клавиатуре будут набраны значения и нажата клавиша <Enter>. После нажатия <Enter>, введенное значение присваивается переменной, имя которой указано в списке аргументов (является следующим по порядку). Первое набранное на клавиатуре значение становится значением первой переменной в списке ввода.
Формат ввода значений переменных задается при помощи спецификатора преобразования – последовательности символов, начинающейся с символа % (см. Таблицу 2).
Таблица 8. Формат спецификации преобразования в управляющей строке для функции scanf()
обязательный символ
необязательный символ
флаги
обязательный символ
тип преобразования
%
число – максимальный размер поля ввода
l – тип long для d, o, x, u или double для f, e
h –тип short для d, o, x, u
d – 10-ичное представление
o – 8-ичное представление
x – 16-ичное представление
c – символ
s – строка
l – float или double
f – float или double
u – беззнаковое целое
p – указатель (адрес)
Вводимая последовательность символов может содержать несколько целых и вещественных значений, которые должны отделяться друг от друга, по крайней мере, одним разделителем (пробелом, запятой, символом табуляции и др.). «Выделение числа» при воде выполняется до обнаружения первого пробела, символа табуляции, признака конца строки или признака конца файла. При этом выполнение следующего вызова функции scanf() начинается с символа, следующего за последним (ранее прочитанным), т.е. переход на новую строку для чтения значения следующей переменной списка не осуществляется.
Правило:
1. функция scanf() вводит только до пустого символа (пробел, ’\t’, ’\n’, ’\v’);
2. при вводе строки функция scanf() считывает столько символов, сколько указано в объявлении строковой переменной; пробел при этом считается разделителем, ввод строки проводится только до пробела;
3. в списке аргументов перед именем переменной надо ставить &; при вводе значения строковой переменной символ & ставить не надо.
4. в качестве разделителя значений (при вводе нескольких переменных одной последовательностью), как правило, используются пробельные символы.
Элементы вводимых данных должны разделяться пробелами, знаками табуляции или новой строки. Знаки пунктуации, такие как запятая, точка с запятой и т.п., не считаются разделителями. Это значит, что для оператора scanf("%d%d", &r, &с); последовательность 10 20 будет воспринята, а последовательность 10,20 – нет.
Наличие обычного символа (например,) в управляющей строке заставляет scanf() считать и отбросить этот символ. Например, "%d,%d" заставляет scanf() считать целое число, считать и отбросить запятую и затем считать еще одно целое число. Если указанный символ не обнаружен во входном потоке, scanf() останавливается.
Строки считываются в массивы символов, а имя массива, без всякого указателя, является адресом первого элемента массива. Поэтому, чтобы считать строку в массив символов addr, можно использовать команду scanf("%s", addr); (в этом случае имя addr уже является указателем и не нуждается в префиксе &).
!!! Использование в качестве параметра функции scanf() имени переменной, а не ее адреса, является типичной ошибкой (компилятор эту ошибку не обнаруживает).
С помощью операторов ввода нельзя ввести:
¨ значение логической переменной;
¨ значение переменной типа «перечисление»;
¨ значение переменной типа «массив» (необходимо вводить значения отдельных элементов);
¨ значение переменной типа «структура» (необходимо вводить значения отдельных полей).
Логическое значение можно получить с помощью ввода значений переменной целочисленного типа и последующего приведения типов. Аналогично, c помощью ввода значений переменной беззнакового целочисленного типа и последующего приведения типов, можно проинициализировать переменную перечислимого типа.
В отличие от процедурно-ориентированной библиотеки <stdio.h>, в С++ реализована новая библиотека ввода-вывода – объектно-ориентированная, получившая название <iostream>. В ней концепция «поточного» ввода-вывода получила свое дальнейшее развитие. Главным достоинством системы ввода-вывода С++ является то, что она может перегружаться для создаваемых новых классов, что позволяет легко встраивать в систему ввода-вывода С++ новые создаваемые типы данных.
Система ввода-вывода С++ состоит из двух уровней:
· Физический уровень – либо внешний файл (т.е. именованная область внешней памяти, содержащая какую-либо информацию),либо область памяти,либо логическое устройство компьютера, имеющее свое фиксированное имя.
· Логический уровень – это «логическое устройство» – поток – структура данных, используемая при программировании, существующая в логическом представлении программиста при написании программы (представляется в программе потоковой переменной определенного типа). После того как в программе создан поток, он может быть использован как средство общения с любым физическим файлом или устройством.
Средства ввода-вывода обеспечивают программиста механизмом для извлечения данных из потоков и для включения данных в потоки:
o операция «извлечение из потока» – >> ( перегруженнаяоперация сдвига вправо)– заключается в преобразовании последовательности символов потока во внутреннее представление в памяти значения типизированного объекта; внешнее представление данных – символьное; внутреннее представление данных – последовательность двоичных кодов, регламентированная для каждого типа данных;
o операция «включение в поток» – << ( перегруженная операция сдвига влево)– предполагает обратное преобразование – внутреннего представления в памяти типизированного значения (int, float, char, …..) в последовательность символов потока; при выводе на экран идет преобразование двоичных кодов в символы алфавита, изображаемые на внешнем устройстве.
При включении в программу заголовка <iostream> в программе автоматически становятся доступными стандартные потоки (глобальные потоковые объекты):
cin – объект класса istream, соответствующий стандартному потоку ввода, по умолчанию связан с клавиатурой;
cout – объект класса ostream, соответствующий стандартному потоку вывода, по умолчанию связан с экраном.
Задача программиста при вводе-выводе с помощью потоков – установить соответствие между участвующими в обмене типизированными объектами и последовательностью байтов потока, в которой отсутствует всякая информация о типах передаваемой информации.
Наличие буфера в стандартном входном потоке создает некоторые особенности; в процессе набора данных на клавиатуре, при вводе, они отображаются на экране, но не извлекаются из потока – это дает возможность исправлять ошибки.
При этом при вводе:
o вся вводимая информация (как строка текста) помещается в буфер ввода;
o ввод завершается нажатием клавиши <enter>; а извлечение из буфера происходит только после нажатия <enter>;
o оператор чтения осуществляет разбор и преобразование последовательности вводимых символов во внутреннее двоичное представление в соответствии с типом приемника;
o чтение начинается с первого непробельного символа,начальные пробельные символы игнорируются;
o ограничителем значения числа является нечисловой или пробельный символ; ограничителем строки – пробельный символ;
o ввод в переменную прекращается при наборе недопустимого для её типа символа (например, для операторов int ii; double dd; cin >> ii; cin >> dd; при наборе на клавиатуре значения 123. 456, ii получит значение 123, а dd – значение 0. 456);
o при вводе целых чисел читаются: +, -, десятичная цифра; числа должны во входном потоке разделяться обобщенными пробельными символами (пробел, знак табуляции, перевод строки, <enter>);
o при вводе 16-ичных целых чисел дополнительно читаются также буквы a, A, b, B, ….f, F;
o при вводе вещественных чисел – дополнительно читаются символы e или E и «точка»;
o булевские значения вводятся как 1 (true) и 0 (false);
o ввод перечислимого типа как целого по умолчанию не работает; для каждого нового перечислимого типа необходимо писать собственные операции ввода-вывода;
o ввод символа (даже единственного) заканчивается нажатием клавиши <enter>; при вводе символов игнорируются пробельные символы, поэтому последовательность символов «qwe» можно задать разными способами (перемежая пробелами или нажатием <enter>); манипулятор noskipws позволяет не пропускать пробелы (cin >> noskipws >> c1 >> c2 >> c3;); ввод символа реализуется также операторами: ch = cin. get (); cin. get (ch); cin. read (& ch,1); ввести целое значение в символьную переменную нельзя (даже при наборе цифры, в символьную переменную попадает код); однако символьные типы могут использоваться в арифметических выражениях,
o при вводе строки в символьный массив, в буфер входного потока заносится вся введенная информация с завершающими символами (\r\n), формируемыми при нажатии клавиши <enter>; при каждой операции «извлечение» – извлечение символов происходит только до ближайшего пробела; в строку заносится завершающий символ ‘\0’;
o для ввода строк с пробелами в символьный массив используются методы cin. get (), cin. getline (); метод cin. ignore() извлекает символы из потока, ничего не занося в переменные и имеет два параметра (количество удаляемых символов, по умолчанию – 1, символ-ограничитель, который тоже удаляется из потока, по умолчанию – конец файла);
при выводе:
o целые числа выводятся в десятичной системе счисления; шестнадцатеричная константа 0xFFFF на экране будет показана как десятичное число 65535;
o для вещественного числа выводится 6 цифр после запятой (например, число 7.123456789 выводится как 7 .123457, а число 123456789.15 – как 123456789.150000 или 1.234568е+008;
o указатели по умолчанию выводятся в шестнадцатеричной системе счисления, независимо от типа; исключение составляет указатель на символьную константу: по умолчанию выводится не указатель, а строка символов, на которую он указывает, поэтому для вывода указателя необходимо преобразование типа к типу void* (char* s = ”aaaaa\n”; cout << s << “ “ << static_cast<void*>(s) << ‘\n’;);
o значения типа bool выводятся как целые (1 – true, 0 – false);
o значения перечислимого типа по умолчанию выводятся как целые;
o символьные переменные по умолчанию выводятся как символы; чтобы вывести символьную переменную как число, необходимо задать явное преобразование типа (char n = -100; cout << int(n) << ‘\n’; одиночный символ можно вывести методом put () (char ch = ‘a’; cout.put(ch);) или методом write() (char ch =’a’; cout.write(&ch, 1);
o при выводе строки из памяти в выходной поток переносятся все символы до ‘\0’; Операция оператор << работает и с символьными массивами и со строками типа string (string s3 = “aaaaa\n”; cout << s3;); вывести строку (кроме переменных типа string) можно и методом write() (char *s1=”bbbbb”; cout. write(s1, strlen(s1));
o при выводе выражений необходимо учитывать приоритет операций; оператор << изначально является операцией сдвига влево и имеет свой приоритет (например, cout << d = f << ‘\n’; интерпретируется как (cout << d) = (f << ‘\n’); и вызовет ошибку трансляции; исправление: cout << (d = f) << ‘\n’;)