Метки и их использование в программах. Оператор безусловного перехода.

Оператор безусловного перехода

Перед любым исполняемым оператором программы может находиться символьная метка, отделяемая от оператора двоеточием:

m5: printf("\nx=%f",x);

На помеченный таким образом оператор может быть передано управление с помощью оператора goto m5. Но такие переходы допустимы только внутри функции. Переход из одной функции в другую по оператору goto недопустим.

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

Рассмотрим две реализации программы, которая вводит три целочисленных значения и выводит максимальное из них. В первой из них (программа-спагетти) на 10 исполняемых строк приходится 5 операторов goto. Во второй реализации потребовалось всего 5 исполняемых строк и ни одного оператора goto.

Пример 6.1. Программа-спагетти.

#include <stdio.h>

#include <conio.h>

void main()

{ int a,b,c;

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

if(a>b) goto m1;

if(b>c) goto m2;

m3: printf("max=%d",c);

goto m5;

m1: if(c>a) goto m3;

m4: printf("max=%d",a);

goto m5;

m2: printf("max=%d",b);

m5: getch();

}

Пример 6.2. Пример прозрачной логики

#include <stdio.h>

#include <conio.h>

void main()

{ int a,b,c;

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

if(b>a) a=b;

if(c>a) a=c;

printf("max=%d",a);

getch();

}

Одним из случаев, когда применение оператора goto приводит к более простой и более эффективной программе является необходимость выйти из внутреннего цикла за пределы внешнего цикла:

int i,j;

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

for(j=0; j<20; j++) {...

if(условие_выхода) goto mm;

}

}

mm:

Конечно, можно придумать более запутанную схему, не используя goto:

int i,j,k=1;

for(i=0; i<n && k; i++) {...

for(j=0; j<20 && k; j++) {...

if(условие_выхода) {k=0; break;}

}

}

Применяя оператор goto, стоит следовать следующим рекомендациям:

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

· не входить в цикл извне, ибо это приведет к неправильной работе цикла;

· не передавать управление внутрь условного оператора;

· не передавать управление внутрь переключателя

· .

Билет 11

Двоичные файлы в языке Си и работа с ними.

Двоичные файлы

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

Создание двоичных файлов с помощью функции fopen отличается от создания текстовых файлов только указанием режима обмена – "rb" (двоичный для чтения), "rb+" (двоичный для чтения и записи), "wb" (двоичный для записи), "wb+" (двоичный для записи и чтения):

FILE *f1;

.........

f1=fopen(имя_файла, "режим");

Обычно для обмена с двоичными файлами используются функции fread и fwrite:

c_w = fwrite(buf, size_rec, n_rec, f1);

Здесь buf – указатель типа void* на начало буфера в оперативной памяти, из которого информация переписывается в файл;

size_rec – размер передаваемой порции в байтах;

n_rec – количество порций, которое должно быть записано в файл;

f1 – указатель на блок управления файлом;

c_w – количество порций, которое фактически записалось в файл.

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

c_r = fread(buf, size_rec, n_rec, f1);

Здесь c_r – количество порций, которое фактически прочиталось из файла;

buf – указатель типа void* на начало буфера в оперативной памяти, в который информация считывается из файла.

Обратите внимание на значения, возвращаемые функциями fread и fwrite. В какой ситуации количество записываемых порций может не совпасть с количеством записавшихся данных? Как правило, на диске не хватило места, и на такую ошибку надо реагировать. А вот при чтении ситуация, когда количество прочитанных порций не совпадает с количеством запрашиваемых порций, не обязательно является ошибкой. Типичная картина – количество данных в файле не кратно размеру заказанных порций.

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

fseek(f1,delta,pos);

Здесь f1 – указатель на блок управления файлом;

delta – величина смещения в байтах, на которую следует переместить указатель файла;

pos – позиция, от которой производится смещение указателя (0 или SEEK_SET – от начала файла, 1 или SEEK_CUR – от текущей позиции, 2 или SEEK_END – от конца файла)

Кроме набора функций {fopen/fclose, fread/fwrite} для работы с двоичными файлами в библиотеке BC предусмотрены и другие средства – _dos_open /__dos_close, _dos_read /_dos_write, _creat /_close, _read /_write. Однако знакомство со всеми возможностями этой библиотеки в рамках настоящего курса не предусмотрено.

Пример 2. Рассмотрим программу, которая создает двоичный файл для записи с именем c_bin и записывает в него 4*10 порций данных в машинном формате (строки, целые и вещественные числа). После записи данных файл закрывается и вновь открывается для чтения. Для демонстрации прямого доступа к данным информация из файла считывается в обратном порядке – с конца. Контроль записываемой и считываемой информации обеспечивается дублированием данных на экране дисплея.

#include <stdio.h>

#include <stdlib.h>

#include <math.h>

#include <conio.h>

main( )

{ FILE *f1; //указатель на блок управления файлом

int j,k;

char s[]="Line";

int n;

float r;

f1=fopen("c_bin","wb"); //создание двоичного файла для записи

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

{ r=sqrt(j);

fwrite(s,sizeof(s),1,f1); //запись строки в файл

fwrite(&j,sizeof(int),1,f1); //запись целого числа в файл

fwrite(&r,sizeof(float),1,f1); //запись вещественного числа

printf("\n%s %d %f",s,j,r); //контрольный вывод

}

fclose(f1); //закрытие файла

printf("\n");

f1=fopen("c_bin","rb"); //открытие двоичного файла для чтения

for(j=10; j>0; j--)

{//перемещение указателя файла

fseek(f1,(j-1)*(sizeof(s)+sizeof(int)+sizeof(float)),SEEK_SET);

fread(&s,sizeof(s),1,f1); //чтение строки

fread(&n,sizeof(int),1,f1); //чтение целого числа

fread(&r,sizeof(float),1,f1); //чтение вещественного числа

printf("\n%s %d %f",s,n,r); //контрольный вывод

}

getch();

}

//=== Результат работы ===

Line 1 1.000000

Line 2 1.414214

Line 3 1.732051

Line 4 2.000000

Line 5 2.236068

Line 6 2.449490

Line 7 2.645751

Line 8 2.828427

Line 9 3.000000

Line 10 3.162278

Line 10 3.162278

Line 9 3.000000

Line 8 2.828427

Line 7 2.645751

Line 6 2.449490

Line 5 2.236068

Line 4 2.000000

Line 3 1.732051

Line 2 1.414214

Line 1 1.000000

Использованные в этом примере операторы:

fclose(f1); //закрытие файла

f1=fopen("c_bin","rb"); //открытие двоичного файла для чтения

могут быть заменены обращением к единственной функции freopen, которая повторно открывает ранее открытый файл:

f1=freopen("c_bin","rb");

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

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