Форматированный вывод данных

Функция printf() (прототип содержится в файле stdio.h) обеспечивает форматированный вывод. Ее можно записать в следующем формальном виде:

рrintf ("<управляющая строка>", аргумент _1, аргумент _2,...);

Управляющая строка содержит компоненты трех типов: обычные символы, которые просто копируются в стандартный выходной поток (выводятся на экран дисплея); спецификации преобразования, каждая из которых вызывает вывод на экран очередного аргумента из последующего списка; управляющие символьные константы.

Каждая спецификация преобразования начинается со знака % и заканчивается некоторым символом, задающим преобразование. Между знаком % и символом преобразования могут встречаться другие знаки в соответствии со следующим форматом:

% [признаки] [ширина_поля] [точность] [F|N|h|l|L] c_n

На месте параметра c_n (символ преобразования) могут быть записаны:

Таблица 11.

Символы преобразования функции printf()

Символы преобразования Описание
с значением аргумента является символ;
d или i значением аргумента является десятичное целое число;
е значением аргумента является вещественное десятичное число в экспоненциальной форме вида 1.23e+2;
Е значением аргумента является вещественное десятичное число в экспоненциальной форме вида 1.23E+2;
f значением аргумента является вещественное десятичное число с плавающей точкой;
g (или G) используется, как е или f, и исключает вывод незначащих нулей;
о значением аргумента является восьмеричное целое число;
s значением аргумента является строка символов (символы строки выводятся до тех пор, пока не встретится символ конца строки или же не будет, выведено число символов, заданное точностью);
u значением аргумента является беззнаковое целое число;
х значением аргумента является шестнадцатеричное целое число с цифрами 0,..., 9, а, b, с, d, е, f;
X значением аргумента является шестнадцатеричное целое число с цифрами 0,..., 9, А, В, С, О, Е, F;
р значением аргумента является указатель;
n применяется в операциях форматирования. Аргумент, соответствующий этому символу спецификации, должен быть указателем на целое. В него возвращается номер позиции строки (отображаемой на экране), в которой записана спецификация %n.

 

Необязательные параметры в спецификации преобразования:

· признак минус (-) указывает, что преобразованный параметр должен быть выровнен влево в своем поле;

· признак плюс (+) требует вывода результата со знаком;

· строка цифр, задающая минимальный размер поля (ширина поля). Здесь может так же использоваться символ *, который тоже позволяет задать минимальную ширину поля и точность представления выводимого числа;

· точка (.), отделяющая размер поля от последующей строки цифр;

· строка цифр, задающая максимальное число выводимых символов, или же количество цифр, выводимых справа от десятичной точки в значениях типов float или double (точность);

· символ F, определяющий указатель типа far;

· символ N, определяющий указатель типа near;

· символ h, определяющий аргумент типа short int (используется вместе с символами преобразования d, i, о, u, х, Х);

· символ l, указывающий, что соответствующий аргумент имеет тип long (в случае символов преобразования d, i, о, u, х, X) или double (в случае символов преобразования е, Е, f, g, G);

· символ L, указывающий, что соответствующий аргумент имеет тип long double (используется вместе с символами преобразований е, Е, f, g, G);

· символ #, который может встречаться перед символами преобразования g, f, е и перед символом х. В первом случае всегда будет выводиться десятичная точка, а во втором - префикс 0x перед соответствующим шестнадцатеричным числом.

Если после знака % записан не символ преобразования, то он выводится на экран. Таким образом, строка %% приводит к выводу на экран знака %.

Функция printf() использует управляющую строку, чтобы определить, сколько всего аргументов, и каковы их типы. Аргументами могут быть переменные, константы, выражения, вызовы функций; главное, чтобы их значения соответствовали заданной спецификации.

При наличии ошибок, например, в числе аргументов или типе преобразования, результаты будут неверными.

Среди управляющих символьных констант наиболее часто используются следующие:

Таблица 12.

Управляющие символьные константы функции printf()

Управляющие символьные константы Описание
для кратковременной подачи звукового сигнала;
\b для перевода курсора влево на одну позицию;
\f для подачи формата;
\n для перехода на новую строку;
\r для возврата каретки;
\t горизонтальная табуляция;
\v вертикальная табуляция;
\\ вывод символа \;
\' вывод символа ' ;
\" вывод символа ";
\? вывод символа ?.

 

Пример 22. В результате вызова функции printf() сначала выполняется горизонтальная табуляция (\t), т.е. курсор сместится от края экрана, затем на экран будет выведено слово Computer, после этого курсор переместится в начало следующей строки (\n), затем будет выведено целое число i по формату %d (десятичное целое), и, окончательно, курсор перейдет в начало новой строки (\n).

int i=100;

printf("\tComputer\n%d\n", i);

 

Результат работы программы будет иметь вид:

® Computer¿

100¿

¿

Преобразование типов

При выполнении операций происходят неявные преобразования типов в следующих случаях:

· при выполнении операций осуществляются обычные арифметические преобразования (которые были рассмотрены выше);

· при выполнении операций присваивания, если значение одного типа присваивается переменной другого типа;

· при передаче аргументов функции.

Кроме того, в Си есть возможность явного приведения значения одного типа к другому.

В операциях присваивания тип значения, которое присваивается, преобразуется к типу переменной, получающей это значение. Допускается преобразования целых и плавающих типов, даже если такое преобразование ведет к потере информации.

Преобразование целых типов со знаком. Целое со знаком преобразуется к более короткому целому со знаком, посредством усечения старших битов. Целая со знаком преобразуется к более длинному целому со знаком, путем размножения знака. При преобразовании целого со знаком к целому без знака, целое со знаком преобразуется к размеру целого без знака, и результат рассматривается как значение без знака.

Преобразование целого со знаком к плавающему типу происходит без потери информации, за исключением случая преобразования значения типа long int или unsigned long int к типу float, когда точность часто может быть потеряна.

Преобразование целых типов без знака. Целое без знака преобразуется к более короткому целому без знака или со знаком путем усечения старших битов. Целое без знака преобразуется к более длинному целому без знака или со знаком путем дополнения нулей слева.

Когда целое без знака преобразуется к целому со знаком того же размера, битовое представление не изменяется. Поэтому значение, которое оно представляет, изменяется, если знаковый бит установлен (равен 1), т.е. когда исходное целое без знака больше чем максимальное положительное целое со знаком, такой же длины.

Целые значения без знака преобразуются к плавающему типу, путем преобразования целого без знака к значению типа signed long, а затем значение signed long преобразуется в плавающий тип. Преобразования из unsigned long к типу float, double или long double производятся с потерей информации, если преобразуемое значение больше, чем максимальное положительное значение, которое может быть представлено для типа long.

Преобразования плавающих типов. Величины типа float преобразуются к типу double без изменения значения. Величины double и long double преобразуются к float c некоторой потерей точности. Если значение слишком велико для float, то происходит потеря значимости, о чем сообщается во время выполнения.

Преобразование величины с плавающей точкой к целым типам. Сначала происходит преобразование к типу long (дробная часть плавающей величины при этом отбрасывается), а затем величина типа long преобразуется к требуемому целому типу. Если значение слишком велико для long, то результат преобразования не определен.

Преобразования из float, double или long double к типу unsigned long производится с потерей точности, если преобразуемое значение больше, чем максимально возможное положительное значение, представленное типом long.

Преобразование типов указателя. Указатель на величину одного типа может быть преобразован к указателю на величину другого типа. Однако результат может быть не определен из-за отличий в требованиях к выравниванию и размерах для различных типов.

Указатель на тип void может быть преобразован к указателю на любой тип, и указатель на любой тип может быть преобразован к указателю на тип void без ограничений. Значение указателя может быть преобразовано к целой величине. Метод преобразования зависит от размера указателя и размера целого типа следующим образом:

· если размер указателя меньше размера целого типа или равен ему, то указатель преобразуется точно так же, как целое без знака;

· если указатель больше, чем размер целого типа, то указатель сначала преобразуется к указателю с тем же размером, что и целый тип, и затем преобразуется к целому типу.

Целый тип может быть преобразован к адресному типу по следующим правилам:

· если целый тип того же размера, что и указатель, то целая величина просто рассматривается как указатель (целое без знака);

· если размер целого типа отличен от размера указателя, то целый тип сначала преобразуется к размеру указателя (используются способы преобразования, описанные выше), а затем полученное значение трактуется как указатель.

Преобразования при вызове функции. Преобразования, выполняемые над аргументами при вызове функции, зависят от того, был ли задан прототип функции (объявление "вперед") со списком объявлений типов аргументов.

Если задан прототип функции и он включает объявление типов аргументов, то над аргументами в вызове функции выполняются только обычные арифметические преобразования.

Эти преобразования выполняются независимо для каждого аргумента. Величины типа float преобразуются к double, величины типа char и short преобразуются к int, величины типов unsigned char и unsigned short преобразуются к unsigned int. Могут быть также выполнены неявные преобразования переменных типа указатель. Задавая прототипы функций, можно переопределить эти неявные преобразования и позволить компилятору выполнить контроль типов.

Преобразования при приведении типов. Явное преобразование типов может быть осуществлено посредством операции приведения типов, которая имеет формат:

(<имя типа>) операнд

В приведенной записи <имя типа> задает тип, к которому должен быть преобразован операнд.

Пример 23. Величины i,l,d будут явно преобразовываться к указанным в круглых скобках типам.

int i=2;

long l=2;

double d;

float f;

d=(double)i * (double)l;

f=(float)d;

 

Инициализация данных

При объявлении переменной ей можно присвоить начальное значение, присоединяя инициатор к описателю. Инициатор начинается со знака "=" и имеет следующие формы.

Формат 1 = <инициатор>;

Формат 2 = { <список инициаторов> };

Формат 1 используется при инициализации переменных основных типов и указателей, а формат 2 - при инициализации составных объектов.

Пример 24.Инициализация переменных.

char tol = 'N'; //Переменная tol инициализируется символом 'N'.

 

const long megabute = (1024 * 1024); /*Немодифицируемая переменная megabute

инициализируется константным

выражением, после чего она не может

быть изменена.*/

 

static int b[2][2] = {1,2,3,4}; /*Инициализируется двухмерный массив b целых

величин, элементам массива присваиваются

значения из списка.*/

 

static int b[2][2] = {{1,2},{3,4}}; /*Назад инициализация может

быть выполнена следующим образом.*/

 

static int b[3[] = {{1,2},{3,4}}; /*При инициализации массива можно

опустить одну или несколько

размерностей.*/

 

static int b[2][2] = {{1,2},{3}}; /*Если при инициализации указано

меньше значений для строк, то

ставшиеся элементы инициализируются 0,

т.е. элементы первой строки получат

значения 1 и 2, а второй 3 и 0.*/

 

При инициализации составных объектов, нужно внимательно следить за использованием скобок и списков инициализаторов.

Пример 25.Инициализируется массив структур comp из двух строк и трех столбцов, где каждая структура состоит из двух элементов real и imag.

struct complex

{

double real;

double imag;

} comp [2][3] = {{{1,1},{2,3},{4,5}},{{6,7},{8,9},{10,11}}};

 

struct complex comp2 [2][3] = {{1,1},{2,3},{4,5}, {6,7},{8,9},{10,11}};

 

В этом примере компилятор интерпретирует рассматриваемые фигурные скобки следующим образом:

· первая левая фигурная скобка - начало составного инициатора для массива comp2;

· вторая левая фигурная скобка - начало инициализации первой строки массива comp2[0]. Значения 1,1 присваиваются двум элементам первой структуры;

· первая правая скобка (после 1) указывает компилятору, что список инициаторов для строки массива окончен, и элементы оставшихся структур в строке comp[0] автоматически инициализируются нулем;

· аналогично список {2,3} инициализирует первую структуру в строке comp[1], а оставшиеся структуры массива обращаются в нули;

· на следующий список инициализаторов {4,5} компилятор будет сообщать о возможной ошибке так как строка 3 в массиве comp2 отсутствует.

При инициализации объединения задается значение первого элемента объединения в соответствии с его типом.

Пример 26. Инициализируется переменная pers.name.

union tab

{

unsigned char name[10];

int tab1;

} pers = {'A','H','T','O','H'};

 

Так как это массив, для его инициализации требуется список значений в фигурных скобках. Первые пять элементов массива инициализируются значениями из списка, остальные нулями.

Инициализацию массива символов можно выполнить путем использования строкового литерала.

char stroka[] = "привет";

 

Инициализируется массив символов из 7 элементов, последним элементом (седьмым) будет символ '\0', которым завершаются все строковые литералы.

В том случае, если задается размер массива, а строковый литерал длиннее, чем размер массива, то лишние символы отбрасываются.

Следующее объявление инициализирует переменную stroka как массив, состоящий из семи элементов.

char stroka[5] = "привет";

 

В переменную stroka попадают первые пять элементов литерала, а символы 'Т' и '\0' отбрасываются.

Если строка короче, чем размер массива, то оставшиеся элементы массива заполняются нулями.

Тема 8. Функции

В отличие от других языков программирования высокого уровня в языке СИ нет деления на процедуры, подпрограммы и функции, здесь вся программа строится только из функций.