Функции

Содержание:

8.3 Системные вызовы open, creat, close, unlink

Скрыть рекламу в статье

8.3 Системные вызовы open, creat, close, unlink

В отличие от стандартных файлов ввода, вывода и ошибок, которые открыты по умолчанию, остальные файлы нужно открывать явно. Для этого есть два системных вызова: open и creat.

Функция open почти совпадает с fopen, рассмотренной в главе 7. Разница между ними в том, что первая возвращает не файловый указатель, а дескриптор файла типа int. При любой ошибке open возвращает -1.

Как и в fopen, аргумент name — это строка, содержащая имя файла. Второй аргумент, flags, имеет тип int и специфицирует, каким образом должен быть открыт файл. Его основными значениями являются:

В System V UNIX эти константы определены в ‹fcntl.h›, а в версиях Berkley (BSD) — в ‹sys/file.h›.

Чтобы открыть существующий файл на чтение, можно написать

Далее везде, где мы пользуемся функцией open, ее аргумент perms равен нулю.

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

Функция creat возвращает дескриптор файла, если файл создан, и -1, если по каким-либо причинам файл создать не удалось. Если файл уже существует, creat «обрежет» его до нулевой длины, что равносильно выбрасыванию предыдущего содержимого данного файла; создание уже существующего файла не является ошибкой.

Если строится действительно новый файл, то creat его создаст с правами доступа, специфицированными в аргументе perms. В системе UNIX с каждым файлом ассоциированы девять битов, содержащие информацию о правах пользоваться этим файлом для чтения, записи и исполнения лицам трех категорий: собственнику файла, определенной им группе лиц и всем остальным. Таким образом, права доступа удобно специфицировать с помощью трех восьмеричных цифр. Например, 0755 специфицирует чтение, запись и право исполнения собственнику файла, а также чтение и право исполнения группе и всем остальным.

Для иллюстрации приведем упрощенную версию программы cp системы UNIX, которая копирует один файл в другой. В нашей версии копируется только один файл, не позволяется во втором аргументе указывать директорий (каталог), и права доступа не копируются, а задаются константой.

Данная программа создаст файл вывода с фиксированными правами доступа, определяемыми кодом 0666. С помощью системного вызова stat, который будет описан в параграфе 8.6, мы можем определить режим использования существующего файла и задать тот же режим для копии.

Заметим, что функция error, вызываемая с различным числом аргументов, во многом похожа на printf. Реализация error иллюстрирует, как пользоваться другими программами семейства printf. Библиотечная функция vprintf аналогична printf, с той лишь оговоркой, что переменная часть списка аргументов заменена в ней одним аргументом, который инициализируется макросом va_start. Подобным же образом соотносятся функции vfprinf с fprintf и vsprintf с sprintf.

На количество одновременно открытых в программе файлов имеется ограничение (обычно их число колеблется около 20). Поэтому любая программа, которая намеревается работать с большим количеством файлов, должна быть готова повторно использовать их дескрипторы. Функция close(int fd) разрывает связь между файловым дескриптором и открытым файлом и освобождает дескриптор для его применения с другим файлом. Она аналогична библиотечной функции fclose с тем лишь различием, что никакой очистки буфера не делает. Завершение программы с помощью exit или return в главной программе закрывает все открытые файлы.

Функция unlink(char *name) удаляет имя файла из файловой системы. Она соответствует функции remove стандартной библиотеки.

Упражнение 8.1. Перепишите программу cat из главы 7, используя функции read, write, open и close. Замените ими соответствующие функции стандартной библиотеки. Поэкспериментируйте, чтобы сравнить быстродействие двух версий.

Оглавление книги

Вещественные данные

Вещественный тип предназначен для представления действительных чисел. Вещественные числа представляются в разрядной сетке машины в нормированной форме.Нормированная форма числа предполагает наличие одной значащей цифры (не 0) до разделения целой и дробной части. Такое представление умножается на основание системы счисления в соответствующей степени. Например, число 12345,678 в нормированной форме можно представить как

12345,678 = 1,2345678·104

Число 0,009876 в нормированной форме можно представить как

0,009876 = 9,876·10-3

В двоичной системе счисления значащий разряд, стоящий перед разделителем целой и дробной части, может быть равен  только 1. В случае если число нельзя представить в нормированной форме (например, число 0), значащий разряд перед разделителем целой и дробной части равен 0.
Значащие разряды числа, стоящие в нормированной форме после разделителя целой и дробной части, называются мантиссой числа.
В общем случае вещественное число в разрядной сетке вычислительной машины можно представить в виде 4 полей.

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

    2n-1,

    где n — количество разрядов, отводимых для представления степени числа.

  • целое — бит, который для нормированных чисел всегда равен 1, поэтому в некоторых представлениях типов этот бит опущен и принимается равным 1.
  • мантисса — значащие разряды представления числа, стоящие после разделителя целой и дробной части в нормированной форме.

 
Различают три основных типа представления вещественных чисел в языке Си:

Тип Обозна-
чение в Си
Кол-во бит Биты степени Мантисса Сдвиг
простое float 32 30…23 22…0 127
двойной точности double 64 62…52 51…0 1023
двойной расширен- ной точности long double 80 78…64 62…0 16383

Как видно из таблицы, бит целое у типов float и double отсутствует. При этом диапазон представления вещественного числа состоит из двух диапазонов, расположенных симметрично относительно нуля. Например, диапазон представления чисел типа float можно представить в виде:Пример: представить число -178,125 в 32-разрядной сетке (тип float).
Для представления числа в двоичной системе счисления преобразуем отдельно целую и дробную части:

17810 = 101100102.

0,12510 = 0,0012.

Тогда

178,12510 = 10110010,0012=1,0110010001·2111

Для преобразования в нормированную форму осуществляется сдвиг на 7 разрядов влево).
Для определения степени числа применяем сдвиг:

0111111+00000111 = 10000110.

Таким образом, число -178,125 представится в разрядной сетке как

Произвольный доступ к файлу

Система ввода-вывода С++ позволяет осуществлять произвольный доступ с использованием методов seekg() и seekp().

  • ifstream &seekg(Смещение, Позиция);
  • ofstream &seekp(Смещение, Позиция);

Смещение определяет область значений в пределах файла (long int).
Система ввода-вывода С++ обрабатывает два указателя, ассоциированные с каждым файлом:

  • get pointer g — определяет, где именно в файле будет производиться следующая операция ввода;
  • put pointer p — определяет, где именно в файле будет производиться следующая операция вывода.

Позиция смещения определяется как

Позиция Значение
ios::beg Начало файла
ios::cur Текущее положение
ios::end Конец файла

Всякий раз, когда осуществляются операции ввода или вывода, соответствующий указатель автоматически перемещается.
С помощью методов seekg() и seekp() можно получить доступ к файлу в произвольном месте.
Можно определить текущую позицию файлового указателя, используя следующие функции:

  • streampos tellg() — позиция для ввода
  • streampos tellp() — позиция для вывода

 Пример на С++

12345678910111213141516171819202122

#include <iostream>#include <fstream>using namespace std;int main(){  system(«chcp 1251»);  system(«cls»);  char s;  fstream inOut;  inOut.open(«file.txt», ios::out);  inOut << «строчка текста» << endl;  inOut.seekp(8, ios::beg);  inOut << «еще строчка текста»;  inOut.close();  inOut.open(«file.txt», ios::in);  inOut.seekg(-6, ios::end);  inOut >> s;  inOut.close();  cout << s;  cin.get();  return 0;}

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

Ещё один пример. Допустим, нам нужно заполнять таблицу

ФИО Дата рождения Хобби
     

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

  • формируем очередную строку для вывода
  • открываем файл для чтения, считываем из него данные и сохраняем их в массив строк
  • закрываем файл
  • открываем файл для записи
  • выводим «шапку» таблицы
  • выводим новую строку
  • выводим все сохраненные строки обратно в файл, начиная со строки после шапки

Пример на C++

12345678910111213141516171819202122232425262728293031323334353637383940414243444546

#include <iostream>#include <fstream>using namespace std;#define LINES 100 // максимальное количество строк в файлеint main() {  system(«chcp 1251»);  system(«cls»);  char line;  char str;  char s[] = «|                              |                |                              |»;  // Ввод данных для размещаемой строки  cout << «ФИО: «;  cin.getline(str, 30); // вводим ФИО  for (int i = 0; str != ‘\0’; i++) // копируем в строку без 0    s = str;               // начиная с указанной позиции  cout << «Дата: «;  cin.getline(str,30);  for (int i = 0; str != ‘\0’; i++)    s = str;  cout << «Хобби: «;  cin.getline(str,30);  for (int i = 0; str != ‘\0’; i++)    s = str;    fstream inOut;  inOut.open(«file.txt»,  ios::in); // открываем файл для ввода  // Считываем из файла имеющиеся данные  int count = 0;  while (inOut.getline(line, 100)) count++;  inOut.close(); // закрываем файл    inOut.open(«file.txt», ios::out); // открываем файл для вывода  inOut << «———————————————————————————» << endl;  inOut << «|   ФИО                        |  Дата          | Хобби                        |» << endl;  inOut << «———————————————————————————» << endl;  inOut << s << endl; // выводим сформированную строку  inOut << «———————————————————————————» << endl;  // Выводим обратно в файл все строки кроме «шапки» (первые 3 строки)  for (int j = 3; j < count; j++)  {    inOut << line << endl;  }  inOut.close();  cin.get();  return 0;}

Полученный файл данных:

Язык C++

Проектная структура управления

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

Преимущества структуры управления по проектам:

  • высокая гибкость;
  • сокращение численности управленческого персонала
    по сравнению с иерархическими структурами.

Недостатки структуры управления по проектам:

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

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

Чтение из потока

при помощи fgetc

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

int fgetc(FILE *fp);

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

Стандартный макрос также определён в , успешно работая как , кроме одного: будучи макросом, он может обрабатывать свои аргументы более одного раза.

Стандартная функция также определена в , она не принимает аргументов, и эквивалентна .

«Ловушка» EOF

Распространённой ошибкой является использование , или для присваивания результата переменной типа перед сравнением его с . Следующий фрагмент кода демонстрирует эту ошибку, а рядом приведён корректный вариант:

Ошибка Правильно
char c;
while ((c = getchar()) != EOF) {
    putchar(c);
}
int c;
while ((c = getchar()) != EOF) {
    putchar(c);
}

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

Когда результат присваивается переменной типа , которая может представить лишь 256 различных значений, происходит вынужденная потеря информации — при сжатии 257 значений в 256 «мест» происходит коллизия. Значение при конвертации в становится неотличимым от любого из остальных 256 символов. Если этот символ обнаружен в файле, код, приведённый выше, может принять его за признак конца файла, или, что ещё хуже, если тип  — беззнаковый, тогда с учётом того, что  — значение отрицательное, оно никогда не сможет стать равным любому беззнаковому , и таким образом, пример выше не закончится на метке конца файла, а будет выполняться вечно, повторно печатая символ, получающийся при конвертации в .

В системах, где и одинакового размера[каких?][источник не указан 3512 дней], даже «правильный» вариант будет работать некорректно из-за сходства и другого символа. Правильным вариантом обработки подобной ситуации является проверка и после того, как вернет . Если определит, что конец файла ещё не достигнут, а «сообщит», что ошибок нет, то , возвращённый , может считаться текущим символом. Такие дополнительные проверки делаются редко, так как большинство программистов считает, что их код никогда не будет выполняться на подобных системах с «большим ». Другой способ состоит в использовании проверки при компиляции, что , которая хотя бы предотвратит компиляцию на подобных системах.

при помощи fgets

Функция применяется для чтения строки из потока. Считывание происходит до тех пор пока не будет достигнут конец строки (hex:0D0A, эквивалентны в листингах \n) или длина строки, в которую происходит считывание.
Предположим, у нас есть файл some_file.txt с текстом

палиндромы
    А в Енисее - синева.
    А лама мала.
    А лис, он умен - крыса сыр к нему носила. (И. Бабицкий)
#include <stdio.h>
#include <string.h>

int main (int argc, char* argv[])	/* argc хранит количество параметров, а argv[] указатели на эти параметры. 
					Например, если мы запустим исполняемый файл "fgets_example param1 param2", то argc будет равно 3, а argv[] = {"fgets_example", "param1", "param2"}*/
{
	
	FILE *file; 
	char *fname = "some_file.txt";
	char result_string20]; //Строка в 20 символов

	file = fopen(fname,"r");

	if(file == NULL)
	{
		printf("не могу открыть файл '%s'",fname);
		return ;
	}

	int i=;
	char *real_tail;

	while(fgets(result_string,sizeof(result_string),file))
	{
		real_tail="";
		printf("Строка %d:Длина строки - %d:",i++,strlen(result_string));

		if(result_stringstrlen(result_string)-1 == '\n')//проверяем является ли последний элемент в строке символом её окончания
		{
			real_tail="\\n";
			result_stringstrlen(result_string)-1='\0';
		};// эта часть кода добавлена лишь для отображения символа конца строки в консоль без перевода на новую строку	
		printf("%s%s\n",result_string,real_tail);
	}

	fclose(file);

    return ;
}

в результате выполнения мы получим

Строка 0:Длина строки - 11:палиндромы\n
Строка 1:Длина строки - 19:    А в Енисее - си
Строка 2:Длина строки - 6:нева.\n
Строка 3:Длина строки - 17:    А лама мала.\n
Строка 4:Длина строки - 19:    А лис, он умен
Строка 5:Длина строки - 19:- крыса сыр к нему
Строка 6:Длина строки - 19:носила. (И. Бабицки
Строка 7:Длина строки - 2:й)

Функция strlen определяет длину строки по количеству символов до ‘\0’ так например

printf("%d",strlen("123 \0 123")); //выведет 4

inet_aton

Функция

inet_aton

конвертирует строку в сетевой адрес. Возвращает int. 1 если конвертация прошла успешно. 0 если
конвертация не получилась.

В качестве параметров использует указатель const char
и структуру in_addr *addr

Эта функция считается устаревшей, на смену ей пришли inet_pton() и inet_ntop()

inet_aton()
convert Internet dot address to network address

Function

SYNOPSIS
#include <arpa/inet.h>

int inet_aton(const char *cp, struct in_addr *addr);

DESCRIPTION
The inet_aton() function converts the specified string,
in the Internet standard dot notation, to a network address,
and stores the address in the structure provided.

The converted address is in network byte order (bytes ordered from left to right).

Values specified using dot notation take one of the following forms:

a.b.c.d
When four parts are specified, each is interpreted as a byte of data and assigned,
from left to right, to the four bytes of an internet address.

a.b.c
When a three-part address is specified, the last part is interpreted as a 16-bit quantity
and placed in the rightmost two bytes of the network address. This makes the three-part
address format convenient for specifying Class B network addresses as 128.net.host.

a.b
When a two-part address is supplied, the last part is interpreted as a 24-bit quantity
and placed in the rightmost three bytes of the network address.

This makes the two-part address format convenient for specifying Class A network addresses as net.host.

a
When only one part is given, the value is stored directly in the network address without
any byte rearrangement.

All numbers supplied as parts in dot notation may be decimal, octal, or hexadecimal,
as specified in the ISO C standard (that is, a leading 0x or 0X implies hexadecimal;
otherwise a leading 0 implies octal; otherwise, the number is interpreted as decimal).

PARAMETERS
cp
Points to a string in Internet standard dot notation.

addr
Buffer where the converted address is to be stored.

RETURN VALUES
The inet_aton() function returns 1 if the address is successfully
converted, or 0 if the conversion failed.

CONFORMANCE
4.4BSD

MULTITHREAD SAFETY LEVEL
MT-Safe.

PORTING ISSUES
None.

AVAILABILITY
PTC MKS Toolkit for Professional Developers
PTC MKS Toolkit for Professional Developers 64-Bit Edition
PTC MKS Toolkit for Enterprise Developers
PTC MKS Toolkit for Enterprise Developers 64-Bit Edition

Пример 1 — фильтруем цифры

uflowunderflowдонесколько раз подряд

Используем буферы

  • eback() (end back pointer) — указатель на первый элемент буфера
  • gptr() (get pointer) — указатель на на элемент буфера, который будет считан следующим
  • egptr() (end get pointer) — указатель на элемент, следующий за последним элементом буфера. Когда достигает его, это означает, что буфер исчерпан и его нужно снова заполнить

Наглядная иллюстрация с сайта mr-edd.co.uk

  • setg(eback, gptr, egptr) — устанавливает значения соответствующих указателей
  • gbump(offset) — сдвинуть указатель на позиций. Фактически, после выполнения функции примет значение
  • pbase() (put base pointer) — указатель на первый элемент буфера
  • pptr() (put pointer) — указатель на на элемент буфера, который будет записан следующим
  • epptr() (end put pointer) — указатель на элемент, следующий за последним элементом буфера.

Еще одна наглядная иллюстрация с сайта mr-edd.co.uk

setp(pbase, epptr) — устанавливает значения соответствующих указателей

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

устанавливается на начало буфера)

pbump(offset) — сдвинуть указатель на позиций. Фактически, после выполнения функции примет значение

Возврат в вызывающую функцию

По окончании выполнения вызываемой функции осуществляется возврат значения в точку ее вызова. Это значение присваивается переменной, тип которой должен соответствовать типу возвращаемого значения функции. Функция может передать в вызывающую программу только одно значение. Для передачи возвращаемого значения в вызывающую функцию используется оператор return в одной из форм:

return(ВозвращаемоеЗначение);

return ВозвращаемоеЗначение;

returnreturnvoidreturn

return;

Пример

123456789101112131415161718

#define _CRT_SECURE_NO_WARNINGS // для возможности использования scanf#include <stdio.h>// Функция вычисления суммы двух чиселint sum(int x, int y) // в функцию передаются два целых числа{  int k = x + y;  // вычисляем сумму чисел и сохраняем в k  return k;       // возвращаем значение k}int main(){  int a, r;      // описание двух целых переменных  printf(«a= «);  scanf(«%d», &a); // вводим a  r = sum(a, 5);    // вызов функции: x=a, y=5  printf(«%d + 5 = %d», a, r); // вывод: a + 5 = r  getchar(); getchar(); // мы использовали scanf(),  return 0;  // поэтому getchar() вызываем дважжы}

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

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

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

12345678910111213141516171819

#define _CRT_SECURE_NO_WARNINGS // для возможности использования scanf#include <stdio.h>int sum(int, int);   // сигнатураint main(){  int a, r;  printf(«a= «);  scanf(«%d», &a);  r = sum(a, 5);    // вызов функции: x=a, y=5  printf(«%d + 5 = %d», a, r);  getchar(); getchar();  return 0;}int sum(int x, int y) // семантика{  int k;  k = x + y;  return(k);}

seekoff и seekpos для перемещения по файлу

  • streampos seekpos(streampos sp, openmode which) — вызывается при попытке перемещения в позицию, заданную абсоолютной величиной, т.е. позицией от начала последовательности.Возвращаемое значение: в случае успеха, новая установленная позиция, иначе -1.Поведение по-умолчанию: ничего не делает и возвращает -1.
  • streampos seekoff(streamoff off, seekdir way, openmode which) — вызывается при попытке перемещения в позицию, заданную относительно некоторой точки отсчета, которая задается аргументом .Возвращаемое значение: в случае успеха, новая установленная абсолютная позиция, иначе -1.Поведение по-умолчанию: ничего не делает и возвращает -1.
  • openmode — тип указателя, который необходимо передвинуть: (позиция чтения) и (позиция записи). Заметьте что аргумент является битовой маской: т.е. может содержать как одно из значений, так и сразу оба.
  • seekdir — применяется при относительном сдвиге и указывает точку отсчета для перемещения указателя. Может принимать одно из трех значений: (от начала потока), (от текущей позиции) или (от конца потока).
Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *