Среда, 27.11.2024, 22:39
Клан программистов ГлавнаяРегистрацияВход
Приветствую Вас Гость | RSS
Меню сайта
Категории каталога
Юмор [6]
Полезные статьи [94]
Полезная информация по вебразработке, продвижению своего проекта и т.д.
Photoshop [29]
Языки программирования [20]
Коды, читы для PSP [27]
Интересное в сети [12]
Самые интересные новости и достижения найденые в инете
Обзоры игр [31]
Железо [93]
Игровые приставки [4]
Мобильные телефоны [835]
Интернета и icq!Настройка [15]
описание как настроить ваш телефон для нормальной работы интернета и icq
Наш опрос
Нужен ли вам Ftp-сервер с бесплатным файлом?
Всего ответов: 35
 Каталог статей 
Главная » Статьи » Языки программирования

Массивы, указатели и структуры. часть 3
 

506. Определение количества элементов массива

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

#define NUM_ELEMENTS 5

Кроме того, в программе можно использовать операцию sizeofдля определения количества элементов массива:

elements = sizeof(array) / sizeof(array[0]);

В следующей программе NUM_ELS.C этот метод используется для вывода на экран количества элементов массива:

#include <stdio.h>

void main(void) 
{
int int_values[] = {51, 23, 2, 44, 45};
float float_values[1 = (21.1, 13.3, 22.2, 34.4, 15.5);

printf("Количество элементов массива int_values %d\n", 
 sizeof(int_values) / sizeof(int_values[0]));

printf("Количество элементов массива float values %d\n", 
 sizeof(float_values) / sizeof(float_values[0]));
}

507. Указатель как адрес

В разделе "Язык Си: начало" этой книги понятие переменная определялось как имя ячейки памяти, в которой хранится значение указанного типа. Каждая ячейка памяти имеет свой уникальный адрес. Указатель - это переменная, значениями которой являются адреса. В языке программирования Си указатели используются очень часто. При передаче функции массива или строки в качестве параметра Си-компилятор в действительности передает указатель. Подобным образом, если функция должна изменить значение параметра, то программа должна передавать функции указатель на память, выделенную переменной. В дальнейшем это описывается более подробно.

508. Определение адреса переменной

Указатель - это адрес участка памяти. При работе с массивами (или строками) программа работает с указателем на первый элемент массива. Для определения адреса переменной программа может использовать операцию нахождения адреса (&). Например, в следующей программе ADDRESS.С операция нахождения адреса используется для вывода на экран адресов различных переменных:

#include <stdio.h>

void main(void) 
{
int count = 1;
float salary = 40000.0;
long distance = 1234567L;

printf("Адрес переменной count is %ac\n", &count);
printf("Адрес переменной salary is %x\n", &salary);
printf("Адрес переменной distance is %x\n", &distance);
}

В этой программе используется формат вывода %х, ввиду чего она может запускаться только в малой (Small) модели памяти. Это же относится ко всем последующим программам этого раздела. - Прим. перев.} После компиляции и выполнения программы на экран выводится:

C:\> ADDRESS <Еnter>
Адрес переменной count is fff4 
Адрес переменной salary is fff0 
Адрес переменной distance is ffec

509. Массив как указатель

Как указывалось ранее, компилятор Си рассматривает массивы как указатели. Например, при передаче массива в функцию в качестве фактического параметра компилятор передает начальный адрес массива. В следующей программе ARRAYADD.C выводятся начальные адреса различных массивов:

#include <stdio.h>

void main(void) 
{
int count[10];
float salaries[5];
long distances[10];

printf("Начальниц адрес массива count - %x\n", count);
printf("Начальный адрес массива salaries - %x\n", salaries);
printf("Начальный адрес массива distances - %x\n", distances);

После компиляции и выполнения программы на экран выводится:

C:\> ARRAYADD <Enter> 
Начальный адрес массива count - ffe2 
Начальный адрес массива salaries - ffce 
Начальный адрес массива distances - ffa6

510. Операция нахождения адреса (&) для массива

Как мы уже знаем Си-компилятор рассматривает массив как указатель на первый элемент этого массива. В С508 было показано использование операции нахождения адреса (&) для получения адреса переменной. Если эта операция применяется к массиву, то Си возвращает начальный адрес массива. Таким образом, для массива операция нахождения адреса является избыточной. рта операция все же может использоваться при объявлении указателя на массив. - Прим. перев.] В следующей программе ARRAYTWO.C выводится начальный адрес массива и указатель на массив, возвращаемый операцией &:

#include <stdio.h>

void main(void) 
{
int count[10];
float salaries[5];
long distances[10];

printf("Начальный адрес массива count %x &count есть %х\n",
 count, &count);
printf("Начальный адрес массива salaries %x &count есть %х\n",
 salaries, &salaries);
printf("Начальный адрес массива distances %э{ &distances есть %х\n",
 distances, &distances);
}

После компиляции и выполнения программы на экран выводится:

C:\> AKRAYTWO <Ентга>
Начальный адрес массива count - ffe2 &count есть ffe2 
Начальный адрес массива salaries - ffce &salaries есть ffce 
Начальный адрес массива distances - ffa6 &distances есть ffa6

511. Объявление указателей

По мере усложнения программы вы обнаруживаете, что в основном работаете с указателями. Для хранения указателей в программе необходимо объявить соответствующие переменные (переменная-указатель). Для объявления указателя необходимо указать тип значения, на которое указывает указатель (например, int-, float, char и т. д.) и звездочку (*) перед именем переменной.

Например, в следующем операторе объявляется указатель типа int:

int *iptr;

Перед использованием указателя (как и любой переменной) ему необходимо присвоить значение. При присваивании значения указателю ему в действительности присваивается некоторый ^дрес. Предположим, что ранее в программе объявлена переменная int count; в следующем операторе указателю iptr присваивается значение адреса переменной count:

iptr = &count; // Присваивание переменной iptr адреса count

В следующей программе IPTR.C объявляется переменная-указатель iptr, затем этому указателю присваивается адрес переменной count. После этого на экран выводится как значение переменной iptr, так и адрес count:

#include <stdio.h>

void main(void)
{
int *iptr; // Описание переменной-укаэателя 
int count = 1;

iptr = &count;

printf("Значение iptr равно %х Значение count равно %d"
 " Address of count равен %х\n", 
 iptr, count, &count);
}

После компиляции и выполнения программы на экран выводится:

C:\> IPTR <Enter> 
Значение iptr равно fff2 Значение count равно 1 Адрес count равен fff2

512. Доступ к значениям, адресуемым указателями

Как мы уже знаем, указатель содержит адрес, который указывает на значение заданного типа. Используя адрес, содержащийся в указателе, можно определить значение, содержащееся по этому адресу. Для этого используется операция "звездочка" С"). Например, следующим оператором выводится значение, содержащееся по указателю iptr:

printf("Значение, содержащееся по указателю iptr, ", *iptr);

Аналогично, в следующем операторе переменной count присваивается значение, содержащееся по адресу указателя iptr:

count = *iptr;

Наконец, в следующем операторе в память по указателю iptr заносится значение 7:

*iptr = 7;

Примечаиие: Для того чтобы использовать значение, лежащее в ячейке памяти, на которую ссылается указатель, применяется операция "звездочка" (*).

513. Использование значений по указателю

В С512 мы видели, что переменной-указателю можно присвоить адрес, используя операцию адресации (&). Там же была представлена операция "звездочка" (*) для доступа к значению по адресу, на который ссылается указатель. В следующей программе PTR_DEMO.C указателю iptr назначения типа int присваивается адрес переменной counter. После этого программа выводит на экран значение указателя и значение, которое хранится по указателю (значение counter). Далее программа изменяет значение по указателю:

#include <stdio.h>

void main(void) 
{
int counter = 10;
int *iptr; // Объявление указателя

iptr = &counter; // Присвоение указателю адреса 
printf("Адрес в iptr %х Значение в *iptr %d\n", iptr, *iptr)

*iptr = 25; // Изменение значения в памяти

printf("Значение counter %d\n", counter);

514. Указатели как параметры функций

Процесс передачи параметров в функциях рассматривался подробно в разделе "Функции". Как станет известно в дальнейшем, если значение параметра должно изменяться, то в функцию передается указатель на параметр. В следующей программе SWAPVALS.C используются указатели на два параметра типа int для операции обмена значениями пере менных:

#include <stdio.h>

void swap_values(int *a, int *b) 
{
int temp;

temp = *a; // Временная переменная для хранения значения а
*а = *b; // Значение Ь присваивается а
*b = temp; // Значение а присваивается Ь }
}

void main(void)
{
int one = 1, two = 2;

swap_values(&one, &two);

printf("one равно %d two равно %d\n", one, two);
}

Как видно, внутри функции для нахождения значения по указателю используется операция 'звездочка" (*). Программа передает адреса переменных в функцию, используя операцию адресации (&).

515. Арифметические операции над указателями

Указатель - это адрес, по которому хранится значение определенного типа. Другими словами, указатель - значение, указывающее на определенную ячейку памяти. Если к указателю прибавить число 1, то он будет ссылаться на следующую ячейку памяти. Если к указателю прибавить 5, то он будет ссылаться на ячейку, отстоящую от текущего адреса на пять ячеек выше. Арифметика указателей не так проста, как кажется на первый взгляд. Предположим для примера, что значение указателя равно 1000. Если к указателю прибавить 1, то ожидаемый результат - адрес 1001. В действительности результат зависит от типа указателя. Например, если указатель имеет тип char, то результатом сложения действительно будет 1001. Однако если прибавить 1 к указателю типа int (тип, требующий 2 байта памяти), то результирующий адрес будет 1002. Аналогично, если прибавить 1 к указателю типа float (тип, требующий 4 байта памяти), то результирующий адрес будет 1004. При выполнении арифметики над указателями необходимо иметь в виду тип указателя. Кроме рассмотренной операции сложения указателя и числа, арифметика над указателями допускает следующие операции: нахождение разности указателя и числа, нахождение суммы и разности двух указателей. В дальнейшем приводятся примеры с использованием различных арифметических операций над указателями.

516. Увеличение и уменьшение указателя на 1

При работе с указателями одна из самых часто используемых операций - увеличение и уменьшение значения на 1 для обращения к следующему или предыдущему участку памяти. В следующей программе PTRARRAY.C указателю iptr присваивается начальный адрес массива. Затем значение указателя в цикле увеличивается на 1 и, таким образом, выводятся на экран значений 5 элементов массива:

#include <stdio.h>
void main(void) 
{
int values[5] = {1, 2, 3, 4, 5};
int counter;
int *iptr;

iptr = values;

for (counter = 0; counter < 5; counter++) 
 {
 printf("%d\n", *iptr);
 iptr++;
 }
}

После компиляции и выполнения программы на экран выводятся числа: от 1 до 5. Сначала указателю присваивается начальный адрес массива. Затем программа увеличивает значение указателя на 1 для того, чтобы он ссылался на следующий элемент массива и т.д.

517. Комбинирование операций доступа по указателю и приращения

В С516 для вывода значений элементов массива был использован указатель iptr в цикле for.

for (counter =0; counter < 5; counter++) 
 {
 printf("%d\n", *iptr);
 iptr++;
 }

Как видно, в цикле for происходит обращение к элементу массива и увеличение на 1 указателя для доступа к следующему элементу массива. Используя постфиксную операцию увеличения, эти две операции можно записать одной строкой. В следующем фрагменте в цикле for сначала значение по указателю выводится на экран, и лишь затем значение самого указателя увеличивается на 1.

for (counter = 0; counter < 5; counter++) 
 printf("%d\n", *iptr++);

518. Использование в цикле указателя на строку

В разделе "Строки" указатели используются достаточно часто. Как нам известно, строка - массив символов, оканчивающийся NULL-СИМВОЛОМ. В следующей программе STR_PTR.C определяется функция show_string для вывода символов строки с использованием указателя на строку:

#include <stdio.h>

void show string(char *string) 
 {
 while (*string)
 putchar(*string++);
 }

void main(void) 
{
show_string("1001 совет по C/C++");
}

Как видим, переменная string объявлена в функции как указатель. Указатель увеличивается в простом цикле, до тех пор пока не встретится конец строки. Перед тем как вывести очередной символ, этот символ определяется в функции посредством операции (*). Затем указатель увеличивается, тем самым ссылаясь на следующий символ.

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

Известно, что функции могут возвращать в программу указатели. Возвращаемые значения функции должны быть всегда одного и того же типа: inl, float или char. Кроме возвращения указателей на значения базовых типов, функция может возвращать указатель на структуру. Например, функция fopen, которую большинство программ Си используют для открытия файлового потока, возвращает указатель на структуру типа FILE:

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

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

520. Создание функции, возвращающей указатель

Из С519 ясно, что многие библиотечные функции Си возвращают указатели. По мере усложнения программы приходится создавать функции, которые возвращают указатели на определенные типы. Например, в следующей программе PTR_UPR.C создается функция string_uppercase, которая преобразует все строчные латинские буквы в прописные и возвращает указатель на строку [строки в данной программе не переведены, поскольку функция toupper не преобразует русские буквы. - Прим. перев.}:

#include <stdio.h> 
#include <ctype.h>

char *string_uppercase(char *string)
 {
 char *starting_address;

 starting_address = string;

 while (*string)
 toupper(*string++);

 return(starting_addresa);
 }

void main(void) 
{
char *title = "1001 Совет по C/C++";
char *string;

string = string_uppercase(title);
printf("%s\n", string);

printf("%s\n", string_uppercase("Arrays and Pointers"));
}

Как можно видеть, для создания функции, возвращающей указатель, перед именем функции необходимо поместить символ "звездочка" (*).

char *string uppercase(char * string);

521. Массивы указателей

О массивах в этом разделе уже достаточно подробно рассказывалось. До сих пор все представленные массивы имели один из базовых типов Си (например, int, float или char). Возможные типы массивов в Си этим не ограничиваются. Так же, как функции, возвращающие указатели, можно создавать и массивы указателей. Наиболее частое использование массивов указателей-массивы строк. Например, в следующем объявлении создается массив weekdays(wn недели), который содержит указатели на символьные строки:

char *weekdays[7] = { "Воскресение","Понедельник",
 "Вторник", "Среда","Четверг","Пятница", 
 "Суббота" };

При просмотре массива справа налево находим, что он содержит 7 элементов. Символ "звездочка" (*) перед именем означает указатель. При комбинации имени типа char, "звездочки" и объявления массива получается массив указателей из 7 символьных строк. Массив символьных строк argv используется особенно часто. Его объявление дано в разделе "Переназначение ввода-вывода и обработка командной строки". Этот массив содержит командную строку запуска программы.

Примечание: При объявлении массива символьных строк компилятор Си не добавляет NULL в конце массива, как это делается для самих символьных строк.

522. Размещение в памяти массива символьных строк

Как мы уже знаем, Си рассматривает массивы как указатели на начало самих этих массивов в памяти. В С521 был создан массив символьных строк weekdays, который содержал дни недели. При создании массива символьных строк компилятор Си хранит указатели на строки массива. На рис. 522 показано, как компилятор Си хранит массив letters, объявление которого имеет вид.

char *lettere[4] = ("ААА", "ВВВ", "ССС", "DDD"};

Рис. 522. Си хранит массив строк как массив указателей

Примечание: При объявлении массива символьных строк компилятор Си не добавляет NULL в конце массива, как это делается для самих символьных строк.

523. Использование в цикле массива символьных строк

Итак, при создании массива символьных строк Си хранит в элементах массива указатели на каждую строку. В следующей программе WEEKDAYS.C массив weekdays, содержащий указатели на строки (названия дней недели), просматривается в цикле:

#include <stdio.h>

void main(void)
{ 
char *weekdays[7] = { "Воскресение","Понедельник",
 "Вторник", "Среда","Четверг","Пятница", 
 "Суббота" };
int i;

for (i = 0; i < 7; i++)
 printf("Строка weekdays[%d] содержит %s\n", i, weekdays[i]);
}

Как вы видите, в программе используется цикл по элементам массива; для вывода на экран строк используется спецификатор формата %s.

524. Символьная строка как указатель

Массивы в Си рассматриваются как указатели на начальные элементы этих массивов. В разделе "Строки" встречается объявление символьной строки следующего вида:

char *string;

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

char *workdays[5];

Поскольку в объявлении создается массив, Си дает возможность обращения к массиву с использованием указателя. Для этого необходимо объявить переменную-указатель, которая ссылается на массив символьных строк. Описание будет иметь вид:

char **work_day_ptr;

Сдвоенный символ "звездочка" (*) обозначает, что work_day_ptr есть указатель на указатель символьных строк. Такие указатели используются в разделе "Переназначение ввода-вывода и обработка командной строки".

525. Указатель на указатель символьных строк

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

char **work_day_ptr;

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

#include <stdio.h> 

void main(void)

{
char *workdays[] ={"Понедельник", "Вторник",
 "Среда", "Четверг", "Пятница", "" };

char **work_day;

work_day = workdays;

while (*work_day)
 printf("%s\n", *work_day++);
}

Сначала в программе указателю work_day присваивается начальный адрес массива workdays (адрес строки "Понедельник"). Затем цикл выполняется до тех пор, пока не встретится указатель на пустую строку (используемую в качестве конца массива).

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

526. Описание строковой константы через указатель

Иногда в этой книге строковые переменные инициализируются в самом объявлении, например:

char titled = "1001 совет по C/C++";

При объявлении массива таким способом компилятор Си выделяет достаточно памяти для хранения всех символов, включая NULL-СИМВОЛ в конце строки, присваивая переменной title указатель на первый символ. Поскольку компилятор Си автоматически выделяет требуемую память и работает с указателем на память, программа может использовать указатель на символьную строку вместо массива:

char *title = "1001 совет no C/C++";

527. Указатель void

Как известно, при объявлении переменной-указателя необходимо указывать его тип (например, int, float и char). Указание типа позволяет компилятору корректно выполнить операции сложения и вычитания с указателем, умножая добавляемую или вычитаемую величину на длину типа для получения смещения. В некоторых случаях, однако, этого не требуется. Может оказаться, что в программе достаточно просто получить указатель памяти, с которым она будет работать далее по назначению.

В таком случае программа может создавать указатель типа void:

void *meinory_pointer;

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

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

Как известно, Си дает возможность создавать указатель на все типы данных (такие как int, char, float и даже символьные строки). Кроме этого, Си дает возможность создавать и использовать указатели на функции. Наиболее общее использование указателей на функции - возможность передать адрес функции в качестве фактического параметра другой функции. Указатели на функции объявляются следующим образом:

int (*min)();
int (*max)();
float (*average)();

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

int *min();
int *max();
float * average;

При чтении объявления переменной необходимо начинать с самого вложенного объявления переменной, заключенного в скобки, и затем - слева направо:

int (*min) ();

529. Использование указателя на функцию

В С528 была рассмотрена возможность языка Си по созданию указателей на функции. Наиболее общее использование указателей на функции - возможность передать адрес функции в качестве фактического параметра другой функции. Ранее в этом разделе мы рассматривали библиотечные функции Си сортировки и поиска. При сортировке значений по возрастанию библиотечной функции передается в качестве параметра некоторая своя функция сравнения. При сортировке значений в обратном порядке передается другая функция. В следующей nporpaммe PASSFUNC.C функции get_result передаются в качестве параметров функции (т.е. их адреса) min и max. Возвращаемое значение функции get_result зависит от того, какая функция передается:

#include <stdio.h>

int get_result(int a, int b, int (*compare)())
 {
 return(compare(a, b)); // Вызов передаваемой функции 
 }

int max(int a, int b)
 {
 printf("Вызов функции max\n");
 return((a > b) ? a: b);
 }

int min(int a, int b) 
 {
 printf("Вызов функции min\n");
 return((a < b) ? a: b);
 }

void main(void)
{
int results;

result = get result(1, 2, &max);
printf("Максимум 1 и 2 есть %d\n", result);

result = get_result(l, 2, 6ain);
printf("Минимум 1 и 2 есть %d\n", result);
}

530. Указатель на указатель на указатель ...

Си дает возможность создавать переменные-указатели, которые сами являются указателями. Вообще говоря, глубина ссылки (указатель на указатель) в программе не ограничивается. Однако для большинства программистов использование глубины ссылок, большей, чем указатель на указатель, приводит к путанице и делает программу очень трудной для понимания. Например, в следующей программе PTRTOPTR.C используется 3 уровня указателей на int. Даже в небольшой программе приходится тратить немало времени на понимание уровней ссылок, чтобы уяснить выполняемую обработку данных:

#include <stdio.h>

int what_is_the_value(int ***ptr)
{
 return(***ptr);
}

void main(void) 
{
int *level_l, **level_2, ***level_3, value = 1001;

level_1 = &value;
level_2 = &level_l;
level_3 = &level_2;

printf("Значение равно %d\n", what_is_the_value(level_3));
}

531. Структуры

Мы уже знаем, что массив - это переменная, которая дает возможность хранить в программе множество значений одного и того же типа. Другими словами, массив дает возможность сгруппировать связанную информацию в одном имени, например, рейтинг 100 человек или зарплата 50 служащих. По мере усложнения программы возникает необходимость группировать связанную информацию различных типов данных. Например, предположим, что программа работает с информацией о служащих. Для каждого служащего необходимо поддерживать следующие данные:

char name[64]; // Фамилия
int age; // Возраст
char ssan[ll]; // Социальный номер служащего
int pay_grade; // Разряд по оплате
float salary; // Зарплата
unsigned employee_number; // Номер служащего

Предположим, что в программе есть несколько различных функций, которые работают с этой информацией. Каждый раз при вызове любой из этих функций необходимо следить за тем, что ей переданы все параметры, причем в указанном порядке. Как обсуждалось в разделе "Функции", чем больше у функции параметров, тем труднее программа для восприятия и больше вероятность ошибки. Для упрощения программа может создать структуру, в которой связанная информация группируется под одним именем. Например, поля сведений о служащих можно сгруппировать в структуру Employee, объявление которой имеет вид:

struct Employee {
 char name[64]; // Фамилия
 int age; // Возраст
 char ssan[ll]; // Социальный номер служащего
 int pay_grade; // Разряд по оплате
 float salary; // Зарплата
 unsigned employee_number; // Номер служащего
};

Описание такого рода создает структуру типа Employee.

532. Структура как шаблон для объявления переменных

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

533. Тег или имя структуры

В С531 обсуждалась возможность языка Си для группирования связанных данных в структуру. Используя ключевое слово struct, программа может объявить структуру следующим образом:

struct Employee {
 char name[64]; // Фамилия
 int age; // Возраст
 char ssan[ll]; // Социальный номер служащего
 int pay_grade; // Разряд по оплате
 float salary; // Зарплата
 unsigned employee_number; // Номер служащего };
};

В данном случае именем структуры является идентификатор Employee. Программисты-пользователи языка Си называют имя структуры тегом структуры. Как мы увидим в дальнейшем, тег структуры используется для объявления переменной указанного типа. Следующее объявление создает структуру с именем Shape:

struct Shape {
 int type; // 0 - окружность, 1 - квадрат, 2 - треугольник
 int color;
 float radius;
 float area;
 float perimeter;
};

534. Различные способы объявления переменных типа структура

Итак, язык Си дает возможность группировать связанные данные в структуру (С531). Само по себе объявление структуры не создает переменной, а служит шаблоном для ее объявления. Описать переменную можно двумя способами. Первый способ, пусть в программе объявлена структура типа Employee:

struct Employee {
 char name[64]; //Фамилия
 int age; //Возраст
 char asan[ll]; //Социальный номер служащего
 int pay_grade; //Разряд по оплате
 float salary; //Зарплата
 unsigned employee number; //Номер служащего

Следуя представленному определению структуры, можно объявить переменную типа Employee:

struct Employee employee_info;
struct Employee new_employee, terminated_employee;

Второй способ, объявление переменной может непосредственно следовать за объявлением структуры:

struct Employee {
 char name[64]; // Фамилия
 int age; // Возраст
 char ssan[ll]; // Социальный номер служащего
 int pay_grade; // Разряд по оплате
 float salary; // Зарплата
 unsigned employee_number; // Номер служащего
} employee_info, new_employee,terminated_employee;

535. Члены структуры

Как уже известно, Си дает возможность группировать связанные данные в структуру. Например, в следующем операторе создается переменная triangle с использованием структуры типа Shape:

struct Shape {
 int type; // 0 - окружность, 1 - квадрат, 2 - треугольник
 int color;
 float radius;
 float area;
 float perimeter;
} triangle;

Каждый выделенный элемент информации структуры представляет собой отдельный член этой структуры. Структура Shape состоит из 5 членов: type, color, radius, area и perimeter. Для обращения к члену структуры используется операция "точка"(.). Например, в следующем фрагменте членам структуры присваиваются конкретные значения:

triangle.type = 2;
triangle.perimeter = 30.0;
triangle.area = 45.0;

536. Распределение памяти под структуры

При объявлении переменной типа заданной структуры выделяется память для всех членов структуры. Например, при объявлении переменной типа Employee с объявлением структуры из С535 выделяет память, как показано на рис. 536.

Рис. 536. Распределение памяти под структуру на примере Employee

537. Адрес структуры как фактический параметр функции

Как известно, язык Си позволяет группировать информацию в виде структуры. В разделе "Дата и время" используется функция geldate для определения текущей системной даты. Функция присваивает текущую дату членам структуры типа date:

struct date {
 int da_year // Год char 
 da_day // День 
 char da_mon // Месяц
};

В следующей программе DOSDATE.C функция getdate используется для присваивания переменной cwr_date текущей системной даты:

#include <stdio.h>
#include <dos.h>

void main (void) 
{
struct date curr_date;

getdate(&curr_date);

printf("Текущая системная дата: %d-%d-%d\n", curr date.da mon, 
 curr_date.da day, curr_date.da_year);
}

Поскольку функция должна изменить значение параметра, программа передает переменную типа "структура" по ссылке (адресу).

538. Передача структуры по значению как фактического параметра функции

В языке Си переменные типа заданной структуры могут передаваться в качестве параметров функций, как и любые другие переменные. В следующей программе STRUFUNC.C функция show_structure принимает в качестве параметра переменную типа структуры Shape. Функция show_struclure выводит на экран значения всех членов структуры:

#include <stdio.h>

struct Shape {
 int type;
 int color;
 float radius;
 float area;
 float perimeter;
};

void show structure(struct Shape shape) 
 {
 printf("shape.type %d\n", shape.type);
 rintf("shape.color %d\n", shape.color);
 rintf("shape.radius %f shape.area %f shape.perimeter %f\n", 
 hape.radius, shape.area, shape.perimeter);
 }

void main(void)
{
struct Shape circle;
 circle.type = 0;
 circle.color = 1;
 circle.radius = 5.0;
 circle.area == 22.0 / 7.0 * circle.radius * circle.radius;
 circle.perimeter = 2.0* 22.0 / 7.0 * circle.radius;
 show structure(circle);
}

539. Изменение структуры внутри функции

В С538 была продемонстрирована возможность передачи переменной типа структура как параметра функции так же, как и для переменных любого типа. Если оказывается необходимым изменять значения членов структуры, то переменная должна передаваться не по значению, а по адресу (так же, как и переменная любого другого типа, значение которой требуется изменить). В следующей программе CHGSTRUC.C определяется функция change„structure, которая изменяет значения членов структуры типа Shape:

#include <stdio.h>

Struct Shape 
{
 int type;
 int color;
 float radius;
 float area;
 float perimeter;
);

void change structure(struct Shape * shape) 
{
 (*shape)-type = 0;
 (*shape).color = 1;
 (*shape).radius = 5.0;
 (*shape).area = 22.0 / 7.0 * (*shape).radius * (*shape).radius;
 (*shape).perimeter = 2.0 * 22.0 / 7.0 * (*shape).radius;
}

void main(void) 
{
 struct Shape circle;

 change_structure(&circle);

 printf("circle.type %d\n", circle.type);
 printf("circle.color %d\n", circle.color);
 printf("circle.radius %f circle.area %f circle.perimeter %f\n", 
 circle.radius, circle.area, circle.perimeter);
}

Для передачи структуры как параметра функции используется указатель на переменную типа структура. Внутри функции доступ к членам структуры осуществляется посредством операции "звездочка" (*) -извлечения значения по указателю:

(*pointer).member = value;

Эта операция рассматривается в С540.

540. Операция (*указатель).член

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

(*pointer).member = value;

В этом выражении сначала выполняется операция в скобках - "звездочка" (*), которая определяет адрес в памяти, отведенной для переменной типа структура. По имени члена компилятор Си добавляет к адресу смещение. Если скобки не указаны:

*pointer.member = value;

Си предполагает, что член структуры сам является указателем и операция "звездочка" (*) применяется к члену структуры. Такой синтаксис может быть корректен в том случае, если член структуры объявлен как указатель, например:

struct Planet {
char name[48];
int *some_pointer;
} planet;

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

*planet.some pointer = 5;

541. Формат указатель->член

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

(*pointer).member = value;
some_value = (*pointer).member;

Второй формат имеет вид:

pointer->member = valued;
some_value = pointer->member;

В следующей программе CHMEMBER.C функция change Jstructure использует второй формат для обращения к членам структуры, передаваемой функции по адресу.

#include <stdio.h>

struct Shape { 
 int type;
 int colors;
 float radius;
 float area;
 float perimeter;
};

void change_structure(struct Shape *shape) 
{
 shape->type = 0;
 shape->color = 1;
 shape->radius = 5.0;
 shape->area = 22.0 / 7.0 * shape->radius * shape->radius;
 shape->perimeter =2.0 * 22.0 / 7.0 * shape->radius;
}

void main(void)
{
 struct Shape circle;

 change_structure(&circle);

 printf("circle.type %d\n", circle.type);
 printf("circle.color %d\n", circle.color);
 printf("circle.radius %f circle.area %f circle.perimeter %f\n", 
 circle.radius, circle.area, circle.perimeter);
}

542. Структура без тега

Тег структуры - это имя структуры. Тег структуры используется при объявлении переменной типа этой структуры. Однако при объявлении переменной типа структура тег указывать необязательно. Например, в следующем фрагменте объявлены две переменные типа структуры:

struct {
 int type; // 0 - окружность, 1 - квадрат, 2 - треугольник 
 int colors;
 float radius;
 float area;
 float perimeter;
} triangle, circle;

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

543. Область видимости объявлений структуры

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

544. Инициализация структуры

Подобно массивам, Си дает возможность инициализировать структуры непосредственно при их объявлении. В следующей программе INITSTRU.C объявляется и инициализируется переменная типа структуры Shape:

#include <stdio.h>

void main(void) 
{
struct Shape { 
 int type;
 int color;
 float radius;
 float area;
 float perimeter;
} circle = {0, 1, 5.0, 78.37, 31.42);

printf("circle.type %d\n", circle.type);
printf("circle.color %d\n", circle.color)

printf("circle.radius %f circle.area %f circle.perimeter %f\n", 
 circle.radius, circle.area, circle.perimeter);
}

Поскольку структура используется только в функции main, она объявлена внутри main.

545. Ввод-вывод переменных типа структура

В приведенных примерах для вывода значений одного или нескольких членов структуры использовалась функция printf. При выполнении операций вывода на экран или ввода с клавиатуры, изменяющих члены структуры, для каждого члена необходимо выполнить отдельную операцию ввода-вывода. Однако при чтении структуры из файла или записи в файл можно работать со всей структурой. Для чтения и записи структур через файловый поток можно использовать функции fread и fwrite. В разделе "Файлы, директории и дисковые устройства" демонстрируется использование этих функций для ввода-вывода структур. При выполнении операций ввод-вывода структур с использованием идентификатора файла применяются функции read и write. Для компилятора Си структура, с точки зрения ее хранения в памяти, - это просто массив байтов. Все упомянутые функции ввода-вывода читают или записывают непрерывную последовательность байтов. При использовании этих функций со структурами необходимо просто передавать функции адреса структур.

546. Вложенные структуры

В структуре могут быть члены любого типа {inl, float и т.д.). Кроме того, члены структуры могут сами быть структурами. Например, в следующем примере объявление структуры включает структуру типа Date, которая обозначает дату приема служащего:

struct Employee {
 char name[64]; // Фамилия
 int age; // Возраст
 char ssan[ll]; // Социальный номер служащего
 struct Date {
 int day;
 int month;
 int year;
 } hire_date;
 int pay_grade; // Разряд по оплате 
 float salary; // Зарплата 
 unsigned employee_number; // Номер служащего 
} new_employee;

Для обращения к члену вложенной структуры используется операция "точка" (.), сначала указывается член внешней структуры, затем член вложенной структуры:

new_employee.hire_date.month = 12;

547. Структура, содержащая массив

Типы членов структуры могут быть любыми, в том числе структурами или массивами. Если член структуры - массив, то обращаться к нему можно по имени, которому должна предшествовать операция "точка" (.). В следующей программе STRUARRA.C инициализируются поля структуры, включая массив. В программе выполняется цикл для вывода на экран всех элементов массива:

#include <stdio.h>

void main(void) 
{
struct Date {
 char month_name[64];
 int month;
 int day;
 int year;
} current_date = ( "Июль", 7, 4, 1994 );

int i;

for (i = 0; current_date.month_name[i]; i++) 
 putchar(current_date.month_name[i]);
}

548. Создание массива структур

Как известно, использование массивов дает возможность хранить множество значений одного и того же типа. Большинство массивов этого раздела имеет тип int, float или char. Однако Си дает возможность объявить массив типа структуры. Например, в следующем объявлении создается массив, в котором хранятся сведения о 100 служащих:

struct 
		
	 
Категория: Языки программирования | Добавил: Fenix (25.03.2008)
Просмотров: 8779 | Комментарии: 2 | Рейтинг: 3.0/2 |
Всего комментариев: 1
1 NiliaWapSaf  
0
ОДНОКЛАССНИКИ ЗНАКОМСТВА

Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Форма входа
Поиск
Друзья сайта
Статистика

Онлайн всего: 5
Гостей: 5
Пользователей: 0
Copyright MyCorp © 2024Используются технологии uCoz