Министерство образования и науки Российской Федерации 3 страница. на вход программы поступают вещественные числа, программа вычисляет их сумму*/

/*prog14.c

на вход программы поступают вещественные числа, программа вычисляет их сумму*/

#include <stdio.h>

main()

{

float x, s = 0; /* переменная s является сумматором, в ней будет накапливаться сумма чисел, которые вводит пользователь; в ячейке x хранится очередное число, прочитанное программой */

int flag; /* в переменную flag помещается значение, которое возвращает функция scanf */

flag = scanf ( "%f", &x);

/*программа читает данные из файла, содержимое которого направляется в её входной поток; когда прочитан весь файл, функция scanf возвращает значение EOF, оно определяется в файле stdio.h*/

while ( flag != EOF )

{

s = s + x;

flag = scanf ( "%f", &x);

}

printf ( “ сумма введённых чисел равна %f\n”, s );

return 0;

}

Составим текстовый файл mydata.txt, содержащий следующие вещественные числа:

121.45 12.78 151.11 777.76

Откомпилируем программу prog14.c и запустим её, набрав в командной строке:

prog14.exe < mydata.txt

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

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

(8.1) на вход поступают два целых числа, программа определяет, равны они или нет;

(8.2) на вход поступают два разных вещественных числа, программа выводит на экран снечала большее число, а под ним меньшее;

(8.3) на вход поступают три вещественных числа, программа определяет, есть ли среди них положительные числа;

(8.4) на вход поступают три вещественных числа, программа определяет, есть ли среди них отрицательные числа;

(8.5) на вход поступают три целых числа, программа определяет, есть ли среди них совпадающие числа;

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

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

(8.8) на вход поступает целое положительное число n, программа вычисляет произведение чисел 1, 2,..., n (значение n! - n факториал);

(8.9) на вход поступают вещественные числа, программа вычисляет их среднее арифметическое;

(8.10) на вход поступают ненулевые целые числа, программа подсчитывет, сколко среди них положительных чисел и сколько отрицательных чисел;

(8.11) на вход поступают вещественные числа, программа определяет, упорядочены они по возрастанию или нет;

(8.12) на вход поступают вещественные числа, программа определяет, упорядочены они по убыванию или нет;

9. СИМВОЛЫ И СТРОКИ

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

/*prog15.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 (имя_переменной)

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

/*prog16.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. Строка не обязательно должна занимать весь массив – ее конец отмечает элемент с нулевым значением.

/*prog17.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, следовательно, строка сокращается. Откомпилировав и выполнив программу prog16.c, мы увидим, что она выводит на экран лишь 6 символов строки: “I am a”.

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

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

/*prog18.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;

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

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

scanf(“%s”, str);

/*prog19.c*/

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

#include <stdio.h>

#define N 1000

main()

{

char str[ N ];

scanf ( “%s”, str);

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

return 0;

}

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

/*prog20.c*/

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

#include<stdio.h>

#define N 1000

main()

{

char c, str[ N ]; /* в массиве str будет храниться строка, которую введёт пользователь, прочитанный символ строки программа будет помещать в ячейку c */

unsigned int i; /* переменная i будет индексом символа строки */

scanf ( “%s”, str);

i = 0;

c = str[ i ]; /*начальный символ строки помещается в ячейку c*/

while ( c != 0 )

{

i++;

c = str [ i ];

}

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

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

return 0;

}

Задания

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

<9.1> составьте программу, подсчитывающую количество символов в строке, которую ввёл пользователь (аналогичную программе prog20.c), причём вместо оператора цикла while используйте оператор цикла for;

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

<9.3> на вход поступают строка (без пробелов), программа выводит символы строки ‘в столбик’;

<9.4> на вход поступают строка (без пробелов), программа выводит символы строки, разделённые пробелами, например:

пользователь вводит: aabbc,

на экране появляется: a a b b c;

<9.5> на вход поступают строка (без пробелов) и символ, программа выводит на экран символы строки ‘лесенкой’, например:

пользователь вводит: aabbc,

на экране появляется:

a

a

b

b

c;

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

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

<9.8> на вход поступает последовательность слов, содержащихся в текством файле, программа подсчитывет, количество слов;

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

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

/*prog20.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 операций.

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

Наши рекомендации