Кстати, в нескольких последних строках обратите внимание на то, что оператор printf() можно разнести на две строки. Его можно разделять на большее количество частей при условии, что разрыв не происходит внутри раздела, заключенного в кавычки, или в середине слова.
Использование типов данных
При разработке программы обращайте внимание на то, какие переменные необходимы, и какие типы они должны иметь. Скорее всего, для чисел вы выберете int или, возможно, float, а для символов — тип char. Объявляйте переменные в начале фун-
Данные в языке С 109
кции, в которой они используются. Выбирайте для переменных имена, отражающие их предназначение. При инициализации обеспечьте соответствие типов констант и типов переменных. Например:
int apples =3; /* правильно */
int oranges = 3.0; /* плохая форма */
В отношении несовпадения типов язык С более либерален, чем, скажем, Pascal. Компиляторы С разрешают инициализацию, сделанную во втором операторе, но могут выдать сообщение, особенно если установлен высокий уровень предупреждений. Поэтому лучше не вырабатывать в себе плохие привычки.
Когда вы инициализируете переменную одного числового типа значением другого числового типа, компилятор С преобразует такое значение в тип переменной. Это означает возможность потери данных. Например, взгляните на следующие примеры инициализации:
int cost = 12.99; /* инициализация переменной типа int значением double */
float pi = 3.1415926536; /* инициализация переменной типа float значением double */
В первом объявлении переменной cost присваивается значение 12; при преобразовании значений с плавающей запятой в целочисленные компилятор С вместо округления просто отбрасывает дробную часть числа (выполняет усечение). Во втором объявлении происходит некоторая потеря точности, поскольку для типа float точность гарантируется только в пределах шести цифр. Когда вы делаете такую инициализацию, компиляторы могут (но не обязаны) выдавать предупреждающее сообщение. Подобная проблема могла возникнуть при компиляции программы, представленной в листинге 3.1.
Многие программисты и организации придерживаются систематических соглашений по назначению имен переменным, согласно которым имя отражает тип переменной. Например, можно было бы воспользоваться префиксом i_ для указания типа int и префиксом us_ для отражения типа unsigned short, так что i smart немедленно опознается как переменная типа int, a us verysmart — как переменная типа unsigned short.
Аргументы и связанные с ними ловушки
Полезно еще раз повторить и акцентировать внимание на сделанном ранее в этой главе предупреждении, касающемся использования функции printf(). Как вы можете помнить, элементы информации, передаваемой функции, называются аргументами. К примеру, вызов функции printf ("Здравствуй, мир. " ) содержит один аргумент: "Здравствуй, мир. ". Последовательность символов в кавычках вроде "Здравствуй, мир. " называется строкой. Строки будут обсуждаться в главе 4. Пока что важным моментом является то, что строка, даже если она содержит несколько слов и знаков препинания, считается одним аргументом.
Аналогично, вызов функции scanf ("%d", &weight) содержит два аргумента: "%d" и &weight. Для отделения аргументов друг от друга в языке С применяются запятые. Функции printf() и scanf() необычны в том, что они не ограничены конкретным количеством аргументов. Например, мы вызывали printf() с одним, двумя и тремя аргументами. Чтобы программа работала должным образом, она должна знать, сколько аргументов получает функция. Функции printf() и scanf() используют первый аргумент для указания количества дополнительных аргументов, который будут переданы. Дело в том, что каждая спецификация формата в первой строке говорит о наличии дополнительного аргумента.
110 Глава 3
Например, приведенный ниже оператор содержит два спецификатора формата,
%d и %d:
printf("%d котов съедают %d банок тунца\n", cats, cans);
Это сообщает о том, что функция должна ожидать еще два аргумента, и действительно, дальше следуют два аргумента — cats и cans.
Как программист, вы отвечаете за гарантию того, что количество спецификаций формата соответствует числу дополнительных аргументов, а типы спецификаторов соответствуют типам значений. В настоящее время язык С располагает механизмом прототипирования функций, который проверяет правильность количества и типов аргументов в вызове функции, однако он не работает в случае функций printf() и scanf(), т.к. они принимают переменное число аргументов. Что случится, если программист не справится со своей обязанностью по отношению к аргументам? Предположим, вы написали программу, показанную в листинге 3.9.
Читать дальше