Смекни!
smekni.com

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

#include <stdio.h>

#include <string.h>

#include <alloc.h>

#include <process.h>

struct Student { /* запись Student */

char Fio[30]; /* полезаписи Fio */

int Gruppa; /* поле записи Gruppa */

}; /* конец записи */

struct Student *A;

main()

{

if ((A =(Student *) malloc(sizeof(Student))) == NULL)

{

printf("Нетпамяти&bsol;n");

exit(1);

}

strcpy(A[1].Fio, "Ivanov");

A[1].Gruppa=385;

printf("Fio1 %s&bsol;n Gruppa %d&bsol;n", A[1].Fio, A[1].Gruppa);

strcpy(A[2].Fio, "Petrow");

A[2].Gruppa=386;

printf("Fio2 %s&bsol;n Gruppa %d&bsol;n", A[2].Fio, A[2].Gruppa);

free(A);

}

Указатель также может использоваться для получения косвенного указателя на структуру.

Пусть poit является указателем на структуру и что elem элемент, определенный структурным шаблоном. Тогда point->elem определяет элемент, на который выполняется ссылка. Рассмотрим предыдущий пример.

struct Student { /* запись Student */

char Fio[30]; /* полезаписи Fio */

int Gruppa; /* поле записи Gruppa */

}; /* конец записи */

Student *poin;

Сейчас к полям структуры мы можем обращаться несколькими способами. Эквивалентные обращения:

Student.Gruppa=236;

poin->Gruppa=236;

Отметим одну важную особенность указателей в Си. Транслятор автоматически учитывает тип указателя в арифметических действиях над ним. Например если i есть указатель на целую (т.е. двухбайтную) переменную, то действие типа i++ означает, что указатель получает приращение не один, а два байта, т.е. будет указывать на следующую переменную или элемент массива. По этой причине указатель можно использовать вместо индексов массива. Например если A - указатель на массив целого типа, то вместо A[i] можно писать *(A+i). Более того, использование указателей вместо индексов позволяет компилятору создавать более компактный и быстрый код.

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

Указатели также можно использовать в качестве формальных параметров функции. Рассмотрим пример.

Функция swap (обмен) объявляет два формальных параметра x и y, как указатели на данные типа int. Это означает, что функция swap работает с адресами целых переменных (а не с их значениями). Поэтому будут обработаны данные, адреса которых переданы функции во время обращения к ней. Функция main(), вызывает swap.

#include <stdio.h>

swap(int *x, int *y)

{

int wr;

wr = *x; *x = *y; *y =wr;

}

main()

{

int i,j;

i = 100;

j = 200;

printf("Было: i=%d, j=%d&bsol;n",i,j);

swap(&i,&j);

printf("Стало: i =%d, j=%d&bsol;n",i,j);

}

После выполнения программы значения i и j поменяются местами. Необходимо отметить, что хотя указатели и экономят память, но они используют время процессора горазда больше.

Таким образом, рассмотрены основные элементы языка Си.

1.10 Файлы

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

Расмотрим сперва режим последовательного доступа.

Ввод-вывода буферизован. Это означает, что программа пишет и читает в буфер; обмен данными между буфером и файлом происходит в случае, если буфер полон, или произошло закрытие файла, или перед выходом из программы.

Рассмотрим пример: считать данные из одного файла и переписать в другой.

#include <stdio.h>

main()

{

FILE *in, *out;

char n1[8], n2[8];

printf("Исходныйфайл&bsol;n");

gets(n1);

printf("Выходнойфайл&bsol;n");

gets(n2);

if ((in = fopen(n1, "rt"))== NULL)

{

printf("Не могу открыть исходный файл&bsol;n"); return 1;

}

if ((out = fopen(n2, "wt"))== NULL)

{

printf("Не могу открыть выходной файл&bsol;n"); return 1;

}

while (!feof(in))

fputc(fgetc(in), out);

fclose(in);

fclose(out);

}

Строка FILE *in, *out; определяет указатель на два файла. Имя файла м.б. любым- в нашем случае in - исходный файл, out - выходной.

В следующей строке char n1[8], n2[8]; определяем две переменные n1 и n2 для хранения имен файлов. Следующие четыре строки позволяют ввести имена входного и выходного файла и присвоить эти имена переменным n1 и n2. Прежде чем начать работать с файлом он должен быть открыт. Для этого существует функция fopen() в которой первый параметр содержит имя файла, а второй - вид работы, например "rt"– читать файл.

Команда in = fopen(n1, "rt" вызовет открытие файла, запомненного в переменной n1 на чтение, и в программе будет возвращен указатель in на этот файл, которым (указателем) мы и будем пользоваться при чтении символов из файла. Если файл не существует, то значение fp будет NULL, произойдет выполнение fprintf(stderr, "Не могу открыть выходной файл&bsol;n"); return 1; и программа завершит работу.

Аналогично работает функция out = fopen(n2, "wt", только теперь out - выходной файл, а вид работы "wt" -запись в файл).

По этой команде создается файл под именем, записанным в переменной n2.

Чтение из файла осуществляется вызовом fgetc(in). Читается один символ из файла, связанного с указателем in.

По команде fputc(fgetc(in), out); считанный символ записывается в файл out. Для чтения информации из всего файла используется конструкция while (!feof(in))

fputc(fgetc(in), out);.

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

Закрытие файла происходит при вызове функции fclose(). Если при этом файл был открыт на запись, происходит вывод содержимого буфера, связанного с этим файлом. Связь между указателем и файлом разрывается.

Аналогично функция fgetc(string,n,fp) читает из файла, связанного с fp, строку и помещает ее в string. Символы читаются, пока не будет получен символ '&bsol;n', или пока не исчерпается файл, или пока не будет прочитано (n-1) символов.

Режим прямого доступа более гибок, т.к. позволяет обращаться напрямую к любой записи файла. Минимальной записью для файла прямого доступа является байт. Ниже будет рассмотрен случай файла прямого доступа с записью равной байту. Такие файлы называются двоичными. Файлы прямого доступа незаменимы при написании программ, которые должны работать с большими объемами информации, хранящимися на внешних устройствах. В основе обработке СУБД лежат файлы прямого доступа.

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

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

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

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

Ниже представлена программа, демонстрирующая работу с файлами.

#include <stdio.h>

#include <io.h>

#include <string.h>

#include <fcntl.h>

#include <SYS&bsol;STAT.H>

void main()

{

int h; /*дескриптор создаваемого файла*/

char * s="Эта строка будет помещена в файл";

char buf[7]; /*буфер для чтения из файла*/

_fmode=O_BINARY; /*работаем с двоичными файлами*/

if((h=creat("proba.txt",S_IREAD |S_IWRITE))==-1) /*создатьфайл*/

{

printf("Не удалось открыть файл!&bsol;n");

exit(1);

}

write(h,s,strlen(s)); /*записатьвфайлстроку s*/

lseek(h,4,SEEK_SET); /*четвертый байт от начала файла*/

read(h,buf,6); /*читать слово 'строка' (6 байт) из файла*/

buf[6]=0; /*отмечаем конец строки*/

close(h); /*закрыть файл*/

printf("%s&bsol;n",buf); /*печать считанной строки*/

}

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

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

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

3. Наконец не забывайте, что строка только тогда станет строкой, когда в конце стоит код &bsol;0.

В заключении нашего рассмотрения файлов отметим, что наше изложение касалось работы с файлами в среде MSDOS. Работа с файлами в ОС Windows несколько отличается от рассмотренного и основывается на использовании специальных функций API (о программировании в Windows см. ниже).

1.11 Дополнительные функции Си

1.11.1 Функции преобразования

Имеется переменная С:

ФУНКЦИЯ ПРОВЕРЯЕТ, ЯВЛЯЕТСЯ ЛИ C

isalpha(c) буквой

isdigit(c) цифрой

islower(c) строчной буквой

isspace(c) пустым символом (пробел, табуляция или новая строка)

isupper(c) прописной буквой

isalnum(c) алфавитноцифровым (буква или цифра)

isascii(c) кодом ASCII (0-127)

iscntrl(c) управляющим символом

ispunct(c) знаком пунктуации

toupper(c) преобразует c в прописную букву

tolower(c) преобразует c в строчную букву

Пример: преобразует букву A из прописной в строчную a.

#include <stdio.h>

#include <ctype.h>

main()

{

char Z,Y='A';

printf("Было %c&bsol;n ",Y);

Z=tolower(Y);

printf("Стало %c&bsol;n ",Z);

}

1.11.2 Преобразования символьных строк: atoi(), atof()

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

Для этого используются функции atoi() и atof(). Первая преобразует строку в целое, вторая - в число с плавающей точкой. Пример:

#include <stdlib.h>

#include <stdio.h>

main()

{

char ch[10];