- •Содержание
- •Глава 1. Описание языка c ......................................................................... 5
- •Глава 2. Описание языка fortran ....................................................... 38
- •Глава 3. Решение задач на языках c и fortran ................................ 70
- •Глава 1. Описание языка c
- •1.1. Структура c-программы
- •1.2. Скалярные типы данных и константы, строковые константы
- •1.3. Описание скалярных переменных. Директива #define
- •1.4. Операции. Выражения
- •1.5. Стандартные математические функции
- •1.6. Ввод-вывод
- •Int printf ( строка формата [ , список вывода ] )
- •Int scanf ( строка формата , список ввода )
- •1.7. Метки, оператор goto, условные конструкции, оператор break, функция exit
- •If ( логическое выражение ) оператор/блок [ else оператор/блок ]
- •1.8. Циклические конструкции
- •1.9. Указатели, адресная арифметика
- •1.10. Массивы
- •1.11. Функции
- •1.12. Классы памяти. Общие правила описания. Оператор typedef
- •1.13. Строковые переменные, ввод-вывод строк, стандартные функции обработки строк. Функции проверки символов
- •Int sprintf(char* s,char* формат [ , список вывода ] )
- •1.14. Макроопределения
- •1.15. Внешние файлы
- •Int fclose(file* указатель на файл )
- •Int feof(file* файл )
- •1.16. Структуры, объединения, битовые поля
- •1.17. Динамическое распределение памяти
- •1.18. Графика
- •Глава 2. Описание языка fortran
- •2.1. Структура fortran-программы
- •2.2. Типы данных. Константы
- •2.3. Описание переменных, правила умолчания, операторы
- •Implicit тип ( буквы ) , ...
- •2.4. Оператор присваивания. Операции
- •2.5. Стандартные математические функции
- •2.6. Ввод-вывод
- •2.7. Оператор соntinue. Операторы перехода. Условные операторы
- •If (логическое выражение)оператор
- •If (арифметическое выражение)метка1,метка2,метка3
- •2.8. Операторы цикла
- •2.9. Функции loc, locfar, locnear
- •2.10. Массивы
- •2.11. Подпрограммы subroutine, function и interface
- •Intrinsic список имен
- •2.12. Общие области, подпрограмма block data. Оператор equivalence
- •2.13. Символьные переменные
- •2.14. Операторные функции
- •2.15. Внешние файлы
- •2.16. Структуры
- •2.17. Динамические массивы
- •2.18. Графика
- •Глава 3. Решение задач на языках c и fortran
- •Задача 1. Вывод натурального числа
- •Задача 2. Сумма ряда
- •Задача 3. Медленная сортировка
- •Задача 4. Быстрая сортировка
- •Задача 5. Слова
- •Задача 6. График
- •Задача 7. Кубическое уравнение
- •Задача 8. Собственное число
- •Комментарии к задачам
1.11. Функции
Если в таких языках, как FORTRAN и PASCAL, делается различие между подпрограммами-процедурами и подпрограммами-функциями, то в языках С и С++ используются только функции. Программы на языке С (C++) - это совокупность следующих одна за другой функций, одна из которых main (главная функция). Функция main обеспечивает создание точки входа в откомпилированную программу. Каждая функция имеет статическую продолжительность существования и внешний тип компоновки: в С запрещено вложение одной функции в другую.
Функция в языке С состоит из заголовка функции и тела функции. Тело функции - это блок (последовательность операторов в {}), а заголовок в общем случае имеет вид:
тип функции имя функции ( тип параметра имя параметра ,
[ [ тип параметра имя параметра , ... ] )
Заголовок функции не является оператором, поэтому он не заканчивается “;”. Функция может иметь скалярный тип (число, указатель) или тип void (не возвращает никакого значения). Все программные единицы, не вычисляющие какого-либо одного скалярного значения, являются функциями типа void. Каждый параметр в заголовке функции описывается отдельно, даже если параметры имеют один и тот же тип. Допускаются функции без параметров, в этом случае список параметров имеет вид () или (void) .
Выход из функции осуществляется оператором
return [ выражение ] ;
Если выражение задано в операторе return, то его значение преобразуется к типу функции, в противном случае значение, возвращаемое функцией, остается неопределенным. Функция может содержать любое количество операторов return. Присваивать имени функции какое-либо значение, как в языках PASCAL и FORTRAN, не нужно.
В большинстве языков программирования параметры передаются либо по значению, либо по адресу. В первом случае подпрограмме доступна не сама переменная, а только ее значение, во втором же случае подпрограмма работает непосредственно с переменной, переданной ей в качестве параметра. Таким образом, переменную, переданную по адресу, подпрограмма может модифицировать, а переданную по значению - нет. В С++, как и в Паскале, реализованы оба способа передачи параметров. В языке С параметры передаются только по значению, функция получает лишь их значение, но не адрес, и поэтому не может изменить значение аргумента. Общепринятый способ обеспечить функции доступ к какой-либо переменной вызвавшей программы состоит в том, что вместо самой переменной в качестве параметра передается указатель на нее.
Параметры-массивы допускаются, но в качестве аргумента, соответствующего такому параметру, задается имя массива, т.е. адрес, таким образом параметр-массив есть на самом деле параметр-указатель. Для параметров-одномерных массивов нет необходимости указывать длину. Запишем, например, функцию, вычисляющую наибольший элемент массива целых чисел :
int max_el(int x[],int n) {int i,m;
for(m=x[0],i=0;i<n;i++) m=(x[i]>m)?x[i]:m; return m;}
Такая функция может обрабатывать массивы любой длины, но при каждом вызове следует определять фактическую длину с помощью аргумента n. Описание параметра int x[] практически эквивалентно описанию int* x, так что в функции можно отказаться от использования индексов, которые введены лишь для удобства записи :
int max_el(int* x,int n) {int i,m;
for(m=*x,i=1;i<n;i++) m=(*(x+i)>m)?*(x+i):m; return m;}
При использовании параметров-многомерных массивов возможно несколько вариантов их передачи в функцию. Например, запишем функцию нахождения наибольшего элемента квадратной матрицы. Пусть матрица описана в программе в виде:
#define SIZE 3
void main(void) { int b[][SIZE]={1,2,3,4,5,6,7,8,9};
Тогда соответствующую функцию можно записать в трех вариантах :
1)
int max_el(int x[][SIZE],int n) {int i,j,m;
for(m=x[0][0],i=0;i<n;i++)for(j=0;j<n;j++) m=(x[i][j]>m)?x[i][j]:m;
return m;}
2)
int max_el(int x[][SIZE],int n) {int i,j,m;
for(m=*x[0],i=0;i<n;i++)
for(j=0;j<n;j++) m=(*(x[i]+j)>m)?*(x[i]+j):m;
return m;}
3)
int max_el(int*x,int size,int n) {int i,j,m;
for(m=*x,i=0;i<n;i++)
for(j=0;j<n;j++) m=(*(x+i*size+j)>m)?*(x+i*size+j):m;
return m;}
Вызов функции будет соответственно иметь вид:
1) max_el(b,SIZE)
2) max_el(b,SIZE)
3) max_el((int*)b,SIZE,SIZE)
Все функции в С являются рекурсивными, т.е. любая функция может вызвать любую функцию, в том числе и функцию main, а также саму себя.
В вызывающей функции (или вне функций) функцию можно описать как
тип имя ( список параметров ) ;
или
тип имя ( список типов параметров ) ;
Первый вариант описания отличается от заголовка функции только наличием “;” и называется прототипом функции, имена параметров могут быть произвольными, т.к. они никак не используются компилятором. Во втором варианте эти имена вообще опущены, например:
int max_el(int*a,int b,int c);
int max_el(int*,int,int);
Для полного описания функций, не имеющих параметров, список типов задается в виде (void) . Функции, расположенные в том же файле, до их вызова можно вообще не описывать, описанием будет служить заголовок функции. Если функция расположена после вызывающей функции или в другом файле, то ее можно описывать или не описывать. Но для неописанной функции компилятор не проверяет соответствие типов параметров и аргументов и соответствие количества параметров и аргументов, кроме того, тип неописанной функции всегда полагается равным int. Если функция описана, компилятор следит за правильностью списка аргументов.
Язык С++ не предусматривает автоматического преобразования в тех случаях, когда аргументы не совпадают по типам с соответствующими им параметрам. С++ требует, чтобы в модуле, где происходит обращение к функции (причем обязательно до соответствующего обращения) присутствовало либо определение этой функции, либо ее прототип. Таким образом, проверка соответствия типов параметров и аргументов всегда выполняется в С++ на этапе компиляции.
В С++ тип функции столь же важен, как и ее имя. Две функции могут иметь одинаковое имя, если имеется возможность различить их по сигнатуре. Сигнатура функции задается числом, порядком следования и типами ее параметров. Определение двух или более функций с одним и тем же именем называется перегрузкой функций, поскольку более поздние объявления перегружают, или перекрывают, более ранние. Рассмотрим следующую программу:
#include <stdio.h>
#include <string.h>
int func (int first){ return first*first;}
int func (unsigned first){ return -first*first;}
char func (char first){ return first+3;}
float func(float r){ return r*r;}
void main() { printf("%d ",func(4)); printf("%d ",func((unsigned)4));
printf("%c ",func('a')); printf("%f\n",func((float)1.2));}
Эта программа, в которой использованы четыре разные функции с именем func, является совершенно корректной и выведет на экран строку
16 -16 d 1.44
В языке С допускаются указатели на функции, им можно присвоить адрес функции и вызвать функцию по адресу, например:
int (*p)(int*,int,int)=&max_el; printf("%d",(*p)((int*)b,SIZE,SIZE));
Операции адрес & и значение * можно не записывать явно, они автоматически генерируются компилятором, т.е. приведенный выше фрагмент можно переписать в виде:
int (*p)(int*,int,int)=max_el; printf("%d",p((int*)b,SIZE,SIZE));
Используя указатели на функции, можно решать два типа задач:
1) присвоение указателю функции и использование указателя в качестве косвенной ссылки на функцию;
2) передача функций в качестве аргументов для других функций. Это позволяет создавать мощную систему функций, где частью функции является другая функция, выбранная из библиотеки подпрограмм.
