Массив до изменения: 1 3 5 8 2. — КиберПедия 

Типы сооружений для обработки осадков: Септиками называются сооружения, в которых одновременно происходят осветление сточной жидкости...

Особенности сооружения опор в сложных условиях: Сооружение ВЛ в районах с суровыми климатическими и тяжелыми геологическими условиями...

Массив до изменения: 1 3 5 8 2.

2021-03-18 145
Массив до изменения: 1 3 5 8 2. 0.00 из 5.00 0 оценок
Заказать работу

Массив после изменения: 1 9 25 64 4.

2.4 Функции с умалчиваемыми значениями параметров

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

тип имя_параметра=умалчиваемое_значение

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

Пример. Функция находит произведение 2-х переменных

#include <stdio.h>

int def(int a=10, int b=3){return a*b;}

Void main()

{int am=2, bm=5, res1,res2,res3;

 res1=def();    // 10*3=30

 res2=def(am); // 2*3=6

 res3=def(am,bm); // 2*5=10

 printf(" res1=%d res2=%d res3=%d\n ",res1,res2,res3); }

В результате работы программы на экран выведется:

res1=3 0 res2= 6 res3=1 0

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

Пример. Функция находит сумму 3-х переменных

#include <stdio.h>

int sum(int,int=5,int=10); // Прототип функции

Void main()

{int am=20, bm=50, cm=100, s1,s2,s3,s4;

 s1=sum(am);  // 20+5+10=35

 s2=sum(am,bm); // 20+50+10=80

 s3=sum(am,bm,cm); // 20+50+100=170

 printf(" s1=%d s2=%d s3=%d\n ",s1,s2,s3);}

int sum(int a,int b,int c) // Определение функции

{return a+b+c;}

В результате работы программы на экран выведется:

s1=35 s2=80 s3=170

2.5 Функции с переменным числом параметров

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

тип имя(спецификация явных параметров,...);

Последняя запятая необязательна. Многоточие сообщает компи­лятору, что не нужно проводить дальнейший контроль соответствия количества параметров и их типов при обработке вызова. Переход в списке от параметра к параметру осуществляется с помощью указате­ля, тип которого является типом переменной. При увеличении указа­теля на единицу осуществляется переход к следующей переменной такого же типа. В ВС31 функции с переменным числом параметров не работают с типами char, unsigned char, float.

В таких функциях существуют два способа определения коли­чества параметров: 1) передача в функцию в списке фактических параметров информации об их количестве; 2) добавление параметра–индикатора в конец списка фактических параметров.

Пример. В функцию передается в списке явных параметров количес­тво элементов, которые суммируются.

#include <stdio.h>

int sumi (int i,...) // Определение функции

{int *num=&i; // 1-й параметр – число переменных в списке

 int sum=0;

 for(;i;i--) sum+=*(++num);// вычисляем сумму со 2-го элемента

return sum;}

double sumd(int i,double d...) // Определение функции

{double *ptr=&d; // указатель на 2-й параметр типа double

 double sum=0.0;

 for(;i;i--) // 1-й параметр – число переменных в списке

sum+=*(ptr++);

 return sum;}

Void main()

{int a1=10,a2=20,a3=30,s1,s2;

 s1=sumi(3,a1,a2,a3); // сумма 3-х чисел a1,a2,a3

 s2=sumi(6,1,a 1,3,a 2,a3,4); // сумма 6-и чисел

 double d1=5. 7, d2=1. 3,s3;

 s3=sumd(4,d1,1.25,d2,2.5); // сумма 4-х чисел

 printf("s1=%d s2=%d s3=%.2lf\n",s1,s2, s3);}

В результате работы программы на экран выведется:

s1=60 s2=68 s3=10. 75

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

#include <stdio.h>

#define term 0. 0

double mult(double arg...) // Определение функции

{double a=1.0, *ptr=&arg;

 if(*ptr==0.0) return 0. 0;

 for(;*ptr!=term;ptr++)

a*=*ptr;

 return a;}

Void main()

{double a=2. 5,b=2. 0,md1,md2;

 md1=mult(a,b,0. 3,term);

 md2=mult(0. 5,a,4. 0,b,1. 1,term);

 printf("md1=%.2lf md2=%.2lf\n",md1,md2);}

В результате работы программы на экран выведется:

md1=1. 50 md2=11. 00

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

Макрокоманда void va_start (va_list param, последний явный параметр) связываетвспомогательный параметр param с последним явным параметром в списке.

Макрокоманда type va_arg (va_list param,type) переме­щает указатель param на число байтзаданного типа type.

Макрокоманда va_end (va_list param) вызывается после обработки списка и организует корректный возврат из функции (вместо оператора return).

2.6 Передача параметров в функцию main()

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

[ тип ] main(int argc, char **argv){...}

[ тип ] main(int argc, char **argv, char **envp){...}

[тип] – тип функции main может быть void или int.

Параметр int argc определяет число слов в командной строке.

Параметр char **argv ‑ указатель на массив строк, вводимых из командной строки. Массив строк –это массив указателей, являющихся адресами первых символов этих строк. Размер этого массива строк равен argc. Нулевое слово, на которое указывает указатель argv[0], всегда является спецификацией запускаемой на выполнение програм­мы (именем программы с расширением .exe).

Параметр char **envp ‑ это указатель на массив указателей на строки среды программы. С помощью этого параметра в программу можно передать всю информацию среде окружения программы, ко­торая определяет некоторые особенности операционной системы и др.

В следующих примерах демонстрируется передача параметров в функцию main. Для работы программ их необходимо запустить из командной строки, например в среде FAR, NC, VC, DN и др.

Пример.

#include <stdio.h>

#include <conio.h>

int main(int argc,char**argv,char**envp)

{clrscr();

 for(int i=0;i<argc;i++)

 printf("argv[%d]=%s\n",i,argv[i]);

 getch();

 puts(" Среда окружения ");

 while(*envp) puts(*envp++);

getch();

 return 0;}

Пусть исполняемый файл этой программы prog1.exe располо­жен в корневом каталоге диска C:\. Тогда, если в командной строке набрать c:\>prog1.exe my age 19, то результат работы программы может быть такой:

argv[0]=C:\PROG1.EXE

argv[1]=my

argv[2]=age

argv[3]=19

Среда окружения

PROMPT=$p$g

COMSPEC=C:\WINDOWS\COMMAND.COM

PATH=C:\BC31\BIN

Proccessor_ArcHitecture=x86

Proccessor_identifier=x86 Family 15 Model 47

Proccessor_Level=15

...

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

Пример.

#include <stdio.h>

#include <conio.h>

#include <string.h>

int main(int argc,char **argv)

{clrscr();

 char str[]="Secret";

 if(strcmp(argv[1],str)!=0)// сравнение 2-х строк

{printf("Error\n"); getch(); return 1;}

 printf("Programma ");

 printf("%s %c.%c.\n",argv[2],argv[3][0],argv[4][0]);

int x, a=5, b=7,res1;

sscanf (argv [5], " % d",& x); // преобразование строки в целое число

res1=(a+b)*x; //(5+7)*100

 printf("res1=%d\n",res1);

getch();

 return 0;}

Пусть исполняемый файл этой программы prog2.exe распо­ложен в корневом каталоге диска C:\. Для правильной работы этой программы в командной строке надо набрать вторым словом Secret. Если в командной строке набрать:

c:\>prog1.exe Secret Petrov Ivan Ivanovich 100:

то на экран выведется:

Programma Petrov I.I.

res1=12 0 0

Если второе слово в командной строке будет другим, то на экран выведется Error, и программа завершится.

2.7 Рекурсивные и подставляемые функции

Рекурсивная функция – эта такая функция, которая содержит в себе вызов этой же функции, т.е. самовызывающая функция.

Рекурсивные функции желательно не использовать, если есть очевидное итерационное решение.

Пример. Вычисление факториала неотрицательного целого числа

long fact (int k) // Определение функции

{if(k<0) return 0;

if (k==0} return 1;

return k* fact(k-1);}

Void main()

{int n=5; long F_n=fact(n);

 printf (“%d!=%ld”,n,F_n);}

На экран выведется: 5!=120

Функция прерывается при k == 0, последнее выражение(1 * fact (1-1)),вычисляет k*(k-1)*(k-2)*(k-3)*(k-4)*1.

Подставляемая функция (inline -функция) – это функция, тело которой подставляется при каждом ее вызове. Такая функция определяется с помощью слова inline.

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

Пример.

inline float mod(float x,float y) // Определение функции

 {return sgrt(x*x+y*y);}

Void main()

{float f1=4. 5, f2=-2. 5, f3=-1. 5,res1,res2;

 res1=mod(f1,f2); // 5.1478

//res1=sgrt(f1*f1+f2*f2);

res2=mod(f1,f3); } // 4.743

//res2=sgrt(f1*f1+f3*f3); }

В ВС31 для inline -функции принято: 1) функция не должна быть рекурсивной; 2) функция не должна содержать операторов for, while, do, switch, goto. В этих случаях компилятор выдает предупреждение, и функция будет трактоваться как обычная.

Пример. Функция вводит символы, пока не введется ’q’. Функция рекурсивная, поэтому не может быть реализована как inline -функция.

inline void str () // Функции будет определена как обычная

{ char b; if (cin >> b && b!= 'q') str ();}

2.8 Функции, возвращающие указатель

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

Пример. Функция возвращает адрес элемента массива, значение которого равно нулю. В программе это значение заменяется на 1.

# include < stdio. h >

int * func (int, int []); // прототип функции

Void main()

{const N=5;

 int *b,arr[N]={3,0,7,0,9}, n=0;

 if((b=func(N,arr))!=NULL) // вызов функции

{n++; *b=1;

printf("*b=%d b=%p\n",*b,b);}

 printf(" выполнено замен %d \n",n);}

int *func(int n,int a[]) // Определение функции

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

 if(a[i]==0) return &a[i];

return NULL;}

На экран выведется:  

*b=1 b=8ff2:0FF 2

*b=1 b=8ff2:0FF 8

выполнено замен =2

2.9 Функции и структуры

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

Пример. В функцию передается структура и указатель на структуру

#include <string.h>

#include <stdio.h>

struct STUD{char name[20];int age;};

void f1(STUD a)

{strcpy(a.name," Петров "); // Копируем в a.name " Петров "

 a.age+=1;

 printf("%s %d\n",a.name,a.age); } // Петров 20

STUD f2(STUD b, STUD с)

{if (b. age> с. age) return b;

// возвращаем ту структуру, в которой age больше

 return с;}

void f3(STUD *p)

{strcpy(p->name,"Сидоров");

 p->age=21;

 printf("%s %d\n", p->name, p->age);} // Сидоров 2 1

STUD *f4(STUD *r1,STUD *r2)

{if(r1->age>r2->age) return r1;

// возвращаем указатель на ту структуру, в которой age больше

 return r2;}

Void main()

{STUD st1={"Иванов",19},st 2 ={" Орлов ", 2 2},st 3;

f1(st1);

 printf("%s %d\n",st1.name,st1.age);// все равно Иванов 19

 st 3 =f2(st1, st 2);

 printf("%s %d\n",st 3.name,st 3.age); // Орлов 22

STUD st 4 ={"Сергеев",18},st 5 ={"Алешин",25},*pst;

  f3(&st4);

 printf("%s %d\n",st4.name,st4.age); // Сидоров 21

pst=f4(&st4,&st5);

 printf("%s %d\n",pst->name,pst->age);} // Алешин 25

2.10 Перегрузка функций

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

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

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

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

# include <stdio.h>

int FF(int x,int y) {return x*x+y*y;}

long FF(long x, long y) {return x*x+y*y;}

float FF(float x, float y) {return x*x+y*y;}

Void main()

{int a1=3,a2=4,a3; long b1=20,b2=400,b3;

float d1=5.5,d2=7.5,d3;

 a3=FF(a1,a2);

 b3=FF(b1,b2);

 d3=FF(d1,d2);

 printf("a3=%d b3=%ld d3=%.2f\n", a3,b3,d3);}

В результате работы программы на экран выведется:

a3=25 b3=160400 d3=86.50

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

#include <stdio.h>

double mul(double x){return x*x*x;}

double mul(double x,double y){return x*y*y;}

double mul(double x,double y,double z){return x*y*z;}

Void main()

{double M1=mul(0.2);

 double M2=mul(0.2,0.4);

 double M3=mul(0.2,0.3,0.4);

 printf("M1=%.4lf M2=%.4lf M3=%.4lf\n", M1,M2,M3);}

В результате работы программы на экран выведется:

M1=0.0080 M2=0.0322 M3=0.0240

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

Int mul(double x,double y)

{if (x>y)return 1; else return 2;}

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

Пример.

#include <stdio.h>

int sum(int x) {return 2*x;}

int sum(int x, int y=5) {return x+y;}

Void main()

{int S1=sum(10); // 20 или 15

… }

2.11 Шаблоны функций

Для автоматизации создания функций различного вида (в т.ч. и перегруженных) в С++ введены шаблоны функций. Определяется шаблон семейства функций следующим образом

template <class type1,class type2, class type3 …>

где type1, type2, type3. – формальные параметры шаблона, которыми можно обозначать тип возвращаемого значения, типы формальных и локальных параметров внутри функции.

Пример.

template <class Tip>

Tip max(Tip x, Tip y)

{if(x>y)return x;

 return y;}

Int main()

{int a1=-7,a2=40,res1; float f1=-3.6,f2=-10.5,res2;

 res1=max(a1,a2);

 res2=max(f1,f2);

 printf("res1=%d res2=%.1f\n",res1,res2);

//res1=40 res2=-3.6

 return 0;}

При компиляции программы с шаблоном функции, компилятор при вызове функции доопределяет ее. В данном примере при вызове res1=max(a1,a2); сформируется определение int max(int x,int y) и вызовется именно эта функция. При вызове функции res2=max(f1,f2); сформируется определение float max(float x,float y) и теперь вызываться будет данная функция.

Свойства параметров шаблонов:

1. Имена параметров шаблона должны быть уникальными во всем определении шаблона;

2. Список параметров шаблона не может быть пустым;

3. Объявление каждого параметра должно начинаться со слова class;

4. Имя параметра шаблона имеет свойства и права типа, т.е. его именем можно называть типы формальных параметров, тип функции, типы локальных переменные функции. Область видимости параметра шаблона во всем определении функции;

5. Все  параметры шаблона, используемыев определении функции, должны быть обязательно использованы в сигнатуре функции. Т.е. недопустимо, например, определение

template <class A,class B>

B func(A x){ … } // Ошибка, параметр В не использован

// в спецификации формальных параметров

Шаблон функции может быть использован и при описании прототипа функции.

template <class A>

void func(A);

Пример. Функция меняет местами значение объектов

#include <stdio.h>

template <class A>

void swp(A *x,A *y) // указатель типа А

{A z=*x; // Шаблон использован в теле функции

*x=*y; *y=z;}

Int main()

{double d1=3.3, d2=2.2;  swp(&d1,&d2);

 printf("d1 _ n=%. 2lf d2 _ n=%. 2lf\n",d1,d2);

//d1 _ n=2.20 d1_n=3.30

 char c1='M', c2='Z';  swp(&c1,&c2);

 printf("c1_n=%c c2_n=%c\n",c1,c2);//c1_n=Z c2_n=M

 char *st1=" Москва ", *st2=" Киев ";  swp(&st1,&st2);

 printf("st1_n=%s st2_n=%s\n",st1,st2);

//st1_n= Киев st1_n= Москва

 swp(st1,st2);

 printf("st1_nn=%s st2_nn=%s\n",st1,st2);

// st1_nn= Миев st1_nn= Косква

return 0;}

В этом примере компилятор сформирует следующие определения к функциям:

void swp(double *x,double*y), void swp(char*x,char*y),

void swp(char**x,char**y), void swp(char*x,char*y).

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

Пример. Функция определяет индекс максимального элемента

template <class T>

int func_max(T x[], int n)

{T m=x[0]; int k=0;

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

if(x[i]>max){max=x[i]; k=i;}

 return k;}

Void main()

{int arr[5]={5,12,40,23,4}, in1, in2;

 in1=func_max(arr,5); //in1=2

 printf("max1=arr[%d]=%d\n",in1,arr[in1]);

 float mas[4]={5.3,1.2,4.0,20.3};

 in2=func_max(mas,4); //in2=3

 printf("max2=mas[%d]=%.2f\n",in2,arr[in2]);}

В результате работы программы на экран выведется:

max1=arr[2]=40 max2=mas[3]=20.30

2.12 Указатели на функции

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

Указатель на функцию определяется специальным образом:

тип_функции (*имя_указателя)(спецификация_параметров);

где тип_функции – тип возвращаемого функцией значения;

спецификация_параметров – типыформальных параметров;

*имя_указателя – имя указателя на функцию в круглых скобках.

Примеры определения указателей на функцию:

i nt (*ptrfunc)(int); // указатель на функцию

char (*pf1)(char*);

char* (*pf3)(char*);

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

int *ptrfunc(int); // прототип

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

Пример.

float mul(float x,float y) {return x*y;}

float sum(float x,float y) {return x+y;}

Void main()

{float (*ptrmul)(float,float); // указатель на функцию

float (*ptrsum}(float,float); // указатель на функцию

 ptrmul=mul; // указателю присвоили имя функции mul

 ptrsum=sum; // указателю присвоили имя функции sum

float f1=3.0,f2=9.0,f3;

 f3=(*ptrmul)(f1,f2)+(*ptrsum)(f1,f2);

 printf("f3=%.1f", f3); // f3=39.0

 float (* ptr)(float, float); // указатель на функцию

 ptr=mul; // указателю присвоили имя функции mul

 printf("mul=%.1f", (*ptr)(f1,f2)); // mul=27.0

ptr=sum; // указателю присвоили имя функции sum

 printf("sum=%.1f",(*ptr)(f1,f2)); }// sum=12.0

Указатели на функцию могут образовывать массив, при этом тип и сигнатура во всех функциях должны быть одинаковые. Синтаксис: тип_функции(*имя_указателя [n])(спецификация_параметров);

где n – размер массива указателей на функции (константное выражение).

Пример. Определен массив из трех указателей на функции, имеющие тип int с параметром типа int

#include <stdio.h>

int f1(int x){return x+x;};

int f2(int x){return x*x;};

int f3(int x){return ~x;}; // побитовое отрицание

Void main()

{int(*p[3])(int);

 p[0]=f1; p[1]=f2; p[2]=f3;

 int a=(*p[0])(5);

 int b=(*p[1])(a);

 int c=(*p[2])(a);

 printf("a=%d b=%d c=%u\n",a,b,c);

}

В результате работы программы на экран выведется:

a=10 b=100 c=65525

Для задания типа указателя на функцию можно применять спецификатор typedef. Синтаксис такой:

// Определение тип указателя на функцию

typedef тип _ функции(*имя _ типа_указателя)(спец_ия _ парам);

// Определение указателя на функцию, массива указателей на функцию

имя_типа_указателя имя_указателя_ф-ции; имя_типа_указателя имя_указателя_ф-ции [n];

Например:

typedef int (*ABC)(int); // тип указателя на функцию ABC

АВС р1; // указатель на функцию р1 типа ABC

АВС р2[4]; //  массивиз 4 указателей на функцию р2 типа ABC

Пример. Определили массив указателей на функции  

#include <stdio.h>

void typ0(){printf("Привет,");}

void typ1(){printf(" дорогой");}

void typ2(){printf(" друг!");}

typedef void (*TYPE)();

TYPE mpf[]={typ0,typ1,typ2};

//void (*mpf[])()={typ0,typ1,typ2};

Void main()

{for(int i=0;i<3;i++) mpf[i]();}

Указатели на функции могут использоваться в качестве параметра функции. В этом случае в функцию можно передать указатели на различные функции.

Пример. Функция суммирует сначала квадраты элементов массива, а затем ‑ их кубы.

#include <stdio.h>

float sqr(float e){return e*e;} // Определение функции

float cube(float y){return y*y*y;} // Определение функции

typedef float(*PF)(float);

float summa(PF f,float a[],int N) // Определение функции

{float sum=0.0;

 for(int i=0;i<N;i++) sum+=(*f)(a[i]);

 return sum;}

Void main()

{float res1, res2, A[3]={1. 5,2. 0,3. 0};

 res1=summa(sqr,A,3); //2. 25+4+9=15. 25

res2=summa(cube,A,3); //3. 375+8+27=15. 25

 printf("res1=%.3f res2=%.3f\n",res1,res2);}

На экран выведется: res 1=15. 250 res 2=38. 375

3 Работа с файлами

Функции для работы с файлами в BC31 построены на функциях MS-DOS. Их разделяют на: потоковые и префиксные. Главное их отличие в том, что потоковые функции выполняют дополнительную буферизацию информации. Это приводит к двойной буферизации информации: на уровне MS-DOS и на уровне библиотечной функции (создается регулируемый буфер). Префиксные функции (функции нижнего уровня) не создают дополнительный буфер, они более эффективны при переносе блоков данных по 512 байт.

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

Ввод-вывод потока позволяет выполнять следующие действия: 1) открывать и закрывать потоки; 2) создавать и удалять временные потоки; 3) считывать и записывать символы, строки, форматирован­ные и неформатированные данные; 4) анализировать ошибки ввода-вывода потока и условия конца потока (конца файла) и т.д.

3.1 Потоковый ввод-вывод

С началом выполнения программы автоматически открываются стандартные потоки: стандартный ввод (с клавиатуры), стандартный вывод (на экран монитора), стандартный вывод сообщений об ошиб­ках (на экран монитора). Для функций библиотеки <stdio.h> эти потоки stdin, stdout, stderr, которые работают с консолью (клавиатурой и монитором).

Все другие потоки для обмена данными между файлами нужно открыть явно. Причем, эти потоки могут открываться в одном направлении (только для чтения или записи) и в двух направлениях (для чтения и записи). Когда поток открывается, он связывается со структурой определенного типа, содержащей всю информацию для работы с потоком. Тип этой структуры с именем FILE определен в файле <stdio.h> и имеет следующий вид:

Struct FILE

{int level;   // пуст или полон буфер

 unsigned flags; // флаги состояния файла

 char fd;      // логический номер файла, дескриптор файла

 unsigned char hold; // непереданный символ

 int bsize;        // размер буфера

unsigned char _FAR *buffer;// указатель на буфер потока

 unsigned char _FAR *curp; // указатель текущей позиции

 unsigned istemp;   // флаг временного файла

 short token;      // маркер действительности файла

};

Флаги состояния задают битовую позицию в члене структуры flags.

Определена специальная константа, обозначающая конец файла:

#define EOF (-1) // индикатор конца файла

Функции обмена с файлами могут работать в двух режимах: текстовом и двоичном. По умолчанию режим устанавливается текс­товый. В текстовом режиме при записи в файл символ '\n' (перевод на начало новой строки) преобразуется в два символа '\n' и '\0', а при чтении из файла такие символы уже воспринимаются как два символа '\n' и '\0'. Такженажатиеклавиш Ctrl-Z (конец ввода строки) воспринимается как конец строки '\0', который не записывается в файл.

При обмене с файлом в двоичном режиме никаких преобразова­ний символов не производится.

В файлах <fcntl.h> и <stdlib.h> определена специальная переменная _fmode, которая может принимать значения O_BINARY (бинарный режим) или O_TEXT (текстовый режим). Например:

_fmode=O_BINARY;

3.2 Открытие и закрытие потока

Для начала обмена данными между программой и файлом необходимо открыть поток.

Функция открытия потока имеет прототип:

FILE* fopen(const char* filename, const char* mode);

Эта функция открывает поток, который связывает программу с заданным файлом. В случае успешного открытия возвращается указатель на структуру типа FILE (FILE*), описывающую открытый поток. Если файл не удалось открыть, функция возвращает значение NULL- указатель. Возвращаемое значение указателя присваивается ранее определенной переменной типа FILE*, которую в дальнейшем можно использовать для обмена данными с файлами.

Переменная char* filename – указатель на константную стро­ку, задающую спецификацию открытого файла, т.е. путь и имя файла. Если путь не задан, то по умолчанию выбирается текущий диск и директорий.

Переменная char* mode ‑ указатель на строку, задающую режим открытия потока и состоящую из определенных символов:

r – поток открывается только для чтения из существующего файла, r+ – поток открывается для чтения из существующего и записи в существующий файл;

w – поток открывается только для записи в файл, w+ – поток откры­вается для записи в файл и для чтения из файла. Если файла нет, он создается, если файл существует, его старое содержимое пропадает;

a – поток открывается для добавления данных в конец файла, a+ – поток открывается для добавления данных в конец файла и для чтения из файла. Если файл не существует, он создается;

t – поток открывается в текстовом режиме, задается после r, w, a;

b – поток открывается в двоичном режиме,  задается после r, w, a.

Пример. Фрагмент программы, в которой открывается поток fp для записи данных в файл myfile.txt ипоток fin для записи данных в файл abc.txt на диске D в директории USER:

FILE *fp, *fin;

fp=fopen("D:\\User\\myfile.txt","w");

char *fname=("D:\\User\\abc.txt");

fin=fopen(fname,"r");

Если открытие потока оказалось неудачным, то функция возвра­щает NULL -указатель. При открытии потока рекомендуется выполнять проверку возвращаемого значения функции открытия файла:

if(fp==NULL){puts(“ошибка открытия файла”); return -1;}

if (fin == NULL){ puts (" ошибка открытия файла "); return -1;}

После работы с потоком его закрывают.

Функция закрытия потока имеет прототип:

int fclose(FILE *имя);

Эта функция закрывает открытый поток. В функцию передается указатель на структуру типа FILE (имя открытого потока), и при успешном закрытии потока функция возвращает 0, иначе – EOF(–1). Эта функция удаляет структуру типа FILE, выгружает содержимое буфера на диск, если файл был открыт для записи, и вызывает функцию закрытия файла MS DOS, которая обновляет содержимое директория (обновляет информацию о размере, дате и времени). Например: fclose(fp);

В программе может быть открыто несколько файлов. В конце работы с ними их необходимо все закрыть. Есть функция закрытия всех открытых файлов с прототипом: int fcloseall(void);   

Эта функция возвращает число закрытых файлов, или EOF, если хотя бы один файл не удалось закрыть.

int n=fcloseall(void);

printf("n=%d",n); //n=2, если закрылись два открытых потока)

// или n=-1,если какой-либо поток из двух открытых не закрывается

Для переоткрытия файла с другим режимом доступа существует функция со следующим  прототипом:

FILE *freopen (const char *filename,const char*mode,FILE *fp);

Эта функция закрывает поток fp с прежним именем файла и открывает снова этот же поток с именем файла, на который указывает ASCIIZ -строка filename в режиме, указанном в строке mode. В случае успеха функция возвращает указатель на описание открытого потока, которое будет совпадать с fp, иначе NULL. Если имена файлов разные, происходит переадресация файлового ввода-вывода.

fp=freopen("D:\\User\\myfile.txt","a",fp);

FILE *fp1=freopen("D:\\User\\myfile1.txt","ab",fp);

freopen("D:\\User\\abc.txt","r",fin);

Пример. Файл сначала открывается для чтения, затем снова откры­вается для дополнения.

#include<stdio.h>

Int main()

{FILE *fp;

char buff[100], *fname ="myfile.txt";

fp=fopen(fname,"r"); // открытие потока для чтения из файла

 if(fp!=NULL)fgets(buff,50,fp);// чтение из файла строки

 else {puts("ERROR_1"); return -1;}

 puts(buff); // вывод на экран считанной из файла строки

 fp=freopen(fname,"a",fp); // переоткрытие потока

 if(fp!=NULL) fputs("abcdef",fp);// добавление строки

else {puts("ERROR_2"); return -1;}

 fclose(fp); // закрытие потока

return 0;}

При работе с файлами большая роль отводится обработке ошибок. Для анализа ошибок можно использовать функцию c прототипом:

void perror(const char * s tr);

Эта функция выводит в поток (на экран) строку s tr вместе с системной ошибкой. Список системных ошибок определен в файле <stdlib.h>.

FILE *ptr=fopen("abc.doc","r"); // открытие файла для чтения

if (fp == NULL){perror("Не могу открыть файл: "); return -1;}

char c = fgetc (ptr); // чтение одного символа

fclose(ptr); // закрытие потока

Если файл в текущем директории отсутствует, то на экран выведется:


Поделиться с друзьями:

Индивидуальные очистные сооружения: К классу индивидуальных очистных сооружений относят сооружения, пропускная способность которых...

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

Организация стока поверхностных вод: Наибольшее количество влаги на земном шаре испаряется с поверхности морей и океанов (88‰)...

Таксономические единицы (категории) растений: Каждая система классификации состоит из определённых соподчиненных друг другу...



© cyberpedia.su 2017-2024 - Не является автором материалов. Исключительное право сохранено за автором текста.
Если вы не хотите, чтобы данный материал был у нас на сайте, перейдите по ссылке: Нарушение авторских прав. Мы поможем в написании вашей работы!

0.321 с.