Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Visual_Studio_2010

.pdf
Скачиваний:
109
Добавлен:
03.03.2016
Размер:
5.94 Mб
Скачать

Задание3

1.Рассчитайте минимальные и максимальные значения сформированных случайных чисел.

2.Размерность массива случайных чисел примите за 100Х, где Х – номер компьютера, на котором выполняется лабораторная работа. Вывод на экран дисплея не производите.

3.С учетом п. 2 рассчитайте среднее значение сформированного массива. Сравните с теоретическим значением.

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

Пример 4. Написать программу заполнения одномерного символьного массива заданным числом (вводимым с клавиатуры) символов с добавлением символа восклицательного знака ! в конце массива.

Для решения примера используем функции динамического распределе-

ния памяти malloc() и realloc().

Программный код решения примера

#include <stdio.h> #include <conio.h> #include <stdlib.h>

#include <string.h>

int main(void) { int n, m; char *ptr;

// Размерность символьного массива

printf("\n Enter a dimention of character array: "); scanf_s("%d", &n);

_ flushall();

ptr = (char *)malloc((n+1)*sizeof(char)); // выделение памяти if (!ptr) {

printf("\n\t 1st Error! "); printf("\n\n Press any key: ");

_getch(); return -1; }

// Ввод строки символов

printf(" Enter a character array of no more than %d characters:

", n);

gets_s(ptr, n+1);

m = strlen(ptr); // число символов в строке printf("\n Start line:\n");

printf(" %s\n", ptr);

151

//Перераспределение памяти

ptr = (char *)realloc(ptr, (m+2)*sizeof(char)); if (!ptr) {

printf("\n\t 2nd Error! "); printf("\n\n Press any key: ");

_getch(); return -1;

}

//Присоединение к массиву символов еще одного символа strcat_s(ptr, m+2, "!");

printf("\n Start line and character \"%c\":\n", '!'); printf(" %s\n", ptr);

free (ptr); // освобождение памяти

printf("\n\n Press any key: ");

_getch(); return 0;

}

В функции malloc() размер требуемой памяти установлен с учетом символа окончания строки '\0'. Аналогично сделан запас количества символов для функции realloc(), так как функция strlen() возвращает количество символов в строке без нулевого символа.

Возможный результат выполнения программы представлен на рис.9.4.

Рис. 9.4. Пример определения динамического массива символов

Размерность выделяемой памяти превышает число вводимых символов, поэтому функция realloc() уменьшает ее.

Задание4

1.Проверьте размерность итогового массива символов.

2.Выведите сформированный массив символов в обратном порядке.

3.Осуществите вывод массива символов с дополнительным случайным символом без применения функции strcat_s().

152

Пример 5. Написать программу транспонирования матрицы, размерности которой (количество строк и количество столбцов) вводятся с клавиатуры, а элементы – вещественные случайные числа, распределенные по равномерному закону из интервала [0; 15].

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

Программный код решения примера

#include <stdio.h> #include <conio.h> #include <stdlib.h> #include <time.h> #include <locale.h>

int main (void) { int i, j, n, m;

double *A_ptr, *B_buf;

// Для рандомизации псевдослучайных чисел srand((unsigned)time(NULL));

setlocale(LC_ALL, "Russian");// русские шрифты

printf("\n Введите размерность матрицы - \n число строк и число столбцов через пробел: ");

scanf_s("%d%d", &n, &m);

A_ptr = (double *) calloc((n*m),sizeof(double)); B_buf = (double *) calloc((n*m),sizeof(double));

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

A_ptr[i] = 15.0*rand()/RAND_MAX; setlocale(LC_NUMERIC, "English"); // для десятичной точки printf("\n Исходная матрица:\n");

for (i = 0; i < n; ++i) { printf("\n"); for(j = 0; j < m; ++j)

printf(" %8.4f", A_ptr[i*m+j]); }

// Основной фрагмент транспонирования for (i = 0; i < n; ++i)

for (j = 0; j < m; ++j) B_buf[j*n+i] = A_ptr[i*m+j];

printf("\n\n Транспонированная матрица:\n"); for (j = 0; j < m; ++j) { printf("\n");

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

printf(" %8.4f", B_buf[j*n+i]); }

// Освобождение выделенной памяти free(A_ptr); free(B_buf);

printf("\n\n Press any key: ");

_getch(); return 0;

}

Возможный результат работы программы показан на рис. 9.5.

153

Рис. 9.5. Пример транспонирования числовой матрицы

Задание 5

1.Выполните двойное транспонирование исходной матрицы. Выведите на консоль результат первого и второго транспонирования.

2.Выполните решение примера с применением указателей на указатели.

3.Заполните исходную матрицу натуральными числами, начиная с номера компьютера, на котором выполняется лабораторная работа.

Контрольные вопросы

1.Что такое динамическая память?

2.Какие средства языка С используются для хранения данных с динамическим выделением памяти компьютера?

3.Какие основные библиотечные функции языка С используются для динамического распределения памяти?

4.Каково различие в действии функций malloc() и calloc()?

5.Как осуществляется перераспределение динамической памяти?

6.Для каких типов данных возможно динамическое распределение памяти?

7.В каком случае функции динамического распределения памяти возвращают нулевой указатель?

БИБЛИОГРАФИЧЕСКИЙ СПИСОК

1.Шилдт Г. Полный справочник по С : пер. с англ./Г. Шилдт. – 4-е изд. –

М. : Вильямс, 2007. – 704 с.

2.Керниган Б. У. Язык программирования С : пер. с англ./Б. У. Керниган, Д. М. Ритчи. – 2-е изд. – М. : Вильямс, 2007. – 304 с.

3.Шеннон Р. Имитационное моделирование систем – искусство и нау-

ка/Р. Шеннон. – М. : Мир, 1978. – 418 с.

154

Тема 10

ФУНКЦИИ ОБЩИЕ СВЕДЕНИЯ О ФУНКЦИЯХ ЯЗЫКА С

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

ТЕОРЕТИЧЕСКАЯ ЧАСТЬ

В основе всех программ на языке программирования С лежат одни и те же фундаментальные элементы – функции [1]. Например, к системным функ-

циям относятся printf(), scanf_s(), gets_s(), putchar() и др. Функ-

ции – это строительные элементы языка С и то место, в котором выполняется вся работа программы [2]. Большие программы обычно состоят из нескольких пользовательских и ряда системных функций. Функция – самостоятельная единица программы. Функции повышают уровень модульности программы, облегчают ее чтение, внесение изменений и коррекцию ошибок. В частности, функция main() является обязательной для любой программы. Во всех программах С определяется единая внешняя функция с именем main(), служащая точкой входа в программу, т. е. первой функцией, выполняемой после запуска программы [3].

Ни одна программа в языке С не может обойтись без функций.

Функция в языке С играет ту же роль, что и подпрограммы или процедуры в других языках [4]. Каждая функция имеет имя и список аргументов. По соглашению, принятому в языке С, при записи имени функции после него ставятся круглые скобки [4]. Так можно легко отличить имена переменных от имен функций.

Рассмотрим модель программы, в которой, кроме функции main(), содержатся еще три функции [4]. Код примера будет следующим:

#include <stdio.h>

int main(void) /* главная функция */

{

/* Начало тела функции */ function1(); /* вызов первой функции */ function2(); /* вызов второй функции */

function3(); /* вызов третьей функции */ } /* конец тела функции main() */

/* Начало определения первой функции */ function1() { /* начало тела первой функции */

155

/* Операторы первой функции */ /* Конец тела первой функции */

}

/* Начало определения второй функции */ function2()

{ /* Начало тела второй функции*/ /* Операторы второй функции */ /* Конец тела второй функции*/

}

/* Начало определения третьей функции */ function3()

{ /* Начало тела третьей функции*/ /* Операторы третьей функции */ /* Конец тела третьей функции*/

}

В представленной программе имеются четыре функции (main(), function1(), function2(), function3()), не содержащие аргументов. Позд-

нее рассмотрим функции, которые имеют аргументы. Аргументы функции – это величины, которые передаются функции во время ее вызова. Аргумент, стоящий в операторе вызова функции, называется фактическим параметром, аргументы, стоящие в заголовке функции, – формальными параметрами. В языке С функция может возвращать значение в вызывающую программу посредством оператора return. Оператор возврата из функции в точку вызова имеет две формы [5]:

return;

return выражение;

В общем виде функция выглядит следующим образом [1]:

возвр-тип имя-функции(список параметров)

{

Тело_функции

}

Тело_функции – это часть определения функции, ограниченная фигурными скобками и непосредственно размещенная вслед за заголовком функции. Оно может быть либо составным оператором, либо блоком [1]. В языке С определения функций не могут быть вложенными, т. е. внутри одной функции нельзя объявить и расписать тело другой функции.

Возвращаемый тип функции возвр-тип определяет тип данного, возвращаемого функцией. Это могут быть, например, int, float, double и т. д. В случае когда функция ничего не возвращает, ей присваивается тип void.

Функция может возвращать любой тип данных, за исключением массивов. Список параметров – это список, элементы которого отделяются друг от друга запятыми [1]. При вызове функции параметры принимают значения аргументов. Если у функции нет параметров, то такой пустой список можно ука-

156

зать в явном виде, поместив для этого в скобки ключевое слово void. Все параметры функции (входящие в список параметров) должны объявляться отдельно, причем для каждого из них надо указывать и тип, и имя. В общем виде список объявлений параметров должен выглядеть следующим образом [1]:

fun(тип имя_перем1, тип имя_перем2, , тип имя_перем N)

Например:

fun(int i, int j, float k, char str1, char str2)

Рассмотрим пример программы с выводом сообщения не в главной функции main(), а в другой.

#include <stdio.h> #include <conio.h>

void printMessage (void)

{

printf("\n\t hello, world\n"); return;

printf("\n\t 123\n");

}

int main(void)

{

printMessage();

printf("\n Press any key: "); _getch();

return 0;

}

Результат выполнения программы показан на рис.10.1.

Рис.10.1. Вывод сообщения с помощью двух функций

Программа состоит из двух функций: printMessage() и main(). Выполнение программы всегда начинается с функции main(), которую называют также главной. Внутри нее происходит вызов функции printMessage() без параметров. При этом выполнение программы передается непосредственно вызванной функции. Внутри функции printMessage() выполняется только утверждение

printf("\n\t hello, world\n");

157

Вфункции printMessage() есть также утверждение printf("\n\t 123\n"), но оно не выполняется, поскольку используется утверждение возврата из функции (return).

Вязыке С функция введена как один из производных типов.

Формальные параметры в определениях функций могут объявляться в форме прототипов, которые дают компилятору возможность тщательнее проверять типы аргументов [1; 3]. Если используются прототипы, то компилятор может обнаружить любые сомнительные преобразования типов аргументов, необходимые при вызове функции, если тип ее параметров отличается от типов аргументов. Компилятор также обнаружит различия в количестве аргументов, использованных при вызове функции, и в количестве параметров функции.

В общем случае прототип функции должен выглядеть таким образом [1]:

тип имя_функции(тип имя_пар1, тип имя_пар2, , тип имя_парN);

В приведенной программе прототип функции printMessage() не применялся, так как сама функция была объявлена до главной функции main(). Для переносимости С-кода в С++ использование прототипа функции обязательно. Поэтому хорошим стилем программирования можно считать работу с прототипами функций, поскольку большие программы обычно состоят из нескольких функций, часто расположенных в различных файлах.

Рассмотренная ранее программа с использованием прототипа функции printMessage() будет выглядеть следующим образом.

#include <stdio.h>

#include <conio.h>

//void printMessage (void);// прототип функции

int main(void)

{

void printMessage (void); // прототип функции printMessage(); // вызов функции

printf("\n Press any key: "); _getch();

return 0;

}

// Определение функции void printMessage (void)

{

printf("\n\t hello, world\n"); return;

printf("\n\t 123\n");

}

158

В листинге программы показаны две возможности применения прототипа функции printMessage(). При этом сама эта функция объявлена после функции main().

Формальные параметры функции определены в ее прототипе. При обращении к функции используются фактические параметры, называемые

аргументами функции.

Список фактических параметров – это список выражений, количество которых равно количеству формальных параметров функции (исключение составляют функции с переменным числом параметров). Соответствие между формальными и фактическими параметрами устанавливается по их взаимному расположению в списках. Между ними должно наблюдаться соответствие по типам.

Синтаксис языка С предусматривает только один способ передачи параметров – передачу по значениям. Формальные параметры функции локализованы в ней, т. е. недоступны вне определения функции, и никакие операции над формальными параметрами в теле функции не изменяют значений фактических параметров [4].

Передача параметров по значению предусматривает следующие шаги [4].

1.При компиляции функции выделяются участки памяти для формальных параметров, т. е. формальные параметры оказываются внутренними объектами функции. При этом для параметров типа float формируются объекты типа double, а для параметров типов char и short int – объекты типа int. Если параметром является массив, то создается указатель на начало этого массива, и он служит представлением массивапараметра в теле функции.

2.Вычисляются значения выражений, использованных в качестве фактических параметров при вызове функции.

3.Значения выражений – фактических параметров заносятся в участки памяти, выделенные для формальных параметров функции.

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

5.Никакого влияния на фактические параметры (на их значения) функция не оказывает.

6.После выхода из функции освобождается память, выделенная для ее формальных параметров.

Важно то, что объект вызывающей программы, использованный в качестве фактического параметра, не может быть изменен из тела функции. Но существует косвенная возможность изменять значения объектов вызывающей программы действиями в вызванной функции. Это реализуется с помощью указателя (указателей), когда в вызываемую функцию передается адрес любого объекта из вызывающей программы. С помощью выполняемого в тексте функции разыменования указателя осуществляется доступ к адресуемому указателем объекту из вызывающей программы. Тем самым, не изменяя

159

параметра (указатель-параметр постоянно содержит только адрес одного и того же объекта), можно изменять объект вызывающей программы.

Массивы и строки также могут быть параметрами функции. В этом случае внутрь функции передается только адрес начала массива. Тогда можно в качесстве параметра использовать указатель. Приведем два равноправных прототипа функций:

float fun(int n, float A[ ], float B[ ]); float fun(int n, float *a, float *b);

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

В языке С существует возможность создавать функции, число аргументов которых не определено – функции с переменным числом аргументов [7]. При этом следует указать только количество аргументов. Пример прототипа такой функции:

int fun(int n, );

Многоточие означает, что функция получает переменное число аргументов любого типа. Многоточие должно всегда находиться в конце списка параметров [7].

Макросы и определения заголовочного файла переменных аргументов stdarg.h (табл. 10.1) предоставляют программисту средства, необходимые для построения функций со списком аргументов переменной длины [7].

 

Таблица 10.1

 

Макросы заголовочного файла stdarg.h

 

 

Идентификатор

Объяснение

 

 

va_list

Тип, предназначенный для хранения информации, необходимой

 

макросам v_start, va_arg и va_end. Чтобы получить доступ к

 

аргументам в списке переменной длины, необходимо объявить объект

 

типа va_list

 

 

va_start

Макрос, который вызывается перед обращением к аргументам списка

 

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

 

помощью va_list, для использования макросами va_arg и va_end

 

 

va_arg

Макрос, расширяющийся до выражения со значением и типом

 

следующего аргумента в списке переменной длины. Каждый его вызов

 

изменяет объект, объявленный с помощью va_list, так, что объект

 

указывает на следующий аргумент списка

 

 

va_end

Макрос обеспечивает нормальный возврат из функции, на список

 

аргументов которой ссылается макрос va_start

 

 

160

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]