Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
1_Линейные программы (МУ к занятию).doc
Скачиваний:
1
Добавлен:
01.05.2025
Размер:
985.6 Кб
Скачать

Приложение 3. Правила оформления текстов программ

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

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

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

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

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

При написании программ обычно используется принцип «лишь бы работало», т. е. совершенно игнорируются элементарные правила оформления текстов программ. Исходный текст программы – это такой же текст, как стихотворение, эссе, рассказ, web-страничка. Посмотрим на фрагмент сильно искаженного текста (рис. 3).

Есть много ситуа ций при кот о рых

Студен там ну ж но написать т у ил и

ин ую прог рамму на т ом и л и

Ино м алго ритми ческом языке

Рис. 3. Фрагмент искаженного текста

Для грамотного написания программы необходимо форматировать исходные тексты. Вспомним, что стандартный размер экрана – 80 х 25 символов (знакомест). Именно такие размеры наиболее комфортны для восприятия.

Правило 1, про количество операторов на одной строке

Количество операторов на строке должно быть равно одному.

Неправильно:

а = b; do while (0); printf("MIEE");

Правильно:

a = b;

do while (0);

printf(MIEE");

Правило 2, про горизонтальные отступы

Все операторы, входящие в составной оператор (напоминаем, составной оператор – это все, что между begin и end в языке программирования Turbo Pascal и между { } в языке программирования С), должны быть сдвинуты вправо (клавиша Tab). Кроме того, размер отступа должен быть восемь символов.

Неправильно:

if (a < b) {

j = m;

i = 1;

for (k = 0; k < N; k++) {

c[k] += (m-1);

u[k-j] = 2*j;

if(1>a) {

break;

};

};

printf("ooo");

};

Правильно:

if (a < b) {

j = m;

i = 1;

for (k = 0; k < N; k++) {

c[k] += (m-1);

u[k-j] = 2*j;

if(1>a) {

break;

};

};

printf("ooo");

};

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

Правило 3, про операторные скобки

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

Неправильно:

for (;;) {

printf("Кафедра ИСТ");

};

for j:=0 to N do begin

WriteLn(‘ Кафедра ИСТ ');

end;

Правильно:

for (;;) {

printf("Кафедра ИСТ");

};

for j:=0 to N do begin

WriteLn(‘ Кафедра ИСТ ');

end;

Допускается такой вариант:

for (;;)

{

printf("истина");

};

for j:=0 to N do

begin

WriteLn('истина');

end;

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

Неправильно:

int my_favorite_function(void) {

return 1;

}

function MyFavoriteFunction:boolean; begin

MyFavoriteFunction := TRUE;

end;

Правильно:

int my_favorite_function(void)

{

return 1;

}

function MyFavoriteFunction:boolean;

begin

MyFavoriteFunction := TRUE;

end;

Начинающие программисты обычно набирают текст последовательно, и поэтому:

а) он у них получается кривой и

б) они постоянно забывают закрыть блок или закрывают его не там, где надо.

Чтобы избежать этого, текст программы нужно набирать не последовательно. К этому располагает сама грамматика и синтаксис почти любого языка программирования. Всегда есть ПАРНЫЕ символы – скобки, составные операторы. Если написали begin, то почему бы сразу не написать end? Если набрали открывающую скобку, то почему бы сразу не набрать закрывающую, а потом не вернуться назад?

Например:

for (i = 0; i < N; i++) {

/* тело цикла */

}

Правило 4, про горизонтальные пробелы

В конце строки пробелов быть не должно. Пробелы полезны вокруг знака какой-нибудь операции.

for (k = NUM; k; k--) {

int tmp;

tmp = some_func(k, arg2, arg3);

printf("%d\n", tmp);

}

Правило 5, про вертикальные пробелы (пустые строки)

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

Неправильно:

int func_1(void)

{ return 1;

}

int func_1(void)

{ return 2;

}

int main(void) {

LIST *1 = get_head();

double a, b;

printf("Hello.\n");

for (1;1->next;1=1->next)

printf("%s\n", 1->name);

};

printf("Bye.\n");

}

Правильно:

int func_1(void)

{

return 1;

}

int func_1(void)

{

return 2;

}

int main(void)

{

LIST *1 = get_head();

double a, b;

printf("Hello.\n");

for (1; 1->next; 1 = 1->next)

printf("%s\n", 1->name);

printf("Bye.\n");

}

Правило 6, про объявления переменных

Тип у переменной следует писать для каждой из них. На одной строчке – одна переменная.

Неправильно:

int a,b,c;

float *q,w,u,z,tmp,ttt,qqq,aaa,bbb;

……..

a,b,c,d,ttt,test,tt0: integer;

q,w,u,z,ppp,ddd,dddO,ddd23: real;

Правильно:

int a;

int b;

int c;

float *q;

……..

a: integer;

b: integer;

c: integer;

q: real;

Правило 7, про говорящие идентификаторы

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

Правило 8, про инициализацию массивов

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

Неправильно:

double matrix[3][3];

….

matrix[0][0] = 1.5;

matrix[0][1] = 2.5;

……

Matrix : array[1..3, 1..3] of extended;

……

Matrix[1,1] := 1.5;

Matrix[1,2] := 2.5;

Правильно:

double matrix[3][3] = {

{1.5, 2.5, ... },

{…},

{...},

};

const

Matrix: array[1..3, 1..3] of extended =

(

(1.5, 2.5, ...),

(…),

(...)

);

Правило 9, про функции и процедуры

Программа без подпрограмм – почти всегда заведомо плохая программа.

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

Функции должны быть короткими – 2 экрана (50 строк) максимум. Единственное оправдание длинной функции – это наличие в ней оператора множественного выбора с большим числом альтернатив.

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

Правило 10, про ошибки

Если функция может потенциально завершиться неудачно, то эту ситуацию следует проверять.

Неправильно:

char * buf;

buf = (char*)malloc(/*много-много байт*/);

buf[0] = some_value;

Правильно:

char * buf;

buf = (char*)malloc(/*много-много байт*/);

if (!buf) {

реггог("нет памяти");

exit(1);

};

buf[0] = some_value;

Если возникшая ошибка такова, что программа (подпрограмма) далее выполняться не может, условие проверки на ошибку лучше писать так, чтобы оно было истинным, если ошибка произошла, а не наоборот, и алгоритм строить так, чтобы избегать оборота else в операторе if. Помните, что из программы (подпрограммы) можно выйти в любом месте, не обязательно в конце ее текста.

Неправильно:

char * buf;

buf = (char*)malloc(/*много-много байт*/);

if (buf) {

/* здесь страница кода */

} else {

реггог("нет памяти");

};

Этот else может быть далеко от if, что уменьшает ясность программы.

Правильно:

char * buf;

buf = (char*)malloc(/*много-много байт*/);

if (!buf) {

реггог("нет памяти");

/* выходим */

exit(BAD_EXIT_CODE)

/* или return BAD_RET__VAL; */

};

/* в этом месте все хорошо, продолжаем */

Правило 11, про данные

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

Специально для структуризации данных существуют типы данных, структуры (или записи в терминах языка программирования Pascal) и классы (в ООП-языках). Если программа работает с какой-либо сущностью, то эта сущность должна быть описана в программе посредством структуры или класса, если используется объектно-ориентированный язык. Сложная программа, в которой нет ни одного массива или структуры, – это плохая программа.

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

Неправильно:

#include <stdio.h>

#include <math.h>

double a=0.2,b=1.0,i,h,x,s;

int n=50,k;

void ffa(void)

{h=(b-a)/n;x=a+h/2;s=0;

for(k=1;k<=n;k++){s+=sin(x);x+=h;}

s*=h;} void main(void) {

ffa();

рrintf("значение=%.8f\n",s);}

Правильно:

#include <stdio.h>

#include <math.h>

#define INTEGR_FUNCTION(x) sin((x))

#define INTEGR_FUNCTION_STR "sin(x)"

#define LOWER_LIMIT 0.2

#define UPPER_LIMIT 1.0

#define INTERVALS 50

typedef

struct integr_task {

double from;

double to;

double (*what)(double); /* интегрируемая функция */

int nint; /* число интервалов */

} INTEGR_TASK;

typedef

enum integr_error {

INTEGR_OK = 0,

INTEGR_NO_TASK_OR_RES,

INTEGR_NO_FUNC,

INTEGR_BAD_LIMITS,

INTEGR_BAD_NINT,

INTEGR_LAST_ERROR

} INTEGR_ERROR;

char *integr_error_str[INTEGR_LAST_ERROR+1] = {

"Все хорошо",

"Указатель на задание или результат равен NULL",

"Указатель на функцию равен NULL",

"Верхний предел меньше нижнего",

"Неправильное число интервалов",

"_INTEGR_LAST_ERROR_"

};

INTEGR_ERROR integrate_rect(INTEGR_TASK * task, double * result);

#define INTEGRATE integrate_rect

double function(double arg)

{

return INTEGR_FUNCTION(arg);

}

int main(void)

{

double integral_val;

INTEGR_ERROR error;

INTEGRJTASK test_task = {

LOWER_LIMIT,

UPPER_LIMIT,

function,

INTERVALS

};

error = INTEGRATE(&test_task, &integral_val);

if (error) {

printf("Ошибка в данных (%s).\n", integr_error_str[error]);

return 1;

};

printf(

"\nЗначение определенного интеграла от функции f(x)=

%s\nв пределах от %.8f до %.8f: %.8f\n",

INTEGR_FUNCTION_STR,

LOWER_LIMIT,

UPPER_LIMIT,

integral_val

);

return 0;

}

INTEGR_ERROR integrate_rect(INTEGR_TASK * task, double * result)

{

double x, s, step;

int j;

/* проверка на корректность данных */

if ((!task) || (!result)) {

return INTEGR_NO_TASK_OR_RES;

};

if (!task->what) {

return INTEGR_NO_FUNC;

};

if (task->from > task->to) {

return INTEGR_BAD_LIMITS;

};

if (task->nint < 1) {

return INTEGR_BAD_NINT;

};

/* собственно вычисление */

step = (task->to - task->from)/task->nint;

x = task->from+step/2.0;

s = 0;

for (j = 1; j <= task->nint; j++, s += task->what(x), x += step);

s *= step;

*result = s;

return INTEGR_OK;

}

Приложение 4. Заголовочный файл <math.h> {<cmath.h>} – математические функции

acos

Возвращает арккосинус аргумента

asin

Возвращает арксинус аргумента

atan

Возвращает арктангенс аргумента

atan2

Возвращает арктангенс отношения аргументов

ceil

Округляет вверх

cos

Вычисляет косинус

cosh

Вычисляет гиперболический косинус

exp

Возвращает степень числа е

fabs

Возвращает модуль числа

floor

Округляет вниз

fmod

Возвращает остаток от деления х на у

frexp

Выделяет из числа мантиссу и экспоненциальную часть

ldexp

Преобразует мантиссу и показатель степени в число

log

Вычисляет натуральный логарифм

log10

Вычисляет логарифм по основанию 10

modf

Разбивает число на целую и дробную части

pow

Возводит число в степень

sin

Вычисляет синус

sinh

Вычисляет гиперболический синус

sqrt

Вычисляет квадратный корень

tan

Возвращает тангенс аргумента

tanh

Возвращает гиперболический тангенс аргумента

1 Программист может задать тип константы самостоятельно.

2 Могут быть опущены либо целая часть, либо дробная, но не обе сразу.

3 Могут быть опущены либо целая часть, либо дробная, но не обе сразу. Если указаны обе части, символ точки обязателен.

4 Если переменная в том же операторе инициализируется, спецификатор extern игнорируется.

5 Пробелы между символами внутри операции не допускаются.

6 Препроцессором называется предварительная фаза компиляции, то есть перевода программы с С++ на машинный язык.

7 Приведем еще две наиболее употребительные спецификации: %d для величин целого типа в деся­тичной системе счисления, %lf – для величин типа double. Более полный список спецификаций см. в Приложении 1.