Министерство образования и науки Российской Федерации 3 страница

 

/*prog11.c*/

 

#include<stdio.h>

 

main()

{

char x = ‘A’, y = 97;

printf (“Численное значение переменной х равно %d, в ней хранится символ %c \n Численное значение переменной у равно %d, в ней хранится символ %с \n”, x, x, y, y);

return 0;

}

 

Откомпилировав и выполнив программу мы увидим, что буква ‘А’ имеет ASCII-код 65, а ASCII-коду 97 соответствует строчная буква ‘а’.

Таким образом, мы можем присваивать целочисленные значения переменным типов int и char, выполнять с ними обычные арифметические действия, представлять в десятичной системе счисления. Однако переменные этих типов занимают разный объем памяти. Операция

 

sizeof (имя_переменной)

 

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

 

/*prog12.c*/

 

#include<stdio.h>

 

main()

{

int x = 97;

char y = 97;

printf (“Переменной х типа int присвоено значение %d, она занимает %d байт Переменной у типа char присвоено значение %d, она занимает %d байт ”, x, sizeof(x), y, sizeof(y));

return 0;

}

 

При вызове функции printf сначала вычисляются значения всех её аргументов - x, sizeof(x), y, sizeof(y) – а затем операционная система выводит их на экран.

 

Строка в языке С представляет собой массив, образованный следующими друг за другом переменными типа char. Они нумеруются целыми числами (индексами массива), начиная с нуля. Спецификация формата ввода-вывода строки - %s. Строка не обязательно должна занимать весь массив – ее конец отмечает элемент с нулевым значением.

 

/*prog13.c*/

 

#include<stdio.h>

 

main()

{

char str[20] = “I am a student”;

str[6] = 0;

printf( “%s \n”, str );

return 0;

}

 

Прежде всего, обратим внимание на объявление массива str. Сначала идёт имя типа элемента массива – char, затем имя массива – str и модификатор типа – квадратные скобки. В языке С квадратные скобки интерпретируются как операция доступа к элементу массива: str[6] = 0 – элементу массива str с индексом 6 присвоить значение 0. Объявление массива следует понимать так: выполнив с переменной str операцию доступа к элементу массива, мы получим значение типа char. Число в квадратных скобках при объявлении массива равно количеству его элементов; так как индекс начального элемента 0, то индекс последнего элемента массива равен 19. В строке “I am a student” 11 букв и три пробела – всего 14 символов, да еще символ с нулевым значением, завершающий строку – получается, что длина строки составляет 15 символов, последний байт массива не используется. В теле функции элементу с индексом 6 присваивается значение 0, следовательно, строка сокращается. Откомпилировав и выполнив программу prog5.c, мы увидим, что она выводит на экран лишь 6 символов строки: “I am a”.

 

Есть еще один способов объявления массива. На самом деле в переменной str хранится адрес начального элемента массива, т.е. str – это ссылка на переменную str[0].

 

Значение адреса можно получить с помощью операции разадресации, она обозначается символом *.

 

/*prog14.c*/

 

#include<stdio.h>

 

main()

{

char *str = “I am a student”;

str[7]=‘S’;

printf(“%s \n”, str);

return 0;

}

 

После выполнения программы на экране появляется сообщение “I am a Student”. Таким образом, объявления

 

char str[20];

 

char *str;

 

можно считать равноправными. На самом деле это не так – в первом случае мы точно знаем, что нам выделено место в памяти для размещения двенадцати элементов, во втором дело обстоит гораздо сложнее.

 

В программах prog9.c и prog10.c мы присваивали строке значение при помощи инициализации, а затем изменяли ее отдельные символы. Использовать операцию = для изменения самой строки в теле функции нельзя, это делает специальная библиотечная функция. Зато при вводе строки не надо выполнять операцию ссылки:

scanf(“%s”, str);

 

/*prog15.c*/

 

/* Программа выводит на экран строку (без пробелов), которую ввёл пользователь */

 

#include <stdio.h>

#define N 1000

 

main()

{

char str[ N ];

scanf ( “%s”, str);

printf(“%s \n”, str);

return 0;

}

 

Переменная str в программе prog15 является буфером – областью памяти для временного хранения введённой информации. Обычно объём требуемой памяти для буфера известен лишь приблизительно, поэтому его выделяют с некоторым запасом. Числовые значения, которые используются в программе, следует задавать с помощью директивы препроцессору define, в этом случае уменьшается вероятность ошибок в случае их изменения.

 

/*prog16.c*/

 

/* Программа подсчитывает количество символов в строке (без пробелов), которую ввёл пользователь */

 

#include<stdio.h>

#define N 1000

 

main()

{

unsigned int i = 0, count = 0; /* переменная i будет индексом массива символов (строки), а переменная count представляет собой счётчик, в ней будет накапливаться количество символов в строке, введённой пользователем */

 

char c, str[ N ];

scanf ( “%s”, str);

 

c = str[ 0 ];

while ( c != 0 )

count++;

 

printf(“%u \n”, count );

return 0;

}

 

Задания

 

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

 

<9.1> на вход поступают строка (без пробелов) и символ, программа подсчитывет, сколько раз символ встречается в строке;

 

<9.2> на вход поступают строка (без пробелов) и два символа, программа определяет, какой из них встречается в строке чаще;

 

 

ПЕРЕДАЧА ПАРАМЕТРОВ В КОМАНДНОЙ СТРОКЕ

 

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

 

/*prog17.c*/

 

#include<stdio.h>

main(int argc, char* argv[] )

{

printf(“Количество слов в командной строке равно %d \n”, argc);

if ( argc > 1)

printf( “Первый аргумент программы - %s \n”, argv[ 1 ] );

else

printf ( “Вы запустили программу %s без аргументов \n”, argv[0] );

return 0;

}

 

Теперь функция main вызывается с двумя аргументами, их передает программе операционная система. Традиционно они называются argc и argv. Аргумент argc относится к типу int, это – целое число. Значением аргумента argc служит количество слов в командной строке при вызове программы, не считая приглашения командной строки. Слова отделяются друг от друга пробелами и знаками табуляции (Tab), а не запятыми, точками или другими знаками препинания. Первое слово – имя исполняемого файла, следовательно, значение argc всегда больше нуля. Команда

 

printf(“Количество слов в командной строке равно %d \n”, argc);

 

выводит на экран значение аргумента argc. Если программа запущена так:

 

prog6.c aa bb cc

 

то значение argc должно быть равно 4.

 

Аргумент argv объявлен при помощи ключевого слова char и двух модификаторов типа - * и []:

 

сhar* argv[];

 

Символ * можно отделять от слов char, argv – пробелами или писать слитно с тем или другим словом. Напомним, что модификаторы типа интерпретируются как операции, но в отличие от бинарных операций (сложение, умножение), у которых два операнда, эти операции являются унарными – у них только один аргумент. В языке С операции доступа к элементу массива ([]) и разадресации (*) выполняются справа налево. Таким образом, если мы осуществляем доступ к элементу массива argv, то получим значение типа char* - строку символов. Отсюда следует, что argv – массив строк, т.е. каждый элемент массива представляет собой цепочку символов, которая завершается нулевым значением. Количество элементов массива не указывается – его определяет операционная система. Значениями элементов массива argv служат слова командной строки: argv[0] - имя исполняемого файла, argv [1] – первый аргумент и т.д., к каждому слову операционная система добавляет завершающий нуль. Команда

 

printf( “Первый аргумент программы - %s \n”, argv[1] );

 

выводит на экран первый аргумент программы - argv[1].

 

Принцип надежности работы программы требует, чтобы при запуске программы без аргументов предыдущая команда не выполнялась. Для этого мы используем управляющую конструкцию с ключевым словом if – если ( ветвление). Программа проверяет истинность условия, в нашем случае – верно ли то, что argс > 1. Если условие истинно, то выполняется следующая за ней команда, если нет, и присутствует ключевое слово else – то выполняется команда, стоящей после этого слова. Затем управление передается команде, следующей за ветвлением. Команда

 

printf(“Вы запустили программу %s

без аргументов \n”, argv[0]);

 

выводит сообщение с указанием имени исполняемого файла.

 

Задание.

 

Составьте программу, которая выводит два первых аргумента, переданных ей при запуске слитно, раздельно и в столбик (один под другим). Не забудьте корректно обработать случай запуска программы с меньшим количеством аргументов.

 

МАТРИЦЫ

 

Матрицей называется квадратная или прямоугольная числовая таблица. Элементы матрицы объединяются в горизонтальные строки и вертикальные столбцы; в языке С строки и столбцы матрицы нумеруются целыми числами 0, 1, 2,.. Порядок матрицы представляют в виде m * n, где m – количество строк, n – количество столбцов. Элемент матрицы а, стоящий на пересечении i-той строки и j-того столбца обозначается символом a [ i ] [ j ]; если а – матрица порядка m * n, то i = 0, 1,.., m - 1, j = 0, 1,…, n - 1. Значения i и j называются индексами элемента a [ i ] [ j ].

Рассмотрим следующую программу:

 

/* matrix1

программа выделяет область памяти для матрицы а порядка 1000*1000; после запуска программы пользователь вводит значение элемента a [ 0 ] [ 0 ], которое затем выводится на экран*/

 

# include<stdio.h>

# define M 1000

# define N 1000

 

main()

{

int a [ M ] [ N ]; //объявляет матрицу а

int i = 0, j = 0;

scanf ( “%d”,&( a [ i ] [ j ] )); //читает значение элемента а [ i ] [ j ], i = j = 0

printf ( “ %d\n”, a [ i ] [ j ] ); // выводит на экран значение элемента а [ i ] [ j ], i = j = 0

return 0;

}

 

Количество строк и столбцов матрицы а определены при помощи директивы препроцессора # define; нулевые значения индексам i, j присвоены в ходе их объявления ( напомним, что этот механизм называется инициализацией переменных).

 

Задание. Откомпилируйте и выполните программу matrix1.

 

Большие программы очень трудно понять, поэтому в них всегда присутствуют комментарии, содержащие необходимые разъяснения. Мы уже отмечали, что комментарии располагаются между специальными символами /* и */; если комментарий занимает часть строки, то перед ним ставят символ //, конец комментария в этом случае совпадает с концом строки.

 

Как правило, программу составляют так, чтобы она обрабатывала матрицы разных порядков. В этом случае количество строк и столбцов вводит пользователь. Выполнять однотипные операции с элементами строки и столбца удобнее всего с помощью оператора цикла for.

 

/* matrix2

После запуска программы пользователь вводит количество строк и столбцов матрицы, а затем ее элементы; программа выводит на экран транспонированную матрицу (напомним, что операция транспонирования матрицы заменяет её строки столбцами)*/

 

// Директивы препроцессора

 

# include<stdio.h>

# define M 1000

# define N 1000

 

main()

{

// объявление переменных

int a [ M ] [ N ];

int m, n, i, j;

// программа читает количество строк и столбцов матрицы а

scanf ( “ %d %d”, &m, &n);

//читает значения элементов матрицы а

for (i = 0; i < m; i++)

for ( j = 0; j < n; j++)

scanf ( “ %d”, &( a [ i ] [ j ] ) );

// выводит элементы матрицы на экран

for ( j = 0; i < n; i++)

for ( i = 0; j < m; i++)

printf(“ %d ”, a [ i ] [ j ] );

return 0;

}

 

Как и в программе matrix1, программа выделяет область памяти, в которой можно разместить матрицу порядка 1000*1000. предполагается, что значения m и n, которые введет пользователь, меньше, чем 1000. Таким образом, часть выделенной памяти не используется. С помощью более сложных механизмов языка С можно каждый раз выделять такую область памяти, в которой можно разместить матрицу порядка m*n.

Оператор цикла for ( i = 0; i < m; i++) сначала присваивает переменной i значение 0, затем проверяет условие i < m. Если неравенство справедливо, выполняется команда, непосредственно следующую за конструкцией for. После этого оператор цикла увеличивает значение i на 1 (выполняет операцию i++, которая называется инкрементом), снова проверяет условие i < m и т.д. Как только условие i < m перестанет быть справедливым, выполнение команды, непосредственно следующей за конструкцией for, прекращается и управление передается команде, стоящей далее.

НЕ НАДО СТАВИТЬ ТОЧКУ С ЗАПЯТОЙ ПОСЛЕ КОНСТРУКЦИИ for !!!

Команда, непосредственно следующая за оператором for, также является циклом. С ее помощью программа повторяет команды – читает элементы строки матрицы а; когда строка прочитана, то внешний оператор цикла for ( i = 0; i < m; i++) осуществляет переход к чтению следующей строки. Когда оба цикла завершатся, будут прочитаны все строки исходной матрицы. Точно также организован вывод элементов матрицы на экран.

 

Отметим, что команда, следующая за оператором цикла, образует с ним единый блок, поэтому ее набирают с отступом на 2-4 интервала.

 

Задание. Откомпилируйте и выполните программу matrix2

 

Выполнив программу, мы убедимся, что результат представлен на экране в виде одной числовой строки. Для того, чтобы на экран выводилась матрица в прямоугольном виде, необходимо вывод каждой строки завершать символом переноса ‘\n’

 

for ( j = 0; j < m; j++)

{

for ( i = 0; i < n; i++)

printf ( “%d ”, a [ i ] [ j ] );

printf ( “ \n ”);

}

 

(после %d в команде printf обязательно оставьте пробел, иначе числа будут выводиться без разделителей).

 

Задание. Дополните программу и выполните ее.

 

Как говорилось выше, оператор цикла for управляет выполнением непосредственно следующей за ним команды. В нашем случае он должен обеспечить выполнение двух команд: вывод элементов строки матрицы и переход на новую строку. Для этого обе команды объединяются в одну при помощи фигурных скобок {…}.

 

Язык С предназначен в первую очередь для разработки программ, которые выполняются на компьютерах, обладающих очень высокой производительностью. Здесь машинное время очень дорого, поэтому ввод данных осуществляется не с клавиатуры, а из файла. Составьте текстовый файл mydata.txt, содержащий следующие данные

 

3 4

1 2 3 4

4 3 1 2

17 18 19 20

 

Первые два числа задают количество строк и столбцов матрицы, а далее следует сама матрица. Откомпилируйте программу matrix2; пусть исполняемый файл называется matrix2.exe. Запустите программу, набрав в командной строке

 

matrix2.exe < mydata.txt

 

Символ < указывает операционной системе, что стандартный канал ввода исполняемого файла matrix2.exe следует соединить с файлом mydata.txt

 

Задание. Выполните программу так, чтобы исходные данные были прочитаны из текстового файла.

 

 

РАЗРАЬРТКА ПРОГРАММЫ

 

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

 

Пусть наша задача формулируется следующим образом: после запуска программы пользователь вводит матрицу порядка m * n; программа изменяет порядок элементов каждого столбца на противоположный.

 

Программы matrix1, matrix2 могут служить макетами. Следующий макет – программа matrix3

 

/* matrix3

после запуска программы пользователь вводит количество строк и столбцов матрицы, а затем ее элементы;

программа меняет местами первый и последний элементы каждого столбца и результат выводит на экран*/

 

// Директивы препроцессора

# include<stdio.h>

# define M 1000

# define N 1000

 

main()

{

// объявление переменных

int a [ M ] [ N ];

int m, n, i, j;

 

int buf;

//выделяется буфер для временного хранения промежуточных результатов

 

scanf(“%d % d”,&m, &n);

//программа читает количество строк и столбцов матрицы,

 

// программа читает элементы матрицы

for ( i = 0; i < m; i++ )

for ( j = 0; j < n; j++ )

scanf ( “%d”,& ( a [ i ] [ j ] ));

 

// программа меняет местами первый и последний элемент каждого столбца

for ( j = 0; j < n; j++ )

 

{ buf = a [ 0 ] [ j ];

// значение первого элемента столбца помещаем в буфер buf

 

//значения первого и последнего элемента столбца j меняются местами

a [ 0 ] [ j ] = a [ m ] [ j ];

a [ m ] [ j ] = buf;

}

 

// программа выводит матрицу-результат на экран

for ( i = 0; i < m; i++ )

for ( j = 0; j < n; j++)

printf ( “%d ”, a [ i ] [ j ] );

return 0;

}

 

Задание. Откомпилируйте и выполните программу matrix3, проверьте правильность результата и внесите в её текст необходимые изменения.

 

Теперь мы можем составить завершающую программу-макет. Она меняет местами элементы a [ i ] [ j ] и a [ к ] [ j ], сначала i равно 0, а к равно n - 1, затем каждый раз значение i увеличивается на 1, а значение к уменьшается на 1 до тех пор, пока I будет меньше к.

НЕ ЗАБУДЬТЕ ОБЪЯВИТЬ НОВУЮ ПЕРЕМЕННУЮ к

 

…………………………………………………………

 

for ( j = 0; j < n; j++ )

for ( i = 0; к = n - 1; i < к; i++, к--)

{ buf = a [ i ] [ j ];

a [ i ] [ j ] = a [ к ] [ j ];

a [ к ] [ j ] = buf;

}

 

Напомним, что операции, которые отделяются друг от друга запятой, в языке С образуют единое целое; таким образом, в начале цикла for переменной i присваивается значение 0, а переменной к - значение n - 1. После каждого прохода цикла значение переменной i увеличивается на 1, а значение переменной к уменьшается на 1 (выполняется операция к--, которая называется декрементом).

 

 

ОПТИМИЗАЦИЯ И ДОКУМЕНТИРОВАНИЕ ПРОГРАММЫ

 

Пример. Натуральное число называется совершенным, если оно равно сумме всех своих делителей, включая 1 и исключая само число. Составьте программу для вычисления совершенных чисел.

 

Постановка задачи. На вход поступают два натуральных числа, a и b, программа находит все совершенные числа, заключённые между a и b.

 

В качестве имени файлов будем использовать аббревиатуру (сокращение слов) perfnum: по-английски совершенное число - perfect number.

 

Составление теста программы. Перебирая натуральные числа, замечаем, что число 6 является совершенным: 6 = 1 + 2+ 3. Файл, содержащий исходные данные:

 

 

a=2 b=10

 

назовём perfnum.txt

 

Результатом выполнения программы должно стать число 6.

 

Макеты программы

 

Макет № 1

 

/*perfnum1.c*/

/*программа читает данные, содержащиеся в тестовом файле и выводит их на экран*/

 

#include <stdio.h>

 

main()

{

int a,b;

scanf (" a=%d b=%d", &a, &b);

printf (" a=%d b=%d\n");

return 0;

}

 

Программа читает из входного потока содержимое файла. Команда запуска откомпилированной программы:

 

perfnum.txt > perfnum.exe

 

Макет № 2

 

/*perfnum2.c*/

/*программа читает числа a,b, содержащиеся в тестовом файле и выводит на экран все делители чисел a и b*/

 

#include <stdio.h>

 

main()

{

int a,b;

int j;

 

scanf (" a=%d b=%d", &a, &b);

printf ("divisors of a: \n")

for ( j = 1; j < a; j++)

if ( a % j == 0 )

printf (" %d ", j);

printf (" /n divisors of b: \n")

for ( j = 1; j < b; j++)

if ( b % j == 0)

printf ("%d ", j);

return 0;

}

 

Программа вычисляет остатки от деления числа a на 1, 2,..., a-1. Если остаток от деления a на j равен нулю, то число j является делителем a, оно выводится на экран. Аналогично вычисляются делители числа b.

 

 

Макет № 3

 

/*perfnum3.c*/

/*программа читает числа a,b, содержащиеся в тестовом файле и выводит на экран все совершенные числа, заключённые между a и b*/

 

#include <stdio.h>

 

 

main()

{

int a, b;

int i, j, s;

 

scanf ( " a=%d b=%d", &a, &b);

 

for ( i = a; i <= b; i++)

{ // перебираем числа, заключённые между a и b

 

s = 0; //инициализация сумматора s

for ( j = 1; j < i; j++)

if ( i % j == 0) s+=j;

/* После завершения цикла значение переменной s равно сумме всех делителей числа i */

if ( i == s)

printf ("%d ", i);

}

return 0;

}

 

Эта программа находит все совершенные числа, заключённые между a и b. Однако она обладает существенным недостатком. Программа, которая выполняется на компьютере, занимает его ресурсы - процессор, оперативную память, каналы ввода-вывода, файлы, внешние устройства и т.д. Основная адача разработчика - рациональное использование ресурсов. Обычно затраты ресурсов оцениваются относительно некоторого характерного параметра программы. Будем считать, что a=1, число b будет характерным параметром, обозначим его символом n. Для того, чтобы найти все совершенные числа, не превосходящие n, программа выполняет 1+2+...+n-1 проходов цикла: она перебирает числа от 1 до n и у k-го числа проверяет k-1 чисел, выбирая из них делители k;1+2+...+n-1 = n * ( n – 1) / 2, следовательно, программа выполняет порядка n * n операций.

 

Решая задачу, человек обычно автоматически старается делать как можно меньше усилий. Поэтому разработчики часто 'проигрывают' программу - один из них имитирует решение задачи, а другой тщательно фиксирует все его действия.

 

Проверим, является ли число 255 совершенным. Число 1 является делителем любого числа, поэтому сразу можно поместить в переменную s значение 1; 255 - нечётное число, следовательно, оно не имеет чётных делителей, так что будем искать делители только среди нечётных чисел. 255 делится на 3, значит оно имеет ещё один делитель – 255 / 3 = 85; s = 1+ 3 + 85 + ... Cледующая пара делителей - 5 и 51: s = 1 + 3 + 85 + 5 + 51 +... Точно так же находим ещё два делителя - 15 и 17. Следующий делитель 17 у нас уже есть, перебор прекращается, s = 1 + 3 + 85 + 5 + 51 + 15 + 17 = 177 < 255, число 255 не является совершенным.

 

Теперь проверим, является ли совершенным число 16: это число чётное, оно делится на 2 и 4, 16 / 2 = 8, 16 / 4 = 4, два делителя совпадают, поиск делителей прекращается.

 

Обобщая наши исследования, можно сказать, что в том случае, когда число p является делителем числа q, то q делится без остатка на q / p, один из этих делителей не превосходит корня квадратного из q, а другой не меньше этого значения. Следующий макет программы выглядит так:

 

Макет № 4

 

/*perfnum4.c*/

/*программа читает числа a,b, содержащиеся в тестовом файле и выводит на экран все совершенные числа, заключённые между a и b*/

 

#include <math.h> /*заголовочный файл math.h содержит описания математических функций */

 

#include <stdio.h> /*файл содержит описания средств ввода-вывода*/

 

main()

{

int a, b;

int i, j, k,s;

int p, q;

float m;

 

scanf ( " a = %d b = %d", &a, &b);

 

if ( b < 6 )

{

printf ("NO PERFECT NUMBERS\n");

return 0;

}

 

if ( a % 2 ==0 ) // Если a чётное число

{ p = a; q = a+1; }

else

{ q = a; p = a +1; }

 

 

for ( i = p; i <= b; i = i+2 )

{ // перебираем чётные числа, заключённые между a и b

 

s = 1; //инициализация сумматора s

m = sqrt ( i ) + 1; // будем искать те делители числа i, которые не превосходят корня квадратного из i

 

 

for ( j = 2; j <= m ; j++)

if ( i % j == 0)

{

s+= j;

 

if ( (k = i / j) < j) s+= k;

}

/* После завершения цикла значение переменной s равно сумме всех делителей числа i */

 

if ( i == s)

printf ("%d ", i);

}

 

 

for ( i = q; i <= b; i = i + 2 )

{ // перебираем нечётные числа, заключённые между a и b

 

s = 1; //инициализация сумматора s

 

m = sqrt ( i ) + 1; // будем искать те делители числа i, которые не превосходят корня квадратного из i

 

for ( j = 3; j <= m; j = j + 2) /*ищем только нечётные делители*/

 

if ( i % j == 0)

{

s+= j;

if ( (k = i / j) < j) s+=k;

}

/* После завершения цикла значение переменной s равно сумме всех делителей числа i */

if ( i == s)

printf ("%d ",i);

}

return 0;

}

 

Вычисление корня квадратного требует значительного времени, поэтому мы введём две целых переменных - m и sqm. Когда начинается выполнение программы, их значения равны, оответственно, (int)sqrt (a) + 1 и m * m. Как только i становится больше sqm, программа прибавляет к sqm 2 * m + 1 и увеличивает m на единицу. Таким образом, m равно наименьшему натуральному числу, квадрат которого больше i.

 

Макет № 5

 

/*perfnum5.c*/

/*программа читает числа a,b, содержащиеся в тестовом файле и выводит на экран все совершенные числа, заключённые между a и b*/

 

#include <math.h> /*файл содержит описания математических функций */

#include <stdio.h> /*файл содержит описания средств ввода-вывода*/

 

 

main()

{

unsigned int a,b;

unsigned int i, j, k,s;

unsigned int p,q;

unsigned int m, sqm;

 

scanf ( " a = %d b = %d", &a, &b);

 

if ( b < 6 )

{

printf ("NO PERFECT NUMBERS\n");

return 0;

}

if ( a % 2 ==0 ) // Если a чётное число

{ p=a; q=a+1; }

else

{ q=a; p=a+1; }

 

m = (int) sqrt (p) + 1; sqm = m * m;

 

for (i = p; i <= b; i = i+2 )

{ // перебираем чётные числа, заключённые между a и b

s = 1; //инициализация сумматора s

 

for ( j = 2; j <= m ; j++)

if ( i % j == 0 )

{

s+= j;

if ( ( k = i / j) < j ) s+= k;

}

/* После завершения цикла значение переменной s равно сумме всех делителей числа i */