Метки и их использование в программах. Оператор безусловного перехода.
Оператор безусловного перехода
Перед любым исполняемым оператором программы может находиться символьная метка, отделяемая от оператора двоеточием:
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");
Основное правило, которого надо придерживаться при обмене с двоичными файлами звучит примерно так – как данные записывались в файл, так они должны и читаться.