Московский государственный университет
физический факультет
кафедра квантовой теории и физики высоких энергий
В.А.Ильина П.К.Силаев
Краткий справочник по языку “C”
Москва 2012
Содержание
1 Введение 2
2 Минимальные требования к счетной программе на “С” 2 3 Минимальные сведения о типах переменных языка “С” 3 4 Массивы и указатели в языке “C” 4 5 Минимальные сведения о ядре языка “С” 5.1 Арифметические операторы и операторы присваивания.............. 5.2 Логические операторы................................. 5.3 Побитовые операторы................................. 5.4 Условный оператор.................................. 5.5 Оператор цикла for.................................. 5.6 Определение функций................................. 5.7 Параметры командной строки............................ 6 Минимальный набор библиотечных функций языка “С” 6.1 Математические функции............................... 6.2 Функции для работы со строками.......................... 6.3 Функции ввода-вывода................................ 7 Минимальные сведения о структуре программы на языке “С” 7.1 Функция “main” и другия функции......................... 7.2 Область локализации переменных.......................... 7.3 Пример простейшей бессмысленной программы.................. 1 Введение Это ни в коем случае не учебник “С”, а именно шпаргалка, которая дает возможность писать элементарные программы. Незнание любого приведенного здесь факта делает программирование на “С” практически невозможным.
Шпаргалка эта не претендует на особенную аккуратность. Главное предупреждение, которое необходимо сделать, касается приведенных в тексте примеров. В них часто перемешаны описания переменных и исполняемые операторы. В настоящей программе описания должны идти до исполняемых операторов.
Сделаем несколько замечаний общего характера о счетных программах.
Во-первых, касательно стиля программирования. Вы должны понимать, что хороший стиль программирования не есть инвариантная величина. То, что является обязательным требованием для кусочка какого-либо большого проекта (front end к какой-нибудь базе данных), становится совершенно необязательным, а зачастую и вредным, если Вы пишете счетную программу для себя. Нет ничего страшного в использовании глобальных переменных, если Вы пишете не кусочек очень большого проекта. И стандартная рекомендация: “если функции для работы нужна память, то в начале работы функции она должна эту память заказать, а в конце освободить” является не просто бесполезной, а вредной, потому что сильно замедляет работу программы (если функция вызывается достаточно часто). Гораздо разумнее застолбить эту память навсегда глобально или статически.
Во-вторых, касательно процедуры написания и отладки счетной программы. Стандартный абсолютно неверный подход заключается в том, что Вы пишете здоровенную программу от начала до конца, а потом начинаете ее компилировать и запускать на счет. После этого как правило произносятся фразы: “я все написал правильно, а машина не работает”. Или даже:
“это ошибка в компиляторе”. Все это проявление мании величия средней степени. Разумеется, надо поступать совершенно иначе. Надо написать маленький кусочек программы, и проверить, как он работает. (При этом следует всегда помнить, что 100% гарантии тут нет даже если этот кусочек правильно работает при тех входных данных, которые Вы использовали при тестировании, он может начать работать неправильно при других входных данных. Как правило такую ошибку поймать труднее всего). Дальше следует дописать еще кусочек, и тоже его оттестировать. И так далее.
2 Минимальные требования к счетной программе на “С” 1. Программа не должна запрашивать входные данные и параметры с клавиатуры и не должна включать в себя входные данные. Входные параметры могут вводиться с командной строки (либо из конфигурационного файла), а входные данные должны считываться из файла данных.
2. Программа, если она считает дольше десятка секунд, должна выводить на экран минимальную информацию о текущем этапе вычислений.
3. Результаты счета должны выводиться в файл результатов, а не только на экран.
4. Программа должна быть легко читаема. Не надо бояться лишних пробельных строк и закомментированных строк-разделителей.
5. Не следует гоняться за лаконичностью в ущерб простоте Ваша задача состоит в написании понятной, работающей и легко отлаживаемой программы, а не в демострации гибкости, которую допускает язык “С”.
6. В программе не должны встречаться загадочные числа 7, 666 и т.п. Все эти константы должны быть заменены посредством #define на подходящие имена. Как правило, эти имена пишутся большими буквами, чтобы отличить константы от настоящих переменных.
7. Однобуквенные имена переменных допустимы для переменных цикла или вспомогательных переменных, но отнюдь не для переменных, несущих смысловую нагрузку имена этих переменных должны быть осмысленными.
8. Не следует все операции запихивать в функцию main(). Крупные логические блоки непременно следует выделять в функции. С другой стороны, не следует дробить программу на кучу маленьких функций, каждая из которых вызывает следующую, которая, в свою очередь, вызывает следующую и т.д.
9. Совершенно необязательно все данные, которые получает и возвращает функция, запихивать в ее аргументы. Выбор между использованием глобальной переменной, использованием формального параметра, использованием адреса переменной как формального параметра и использованием оператора return должен делаться в зависимости от конкретной ситуации.
3 Минимальные сведения о типах переменных языка “С” 1. Во-первых, существуют переменные типа char, т.е. буквы. Как правило, эти переменные имеют размер в 1 байт (т.е. 8 бит) и меняются в пределах 128... 127. Если отрицательные значения нежелательны, можно использовать модификатор unsigned: переменные unsigned char меняются в пределах 0... 255. В счетных программах переменные char употребляются главным образом для сооружения строк (см. ниже). Чтобы присвоить переменной char значение, соответствующее коду какой-либо буквы, достаточно написать На машине с ASCII-кодировкой такая запись вполне эквивалентна загадочной и неразумной записи “buk=68;”.
Кроме “обычных” (печатаемых) букв существуют и спецсимволы: ’\n’ переход на новую строку; ’\t’ табуляция; ’\r’ переход на первую позицию текущей строки;
“звонок”, т.е. одиночный писк терминала; ’\” ’ двойная кавычка; ’\’ ’ одиночная кавычка; ’\\’ сам символ “\” (обратный слэш).
2. Во-вторых, существуют переменные типа int, т.е. целые числа. Допустимы модификаторы unsigned и long: “long int”, “unsigned int” и “unsigned long int”. Диапазон изменения этих переменных зависит и от типа машины и от компилятора. Обычно int 2 байта (т.е. 32768... 32767 и 0... 65536 для unsigned int), а long int 4 байта (т.е. 2147483648... 2147483647 и 0... 4294967296 для unsigned long int). Нередко оба 3. В-третьих, это переменные типа double. Форма их записи традиционна для алгоритмических языков: 1.2345, -0.7777777777, -33.4444444e-86, -2.1111e33 (Существует еще тип float, но переменные этого типа безусловно не должны встречаться в счетных программах. Экономия памяти, которую они дают, довольно иллюзорна, а потеря точности будет очень существенна. Кроме того, использование float как правило приводит к замедлению работы программы.) Тип любой используемой переменной должен быть определен. Это делается так:
Подробнее о области действия этих определений см. ниже. Кроме того, Вам необходимо знать о модификаторе “static” (также см. ниже).
4 Массивы и указатели в языке “C” Обычно в счетных программах употребляются одномерные и двумерные массивы. Массив фиксированного размера определяется так:
int numer[9],indeks[20][30];
Число в квадратных скобках задает размер массива. После этих определений можно ссылаться на любой элемент массива Нумерация элементов массива начинается с нуля. Поэтому последний (девятый) элемент массива numer это numer[8]. Попытка написать numer[9] постоянная ошибка. Компиляторы языка “C” не проверяют неправильность индекса (отрицательный индекс или индекс, больший или равный размеру массива). Более того, если Вы положили число по неверному адресу, Вы с некоторой вероятностью его оттуда и возьмете. Поэтому неверная программа может иногда делать вид, что она работает правильно. Неустойчивость работы программы (разные результаты при последовательных запусках) верный признак неправильной работы с памятью.
В том случае, когда размер массива заранее неизвестен, приходится прибегать к динамической аллокации памяти.
Для одномерных массивов следует сначала определить указатель а затем, когда уже стал известен размер массива (например, 95), отвести память под массив и поместить указатель на его начало:
vect=(double *)malloc(95*sizeof(double));
if(vect==NULL) {printf(”No mem for vect!\n”); exit(1); } Функция malloc ничего не знает о типах переменных, поэтому она измеряет память просто в байтах и возвращает указатель типа “void *”. Так что размер массива в байтах вычисляется с помощью функции sizeof, которая возвращает размер переменной типа double в байтах, а запись “(double *)” является принудительным преобразованием типа от “void *” к “double *”.
Кстати, совершенно аналогично можно принудительно преобразовывать и другие типы.
Например, целочисленной переменной можно присвоить значение типа double: “n=1.5;”, но лучше записать это как “n=(int)1.5;”. Более того, “3/2” равно единице, а вот “3/2.0” или “3/(double)2” равно 1.5.
Если памяти не хватает, функция malloc возвращает константу NULL. В этом случае необходимо немедленно прекращать работу программы с помощью функции exit. Чтобы пользоваться функциями malloc и exit, в заголовке программы необходимо поставить Для двумерных массивов следует сначала определить указатель на массив указателей:
когда размер массива уже известен (например, 20 30), надо сначала отвести память под массив указателей:
dat=(int **)malloc(20*sizeof(int *));
if(dat==NULL) {printf(”No mem for dat!\n”); exit(1); } А затем для каждого из 20 указателей провести такую процедуру: отвести память под целых чисел и поставить этот указатель на начало соответствующего массива:
Результат действия оператора if очевиден при выполнении условия первый оператор или первый набор операторов, ограниченный фигурными скобками (блок) исполняется, в противном случае исполняется оператор или блок, расположенный после ключевого слова else.
Вторая половина условного оператора (начиная с else) может быть опущена.
Существуют несколько характерных ошибок при написании условного оператора. Одна из них уже упоминалась:
Здесь нет синтаксической ошибки, но a будет равно 7.
Здесь тоже нет синтаксической ошибки, но a будет равно 7.
К счастью, другие аналогичные ошибки вызывают ругань компилятора:
Можно порекомендовать всегда ставить фигурные скобки в условном операторе, даже вокруг одного оператора. Это несколько уменьшает читабельность, но и уменьшает вероятность ошибки при редактировании (если вдруг понадобится дописать еще пару операторов).
5.5 Оператор цикла for Допустимые варианты синтаксиса иллюстрируются такими примерами: