Главная

Популярная публикация

Научная публикация

Случайная публикация

Обратная связь

ТОР 5 статей:

Методические подходы к анализу финансового состояния предприятия

Проблема периодизации русской литературы ХХ века. Краткая характеристика второй половины ХХ века

Ценовые и неценовые факторы

Характеристика шлифовальных кругов и ее маркировка

Служебные части речи. Предлог. Союз. Частицы

КАТЕГОРИИ:






Связь между указателями и массивами.




В языке СИ между указателями и массивами существует тесная связь. Например, когда объявляется массив в виде int array[25], то этим определяется не только выделение памяти для двадцати пяти элементов массива, но и для указателя с именем array, значение которого равно адресу первого по счету (нулевого) элемента массива, т.е. сам массив остается безымянным, а доступ к элементам массива осуществляется через указатель с именем array. С точки зрения синтаксиса языка указатель arrey является константой, значение которой можно использовать в выражениях, но изменить это значение нельзя.

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

int arrey[25];

int *ptr;

ptr=array;

Здесь указатель ptr устанавливается на адрес первого элемента масcива, причем присваивание ptr=arrey можно записать в эквивалентной форме ptr=&arrey[0].

Для доступа к элементам массива существует два различных способа. Первый способ связан с использованием обычных индексных выражений в квадратных скобках, например, array[16]=3 или array[i+2]=7. При таком способе доступа записываются два выражения, причем второе выражение заключается в квадратные скобки. Одно из этих выражений должно быть указателем, а второе - выражением целого типа. Последовательность записи этих выражений может быть любой, но в квадратных скобках записывается выражение следующее вторым. Поэтому записи array[16] и 16[array] будут эквивалентными и обозначают элемент массива с номером шестнадцать. Указатель используемый в индексном выражении не обязательно должен быть константой, указывающей на какой-либо массив, это может быть и переменная. В частности после выполнения присваивания ptr=array доступ к шестнадцатому элементу массива можно получить с помощью указателя ptr в форме ptr[16] или 16[ptr].

Второй способ доступа к элементам массива связан с использованием адресных выражений и операции разадресации в форме *(array+16)=3 или *(array+i+2)=7. При таком способе доступа адресное выражение равное адресу шестнадцатого элемента массива тоже может быть записано разными способами *(array+16) или *(16+array).

При реализации на компьютере первый способ приводится ко второму, т.е. индексное выражение преобразуется к адресному. Для приведенных примеров array[16] и 16[array] преобразуются в *(array+16).

Для доступа к начальному элементу массива (т.е. к элементу с нулевым индексом) можно использовать просто значение указателя array или ptr. Любое из присваиваний

*array = 2;

array[0] = 2;

*(array+0) = 2;

*ptr = 2;

ptr[0] = 2;

*(ptr+0) = 2;

присваивает начальному элементу массива значение 2, но быстрее всего выполнятся присваивания *array=2 и *ptr=2, так как в них не требуется выполнять операции сложения.

Структуры.

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

При разработке программ важным является выбор эффективного способа представления данных. Во многих случаях недостаточно объявить простую переменную или массив, а нужна более гибкая форма представления данных. Таким элементом может быть структура, которая позволяет включать в себя разные типы данных, а также другие структуры. Приведем пример, в котором использование структуры позволяет эффективно представить данные. Таким примером будет инвентарный перечень книг, в котором для каждой книги необходимо указывать ее наименование, автора и год издания. Причем количество книг может быть разным, но будем полгать, что не более 100. Для хранения информации об одной книге целесообразно использовать структуру, которая задается в языке С++ с помощью ключевого слова struct, за которым следует ее имя. Само определение структуры, т.е. то, что она будет содержать, записывается в фигурных скобках {}. В данном случае структура будет иметь следующий вид:

struct book {
char title[100]; //наименование книги
char author[100]; //автор
int year; //год издания
};

Такая конструкция задает своего рода шаблон представления данных, но не сам объект, которым можно было бы оперировать подобно переменной или массиву. Для того чтобы объявить переменную для структуры с именем book используется такая запись:

struct book lib; //объявляется переменная типа book

После объявления переменной lib имеется возможность работать со структурой как с единым объектом данных, который имеет три поля: title, author и year. Обращение к тому или иному полю структуры осуществляется через точку: lib.title, lib.author и lib.year. Таким образом, для записи в структуру информации можно использовать следующий фрагмент программы:

printf(“Введите наименование книги: “);
scanf(“%s”,lib.title);
printf(“Введите автора книги: “);
scanf(“%s”,lib.author);
printf(“Введите год издания книги: “);
scanf(“%d”,&lib.year);

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

struct book lib[100];

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

Листинг 3.5. Инвентарный перечень книг.

#include
struct book {
char title[100]; //наименование книги
char author[100]; //автор
int year; //год издания
};

int main()
{
int cnt_book = 0, ch;
struct book lib[100];
do
{
printf(“Введите наименование книги: “);
scanf(“%s”,lib[cnt_book].title);
printf(“Введите автора книги: “);
scanf(“%s”,lib[cnt_book].author);
printf(“Введите год издания книги: “);
scanf(“%d”,&lib.year);
printf(“Нажмите q для завершения ввода: ”);
cnt_book++;
}
while(scanf(“%d”,ch) == 1 && cnt_book < 100);
return 0;
}

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

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

struct bool lib = {
“Евгений Онегин”,
“Пушкин А.С.”,
1995
};

При выполнении данного фрагмента программы в переменные структуры title, author и year будет записана соответственно информация: “Евгений Онегин”, “Пушкин А.С.”, 1995. Здесь следует обратить внимание, что последовательность данных при инициализации должна соответствовать последовательности полей в структуре. Это накладывает определенные ограничения, т.к. при инициализации необходимо помнить последовательность полей в структуре. Стандарт C99 допускает более гибкий механизм инициализации полей структуры:

struct book lib = {.year = 1995,
.author = “Пушкин А.С.”,
.title = “Евгений Онегин” };

или

struct book lib = {.year = 1995,
.title = “ЕвгенийОнегин” };

или

struct book lib = {.author = “ПушкинА.С.”,
.title = “ЕвгенийОнегин”,
1995 };

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

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

struct tag_fio {
char last[100];
char first[100];
char otch[100];
};
struct tag_people {
struct tag_fio fio; //вложеннаяструктура
char job[100];
int old;
};

Рассмотрим способ инициализации и доступ к полям структуры people на следующем примере.

Листинг 3.6. Работа с вложенными структурами.

int main()
{
struct people man = {
{“Иванов”, “Иван”, “Иванович”},
“Электрик”,
50 };
printf(“Ф.И.О.:%s %s %s\n”,man.fio.last,man.fio.first,
man.fio.otch);
printf(“Профессия: %s \n”,man.job);
printf(“Возраст: %d\n”,man.old);
return 0;
}

В данном примере показано, что для инициализации структуры внутри другой структуры следует использовать дополнительные фигурные скобки, в которых содержится информация для инициализации полей фамилии, имени и отчества сотрудника. Для того чтобы получить доступ к полям вложенной структуры выполняется сначала обращение к ней по имени man.fio, а затем к ее полям: man.fio.last, man.fio.first и man.fio.otch. Используя данное правило, можно создавать многоуровневые вложения для эффективного хранения и извлечения данных.

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

Листинг 3.7. Передача структур через аргументы функции.

#include
struct tag_people {
char name[100];
char job[100];
int old;
};
void show_struct(struct tag_people man);
int main()
{
struct tag_people person = {“Иванов”,”Электрик”,30};
show_struct(person);
return 0;
}
void show_struct(struct tag_people man)
{
printf(“Имя: %s\n”,man.name);
printf(“Профессия: %s\n”,man.job);
printf(“Возраст: %d\n”,man.old);
}

В приведенном примере используется функция с именем show_struct, которая имеет тип аргумента struct tag_people и переменную-структуру man. При передаче структуры функции создается ее копия, которая доступная в теле функции show_struct под именем man. Следовательно, любые изменения полей структуры с именем man никак не повлияют на содержание структуры с именем person. Вместе с тем иногда необходимо выполнять изменение полей структуры функции и возвращать измененные данные вызывающей программе. Для этого можно задать функцию, которая будет возвращать структуру, как показано в листинге 3.8.

Листинг 3.8. Функции, принимающие и возвращающие струткуру.

#include
struct tag_people {
char name[100];
char job[100];
int old;
};
void show_struct(struct tag_people man);
struct tag_people get_struct();
int main()
{
struct tag_people person;
person = get_struct();
show_struct(person);
return 0;
}
void show_struct(struct tag_people man)
{
printf(“Имя: %s\n”,man.name);
printf(“Профессия: %s\n”,man.job);
printf(“Возраст: %d\n”,man.old);
}
struct tag_people get_struct()
{
struct tag_people man;
scanf(“%s”,man.name);
scanf(“%s”,man.job);
scanf(“%d”,man.old);
return man;
}

В данном примере используется функция get_struct(), которая инициализирует структуру с именем man, запрашивает у пользователя ввод значений ее полей и возвращает введенную информацию главной программе. В результате выполнения оператора присваивания структуры man структуре person, происходит копирование информации соответствующих полей и автоматическое удаление структуры man.

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

void show_struct(struct people mans[], int size);

Здесь size – число элементов массива, которое необходимо для корректного считывания информации массива mans. Следующий пример показывает принцип работы с массивами структур.

Листинг 3.9. Передача массив структур функции.

#include
#define N 2
struct tag_people {
char name[100];
char job[100];
int old;
};
void show_struct(struct people mans[], int size);
int main()
{
struct people persons[N] = {
{ “Иванов”, «Электрик», 35 },
{ “Петров”, «Преподаватель», 50 },
};
show_struct(persons, N);
}
void show_struct(struct people mans[], int size)
{
for(int i = 0;i < size;i++) {
printf(“Имя: %s\n”,mans[i].name);
printf(“Профессия: %s\n”,mans[i].job);
printf(“Возраст: %d\n”,mans[i].old);
}
}

При передаче аргумента persons выполняется копирование информации в массив mans и указывается дополнительный параметр size, для определения числа элементов массива mans. Затем в функции show_struct() реализуется цикл, в котором выполняется отображение информации массива структуры на экран монитора.

Файлы.

Работа с файлами складывается из трех шагов.

1. Файл открывается. Это означает, что программа "захватывает" заданный по имени файл, сообщает Windows, что далее она будет с ним работать. Данный шаг нужен, чтобы не возникало конфликтов, когда несколько программ одновременно хотят записывать информацию в один и тот же файл. Правда, считывать данные из файла, очевидно, допустимо одновременно множеством программ, поэтому в операции открытия файла обычно уточняется, что файл открывается "на чтение" (считывание информации, которая не меняется) либо "на запись" (данные в файле модифицируются).
Операция открытия файла возвращает некий идентификатор (как правило, целое число), которое идентифицирует в программе в дальнейшем нужный открытый файл. Этот идентификатор запоминается в переменной; обычно такая переменная называется файловой переменной.

2. Ведется работа с файлом. Из него данные либо считываются, либо в него записываются.

3. Файл закрывается. После этой операции он снова доступен другим программам для обработки.

Стандартные операции работы с файлами, существующие практически во всех реализациях Си, хранятся в библиотеке stdio.h. Функция открытия файла называется fopen(). Она возвращает указатель на объект стандартного (определенного в данной библиотеке) типа FILE.
Параметров у функции fopen() два. Первый -- это путь к файлу (строка), второй -- параметры открытия файла.

Допустим, мы хотим создать в текущем каталоге проекта новый текстовый файл и записать в него два числа и строку. Пусть этот файл будет называться test.txt. Параметр открытия файла на запись в текстовом виде записывается строкой "wt". Буква w означает write (писать), буква t -- text (текст). Если такой файл в каталоге существует, он перезаписывается (все старое содержимое в нем уничтожается), если он не существует, то создается исходно пустым.

Тогда команда открытия (создания пустого) файла запишется так:

FILE * fo;
fo = fopen("test.txt","wt");

Можно задать и полный путь к файлу, например:

fo = fopen("c:\\tmp\\test.txt","wt");

Не забываем, что одиночный символ \ внутри строки Си задается двумя наклонными слэшами \\. Это частая ошибка.

После открытия файла в файловую переменную fo занесется некоторое число. Если таким числом будет ноль, считается, что файл открыть не удалось. В Си нередки записи вида

if( (fo=fopen("c:\\tmp\\test.txt","wt")) == 0) {
// ошибка!
}

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

Закрывается файл с помощью функции fclose():
fclose(fo);
После закрытия файла к файловой переменной обращаться уже нельзя.

Запись текстовой строки в файл выполняет функция fprintf():
fprintf(имя-файловой-переменной, формат, список-переменных-для-вывода);

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

fprintf(fo, "Привет!");

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

Чтобы вывести значение некоторой переменной, надо использовать элемент формата, который начинается с символа %. Так, формат %d задает вывод целочисленного значения, формат %s - вывод строки.
Например:

int n = 10;
char str[20] = "значение переменной n равно ";
fprintf(fo, "Вывод: %s %d", str, n);

В файл запишется строка ""Вывод: значение переменной n равно 10". Вместо формата %s подставлено значение переменной str, а вместо формата %d - значение переменной n.

Для вывода дробных чисел используют формат %f, где перед f ставят число всех выводимых символов и число символов после запятой, разделенные точкой. Например: "%5.2f" означает вывод числа из пяти позиций, причем две из них будут отведены для дробного значения. Если оно не уместится в этих позициях, то вылезет за границу, а если займет меньше, то дополнится пробелами (хотя в разных реализациях может быть по разному).

Следующая программка записывает в файл 100 строк с числами. Результирующий файл будет текстовым, его можно открыть и просмотреть, например, в Блокноте.

FILE * fo;
fo = fopen("test.txt","wt");
int i;
for( i=0; i<100; i++) {
fprintf(fo, "%d\n", i);
}
fclose(fo);

Комбинация символов "\n" в конце строки формата означает перевод на новую строку в выводимых текстовых данных.

Для ввода данных (текстовой строки) используют функцию fscanf(). Она напоминает fprintf():

fscanf(файловая-переменная, формат-ввода, список-адресов-переменных)

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

Операция &, примененная к имени переменной, возвращает адрес этой переменной в памяти. Тогда операцию ввода числа из текстового файла fi в целочисленную переменную n можно записать так:

fscanf(fi, "%d", &n);

Формат записывается также, как и в случае с fprintf, а вот перед именем переменной n дополнительно указывается операция получения адреса &.

Полезная функция feof(файловая-переменная) возвращает 1 (истинное значение), если файл, открытый для считывания, закончился. Она возвращает 0, если из файла еще можно продолжать ввод.

Программа считывания из файла всех строк с числами запишется так:

FILE * fi;
fi = fopen("test.txt","rt"); // rt означает открытие текстового файла на
чтение
int n;
while(!feof(fi)) {
fscanf(fi, "%d", &n);
}
fclose(fi);

ФАЙЛОВЫЙ УКАЗАТЕЛЬ

 

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

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

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

Замечание. Нумерация элементов в файле начинается с нуля!

Например, после выполнения оператора Rewrite(k) из программы Pr1 предыдущего шага положение указателя будет следующим (рис.1):


Рис.1. Положение указателя после открытия файла

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


Рис.2. Положение указателя после записи элемента

Таким образом, при выполнении процедуры Write(k,a) из программы Pr1 указатель каждый раз будет смещаться за последний введенный элемент. В этом случае говорят, что указатель находится в конце файла. Если указатель расположен на первом элементе файла (его порядковый номер нулевой), то говорят, что указатель находится в начале файла.

 

Аналогично, перед первым выполнением процедуры Read(k,a); из программы Pr2 предыдущего шага указатель находился в начале файла (рис.3):


Рис.3. Положение указателя перед чтением элемента

После выполнения данной процедуры картина будет следующей (рис.4):


Рис.4. Положение указателя после чтения элемента

Подведем итог:

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

 






Не нашли, что искали? Воспользуйтесь поиском:

vikidalka.ru - 2015-2024 год. Все права принадлежат их авторам! Нарушение авторских прав | Нарушение персональных данных