На сегодняшнем занятии мы разберем особенности функции printf()
и различные типы данных: целочисленные и вещественные числа, символы, массивы и строки. Однако в языке C существуют и другие типы данных, такие как указатели, структуры, объединения, перечисления и есть возможность создавать пользовательские типы данных.
Функция printf() и форматированный вывод
Для вывода символов на экран или в стандартный поток вывода в языке C используется функция printf()
. Она принимает строку как первый аргумент и заменяет специальные символы в этой строке данными, переданными следующими аргументами. Эти данные могут быть представленными в виде строк, символов, целых или вещественных чисел, а также указателей. Каждый тип данных имеет свой формат спецификации.
В прошлом уроке мы выводили строку "Hello World" следующим образом:
printf("Hello World\n");
Этого же эффекта можно добиться так:
printf("%s\n", "Hello World");
Спецификатор %s
означает, что вместо него будет подставлен следующий аргумент, данные которого должны быть строкой. Для вывода целого числа применяется следующий код:
printf("%d\n", 5);
Вместо значения 5 может быть использована переменная целочисленного типа. Функция printf()
позволяет использовать произвольное количество аргументов:
printf("%d %s, %d %s.\n", 3, "dogs", 2, "cats");
Данные подставляются в порядке следования: число 3 помещается на место первого спецификатора, "dogs" — на место второго и так далее. Следует внимательно соблюдать соответствие форматов и данных.
Иногда необходимо зарезервировать больше пространства под вывод данных, чем требуется. Число между символом %
и форматом указывает ширину поля, например, %10d
. По умолчанию текст выравнивается по правому краю, но добавив знак минус перед числом, можно изменить выравнивание на левое.
Создайте программу, которая бы выводила информацию подобно изображению выше, используя возможность задавать ширину поля и выравнивание по краям.
Целочисленные типы
Язык C предлагает несколько типов для работы с целыми числами, различающихся объемом памяти, выделяемой под переменную, а также допускаемым диапазоном значений. Объем памяти определяет, какое максимальное значение можно хранить в данной переменной. Стоит помнить, что количество выделенной под тип памяти может зависеть от операционной системы.
К примеру, если переменной целочисленного типа выделяется 2 байта (что составляет 16 бит) и она принимает только неотрицательные числа, диапазон значений будет от 0 до 65535. Если же тип позволяет отрицательные значения, диапазон будет от -32768 до +32767.
На практике часто используется тип int
. Пример объявления и инициализации целочисленных переменных, а также вывода их значений:
#include <stdio.h>
int main() {
int lines, i;
int count = 0;
lines = 100;
i = -1;
printf("%5d %5d %5d\n", i, count+10, lines);
}
Заметьте, что в языке C можно присвоить значение переменной прямо при ее объявлении.
Обычно под int
, который может принимать как положительные, так и отрицательные значения, выделяется 4 байта (32 бита). Это позволяет хранить значения от -2 147 483 648 до 2 147 483 647. Даже если в коде языка C переменной int max
будет присвоено максимально допустимое значение, а потом это значение превысится, ошибки не будет ни на этапе компиляции, ни при выполнении программы.
#include <stdio.h>
int main() {
int max = 2147483647;
printf("%d\n", max+1);
printf("%d\n", max+2);
printf("%d\n", max+10);
}
Результат выведется таким образом:
-2147483648
-2147483647
-2147483639
Эффект объясняется тем, что числовая ось представляется как круг. Достигнув конца в одном направлении, мы возвращаемся к началу с противоположной стороны. Таким образом, перевысив максимальное значение, мы увидим минимальное.
Аналогично для минимума int
. Если попробовать выделить больше, то результат снова перевернется.
#include <stdio.h>
int main() {
int min = -2147483648;
printf("%d\n", min-1);
printf("%d\n", min-2);
printf("%d\n", min-10);
}
Получаем:
2147483647
2147483646
2147483638
В C помимо int
существуют другие целочисленные типы:
-
short
— выделяется меньше байтов, чем дляint
; -
long
— выделяется больше байтов, чем дляint
; это зависит от системы; -
unsigned
— такой же размер как уint
, но отсутствуют отрицательные значения, что увеличивает диапазон положительных; -
unsigned short
; -
unsigned long
.
При работе с длинными числами, спецификацию формата дополняют буквой l
. Например:
printf("%ld\n",i);
printf("%15ld\n",i);
Символы
Символьный тип данных занимает 1 байт. Каждый символ имеет собственное целое число в таблице ASCII.
Тип char
в C охватывает диапазон от -128 до 127. Значения от 0 до 127 можно выводить как символы, если же значение указывает на символ, оно заключено в одиночные кавычки, например: 'w'. Еще имеется тип unsigned char
с диапазоном от 0 до 255.
Также можно использовать целые числа, такие как int
или short
, чьи значения выводятся как символы, если они находятся в соответствующем диапазоне. Таким переменным можно присваивать символы.
Целые числа до 127 или 255 можно разместить в char
или unsigned char
для экономии памяти.
В языке C символы и числа взаимозаменяемы. Для вывода символов используется формат %c
.
Следующая программа:
#include <stdio.h>
main() {
char ch = 63;
unsigned char uch = 'r';
short j = 'b', k = 99;
printf("%c == %d\n", ch, ch);
printf("%c == %d\n", uch, uch);
printf("%c, %c\n", j, k);
}
выводит:
? == 63
r == 114
b, c
В таблице ASCII число 63 соответствует знаку '?'. Сначала выводится переменная ch
как символ, затем как число. Аналогично с uch
, хотя оно и было задано через символ.
Вещественные типы данных
В языке C определены три типа чисел с плавающей точкой: float
, double
и long double
. Для вывода вещественных чисел доступны три формата, не зависящих от типов, но связанных с удобством отображения. Вещественные числа могут иметь высокую точность и широкий диапазон значений. Если воспользоваться printf()
с такими параметрами:
double a = 0.0005;
printf("%f\n", a);
printf("%g\n", 0.0005);
printf("%g\n", 0.00005);
printf("%e\n", 0.0005);
, то увидите такой результат:
0.000500
0.0005
5e-05
5.000000e-04
При %f
число отображается в обычном формате с точностью до шести знаков после запятой по умолчанию.
Для %g
число выводится в нормальном виде, если значимые нули не превышают четырех. В случае превышения они, как и в третьем примере, выходят в экспоненциальной записи 5e-5, что означает 5 * 10-5, равно 0.00005. Аналогично для большого числа, представляющего 4325, в виде 4.325e+3, что эквивалентно 4.325 * 103. Для дополнительных сведений можно изучить статью на Википедии «Экспоненциальная запись».
Формат %e
всегда выдает число в нормализованной форме.
Чтобы округлить число до необходимой точности, перед буквой формата указывают количество знаков, например, printf("%.2f", 0.23)
выведет 0.23. Когда необходимо указать ширину поля, этот параметр записывают перед точкой, к примеру, %10.3f
.
Массивы
Переменные-массивы объявляются в C следующим образом:
int arr[5], nums[N];
float f_arr[100];
char str[80];
Если для размера массива используется константа, она должна быть определена заранее, как правило, вне функций:
#define N 100
#define
— команда препроцессора, применяемая не только для констант. Препроцессор заменяет все упоминания константы ее значением при обработке исходного кода программы.
Индексы массивов в языке C начинаются с нуля.
Значения элементам массивов присваиваются сразу или в процессе выполнения кода. Пример:
char vowels[] = {'a', 'e', 'i', 'o', 'u', 'y'};
float f_arr[6];
f_arr[0] = 25.3;
f_arr[4] = 34.2;
printf("%c, %.2f\n", vowels[4], f_arr[0]);
Когда переменная-массив объявляется и определяется одновременно, как vowels
, то размер массива указывать не обязательно, он определяется автоматичесkи по количеству элементов в инициализаторе.
Строки
Несмотря на наличие спецификатора вывода строк %s
, в языке C отсутствует отдельный строковой тип данных. Строки в C представляют собой массивы символов, последний из которых — это символ '\0', обозначающий конец строки.
Строки отличаются от числовых массивов в C, и работа с ними требует отдельного подхода. Мы еще вернемся к этому.
Ранее мы объявили массив vowels
. Если определить его так:
char vowels[] = {'a', 'e', 'i', 'o', 'u', 'y', '\0'};
или так:
char vowels1[] = "aeiouy";
то он будет являться строкой. Во втором примере, кавычки указывают, что это строка, и завершающий символ '\0' добавляется автоматически.
Вывод массивов символов возможен, просто указав имя переменной, однако это не работает с массивами чисел:
printf("%s\n", vowels);
printf("%f\n", f_arr); // ошибка
Функция sizeof()
Функция sizeof()
в языке C возвращает количество байт, выделяемых памяти под указанный аргумент — это может быть константа, тип данных или переменная.
Для отображения результата, возвращенного sizeof()
, используется формат %lu
(беззнаковое длинное целое). Примеры:
int a = 10;
int b[100];
printf("Integer: %lu \n", sizeof(a));
printf("Float: %lu \n", sizeof(float));
printf("Array of 100 integers: %lu \n", sizeof(b));
Задание
Создайте программу, которая выводит информацию о выделяемых в памяти байтах под типы данных, изученные в этом уроке. Помните, что при использовании массивов символов, определяемых через строковые литералы (в двойных кавычках), встроенный '\0' увеличивает размер массива на единицу.