Поможем написать учебную работу
Если у вас возникли сложности с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой - мы готовы помочь.
Если у вас возникли сложности с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой - мы готовы помочь.
Лабораторная работа №7 .
Функции. Механизм объявления, обращения, возврата параметров. Указатели на функции.
Цель работы. Изучить синтаксис работы с функциями. Научится использовать функции в программах.
Теоретическая часть.
Функции разбивают большие вычислительные задачи на маленькие подзадачи и позволяют использовать в работе то, что уже сделано другими, а не начинать каждый раз с пустого места. Соответствующие функции часто могут скрывать в себе детали проводимых в разных частях программы операций, знать которые нет необходимости, проясняя тем самым всю программу, как целое, и облегчая мучения при внесении изменений.
Язык Си разрабатывался со стремлением сделать функции эффективными и удобными для использования; Си-программы обычно состоят из большого числа маленьких функций, а не из нескольких больших.
Когда следует использовать функции:
Объявление функции.
Тип_возвращаемого_значения имя_функции(Тип аргумента 1, Тип аргумента 2,…);
Реализация функции:
Тип_возвращаемого_значения имя_функции(Тип аргумента 1 имя аргумента1 , Тип аргумента 2 имя аргумента2…){
// Тело функции.
return 0; // возвращаемое значение
}
Использование функции
переменная = имя_функции(имя аргумента1, имя аргумента2…);
причем тип аргументов должен совпадать с типом аргументов в объявлении и реализации функии.
Любая функция, кроме main{}, должна быть объявлена перед использованием. Т.е. объявление (или реализация) функции должна быть выше по тексту файла от места ее использования.
Первая функция, которую вы встретили при программировании на языке Си, это
int main(){
return 0;
}
По виду это реализация функции.
Int означает, что функция возвращает значение типа int.
() означают, что функция не принимает аргументов. Можно написать int main(void).
Оператор return используется для возвращения значения из функции.
Напишем реализацию своей функции суммирующей два 16 битных числа и возвращающую 32 битный результат.
int summa(short int a, short int b){
int summa = a + b;
return summa;
}
int main(void){
int c = summa(1,2);
printf("Сумма равна %d \n", c);// выведет 3
return 0;
}
Так как объявление функции в файле стоит выше места ее использования, то объявление можно опустить.
В следующем примере реализация функции, ниже места ее использования, поэтому необходимо записать объявление функции.
// объявление функции
int summa(short int , short int );
int main(void){
// использование функции
int c = summa(1,2);
printf("Сумма равна %d \n", c);
return 0;
}
//реализация функции
int summa(short int a, short int b){
int summa = a + b;
return summa;
}
Аргументы функции передаются по значению. Т.е. в функцию передается не сама переменная, а ее значение.
Рассмотрим пример возможной ошибки.
Задача поменять значения двух переменных друг с другом
//реализация функции
void change(int a, int b){
int temp = a;
a = b;
b = temp;
}
// использование функции
int a = 9;
int b = 3;
change(a,b);
printf("a равно %d b равно %d \n", a,b);
В этом примере после выполнения функции change(a,b); a и b не изменятся т.к. в функции создались свои переменные локальные переменные a и b, и они обменялись местами.
Для того чтобы исправить ошибку можно воспользоваться указателями на переменные.
Аргументы функции change теперь указатели на переменные
void change(int* a, int* b){
int temp = *a;
*a = *b;
*b = temp;
}
int a = 9;int b = 3;
change(&a,&b); // при использовании передаем в функцию адреса переменных
Указатели также используются при необходимости возвращения значений из функции.
Например, функцию суммирования, аналогичную, приведенной выше, можно записать так:
//реализация функции
void summa(short int a, short int b, int* summa){
*summa = a + b;
}
// использование функции
int c;
summa(1,2,&c);// передаем указатель на переменную
При написании функций пользуются следующими правилами хорошего стиля программирования:
Область видимости переменных
Область видимости имени (англ. scope) понятие, означающее места в исходном коде, в которых может использоваться данное имя.
В Си есть:
локальные переменные, которые «видны» внутри данной структуры функции или управляющего блока (цикла).
статические переменные, которые видны внутри данного файла.
глобальные, которые «видны» во всей программе.
Переменные объявленные внутри тела функции являются локальными, их значение исчезает при выходе из функции.
Указатели на функции.
Бывает удобно передать саму функцию, как аргумент другой функции.
Тогда необходимо создать указатель на функцию и передать его в качестве аргумента другой функции.
Указатель на функцию - переменная, которая содержит адрес некоторой функции. Соответственно, косвенное обращение по этому указателю представляет собой вызов функции.
В программе на С адресом функции служит ее имя без скобок и аргументов (это похоже на адрес массива, который равен имени массива без индексов).
Определение указателя на функцию имеет вид, например:
int (*p)(const char *, const char *);
Это объявление сообщает компилятору, что p это указатель на функцию, имеющую два параметра типа const char * и возвращающую значение типа int. Скобки вокруг p необходимы для правильной интерпретации объявления компилятором. Подобная форма объявления используется также для указателей на любые другие функции, нужно лишь внести изменения в зависимости от возвращаемого типа и параметров функции.
Рассмотрим пример, сравнения двух строк с использованием указателя на функцию
#include <stdio.h>
#include <string.h>
// объявление функции
void check(char *a, char *b,
int (*cmp)(const char *, const char *));
int main(void)
{
char s1[80], s2[80];
int (*p)(const char *, const char *);
/* указатель на функцию */
p = strcmp;
/* присваивает адрес функции strcmp указателю p */
printf("Введите две строки.\n");
gets(s1);
gets(s2);
check(s1, s2, p); /* Передает адрес функции strcmp
посредством указателя p */
return 0;
}
// реализация функции
void check(char *a, char *b,
int (*cmp)(const char *, const char *))
{
printf("Проверка на совпадение.\n");
int result_compare = (*cmp)(a, b); // вызов функции по указателю
if(!result_compare){
printf("Равны");
}else {
printf("Не равны");
}
}
Практическая часть.
Переписать программу с лабораторной по структурам, используя функции.
Программа разбита на логические блоки, с соответствующими комментариями. Сделаем из каждого логического блока функцию.
// объявление подключаемых файлов
#include <stdio.h>
#include <math.h>
//объявление именнованных констант
enum {
TAU,
Informatika,
NumberExamsSubject,
};
enum {
NumberStudents = 3,
};
// объявление собственных типов
typedef struct{
char name[40];
unsigned char examsMark[NumberExamsSubject];
float meanExamsMark;
}recordBook;
// объявление функций
void for_correct_work_printf(void);
void init_from_keyboard(recordBook* students);
void calc_mean_mark(recordBook* students);
float calc_mean_group(recordBook* students);
void calc_mean_student(recordBook* students, float mean_value, int* mean_index);
int main(void){
// для корректной работы printf и scanf
for_correct_work_printf();
// Инициализация в тексте программы
recordBook students[] = {
{"Иванов", {4,4}},
{"Петров", {5,5}},
{"Сидоров", {3,4}}
};
// инициализация с помощью клавиатуры
init_from_keyboard(&students[0]);
// Нахождение средней оценки по экзаменам
calc_mean_mark(&students[0]);
// Нахождение средней оценки по группе
float meanMarkGroup = calc_mean_group(&students[0]);
// Нахождение студента с оценкой самой близкой к средней
int mean_student_index=0;
calc_mean_student(&students[0], meanMarkGroup, &mean_student_index);
// Вывод результата на экран
printf(" Самый средний студент - %s",students[mean_student_index].name);
return mean_student_index;
}
void for_correct_work_printf(void){
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
}
void init_from_keyboard(recordBook* students){
int i = 0;
int j = 0;
for(i = 0; i < NumberStudents; i++){
printf("Введите фамилию %d студента \n", i+1 );
scanf("%s", (students+i)->name);
for(j = 0; j < NumberExamsSubject;j++ ){
printf("Введите оценку %d студента за %d предмет\n", i+1, j+1 );
scanf("%d", (int*)&((students+i)->examsMark[j]));
}
}
}
float calc_mean_group(recordBook* students){
int i = 0;
float meanMarkGroup = 0;
for(i = 0; i < NumberStudents; i++){
meanMarkGroup += (students+i)->meanExamsMark;
}
meanMarkGroup = meanMarkGroup/NumberStudents;
return meanMarkGroup;
}
void calc_mean_mark(recordBook* students){
int i = 0;
int j = 0;
for(i = 0; i < NumberStudents; i++){
int summa = 0;
for(j = 0; j < NumberExamsSubject;j++ ){
summa += (students+i)->examsMark[j];
}
(students+i)->meanExamsMark = (float)summa/NumberExamsSubject;
}
}
void calc_mean_student(recordBook* students, float mean_value, int* mean_index){
float difference = 0;
float min_difference = fabs(mean_value - students->meanExamsMark);// начальная разница
*mean_index = 0;
int i;
for(i = 1; i < NumberStudents; i++){
difference = fabs(mean_value - (students+i)->meanExamsMark);
if(difference < min_difference){
min_difference = difference;
*mean_index = i;
}
}
}