Правильное определение структуры в с. Передача структуры в функцию. Структура и функции
Перед тем как приступить к изучению классов в C++, мы рассмотрим тип данных подобный классу — структуры. Структуры полезны, когда нам надо объединить несколько переменных с разными типами под одним именем. Это делает программу более компактной и более гибкой для внесения изменений. Также структуры незаменимы, когда необходимо сгруппировать некоторые данные, например, запись из базы данных или контакт из книги адресов. В последнем случае структура будет содержать такие данные контакта как имя, адрес, телефон и т.п.
Синтаксис
В процессе написания программы может потребоваться сгруппировать разные данные. Например, вы захотите хранить координаты некоторых объектов и их имена. Вы можете сделать это с помощью :
Int x_coor; int y_coor; string names;
Но так как каждый элемент одного массива связан с другим, то при изменении одного, придется менять остальные тоже. И чем больше данных вам надо объединить, тем сложнее будем такая программа. Поэтому для объединения разных данных используются структуры .
Формат объявления структуры выглядит так:
Struct Car { int x_coor; int y_coor; string name; };
Объявляя структуру, мы вводим в программу наш собственный тип данных, которым можем пользоваться, так же как и стандартными типами, т.е. объявление переменной нашего типа будет таким:
StructName variableName;
structName — имя структуры, variableName — имя переменной.
x_coor, y_coor и name — поля нашей структуры. При объявлении структуры мы создаем составной тип данных, с помощью которого можно создавать переменные, которые сочетают в себе несколько значений (например, координаты и имя). Внутри структуры каждому полю мы даем имя, чтобы потом обращаться к этому значению по его имени.
Для доступа к полям структуры используется точка:
// объявляем переменную Car myCar; // и используем её myCar.x_coor = 40; myCar.y_coor = 40; myCar.name = "Porche";
Как видите, вы можете хранить в структуре столько полей, сколько вам угодно и они могут иметь разные типы.
Рассмотрим пример, демонстрирующий сочетание массивов и структур.
#include
Так же как и с простыми типами (int, например), вы можете создавать массивы структур. А с каждым элементом этого массива работать так же как и с отдельной переменной. Для доступа к полю name первого элемента массива структур, просто напишите:
Players[ 0 ].name
Структуры и функции
Очень часто требуется писать функции, которые принимают структуры в качестве аргумента или возвращают структуру. Например, если вам надо написать небольшую космическую аркаду, вам может понадобится функция для инициализации нового противника:
Struct EnemySpaceShip { int x_coordinate; int y_coordinate; int weapon_power; }; EnemySpaceShip getNewEnemy();
Функция getNewEnemy должна возвращать структуру с инициализированными полями:
EnemySpaceShip getNewEnemy () { EnemySpaceShip ship; ship.x_coordinate = 0; ship.y_coordinate = 0; ship.weapon_power = 20; return ship; }
На самом деле эта функция вернет копию созданной локальной переменной ship. Это значит, что каждое поле структуры будет скопировано в новую переменную. В нашем случае копирование малого количества полей не заметно, но когда вы работаете с большими объемами данных нужно избегать лишних действий, подробнее об этом поговорим в статье про указатели.
Таким образом, для получения новой переменной будем использовать следующий код:
EnemySpaceShip ship = getNewEnemy();
Теперь эту переменную можно использовать как обычную структуру.
Передавать структуры в функцию можно так:
EnemySpaceShip upgradeWeapons (EnemySpaceShip ship) { ship.weapon_power += 10; return ship; }
Когда мы передаем структуру в функцию, она копируется, так же как и при возвращении структуры. Поэтому любые изменения сделанные внутри функции будут потеряны, поэтому мы возвращаем структуру после изменения.
Использование функции:
Ship = upgradeWeapons(ship);
Когда вызывается функция, переменная ship копируется и изменяется в функции, а когда переменная возвращается, она снова копируется и перезаписывает поля оргинальной переменной.
И наконец, программа для создания и улучшения одного корабля:
Struct EnemySpaceShip { int x_coordinate; int y_coordinate; int weapon_power; }; EnemySpaceShip getNewEnemy() { EnemySpaceShip ship; ship.x_coordinate = 0; ship.y_coordinate = 0; ship.weapon_power = 20; return ship; } EnemySpaceShip upgradeWeapons(EnemySpaceShip ship) { ship.weapon_power += 10; return ship; } int main() { EnemySpaceShip enemy = getNewEnemy(); enemy = upgradeWeapons(enemy); }
Указатели
Если вы работаете с на структуру, то для доступа к переменным надо использовать оператор «->» вместо точки. Все свойства указателей не изменяются. Пример:
#include
Структуры в си (c) - это объединенные данные, у которых есть некоторая логическая взаимосвязь. В отличие от массивов, структуры могут содержать данные разных типов. Вот пару примеров структур в си (c): структура класс (имя учащегося, буква класса, средний балл); структура футбольная команда (тренер, название команды, место в турнирной таблице). Т.е. структуру вы будете использовать довольно часто. Теперь давайте рассмотрим, как описываются структуры в си:
struct klass {
char name;
char klass_name;
float bal;
};
struct
Любая структура в языке си (c) должна начинаться с ключевого слова - struct , которое сообщает компилятору, что тут у нас будет структура. Все данные в структуре (struct) пишутся в фигурных скобках, и в конце ставится запятая с точкой (;). Советую сразу ставить запятую с точкой, что бы не было ошибок.
Как вы видите, в структуре (struct) у нас находятся данные различных типов, но они объединены в логическую связь, так как в моем примере они являются определенным школьным классом. Данные в структуре должны иметь уникальные имена, но в различных структурах можно использовать одинаковые названия.
Структура, которая создана выше не занимает в памяти компьютера места, так как мы, на самом деле, просто создали свой тип данных. Объявление структуры ни чем не отличается от объявления любого типа данных в языке си (c). Вот пример:
struct klass a, b, *c;
Мы объявили переменную а типа struct klass, массив b, состоящий из 5 элементов типа struct klass и указатель на переменную struct klass.
Так же можно объявлять переменные сразу после объявления структуры:
struct klass {
char name;
char klass_name;
float bal;
} a, b, *c;
А какие же операции можно проделывать со структурами? Ответ на этот вопрос лучше перечислить по пунктам:
- присваивание полю структуры значение того же типа
- можно получить адрес структуры. Не забываем операцию взятия адреса (&)
- можно обращаться к любому полю структуры
- для того, что бы определить размер структуры можно использовать операцию sizeof()
Инициализация структуры
Инициализация структуры в языке си (c) происходит так же, как и при инициализации массива. Вот пример инициализации структуры:
struct klass a = {"Sergey", "B", 4.5 };
Т.е. мы создаем переменную типа struct klass и присваиваем всем трем полям, которые у нас определенны в структуре, значения. Порядок очень важен при инициализации структуры , так как компьютер сам не может отсортировывать данные. Если какое-либо поле у вас будет не заполненным, то оно автоматом заполнится 0 - для целочисленных типов; NULL - для указателей; \0 (ноль-терминатор) - для строковых типов.
Последнее обновление: 09.10.2017
Ранее для определения классов мы использовали ключевое слово class . Однако C++ предоставляет еще один способ для определения пользовательских типов, который заключается в использовании структур. Данный способ был унаследован языком С++ еще от языка Си.
Структура в языке C++ представляет собой производный тип данных, который представляет какую-то определенную сущность, также как и класс. Нередко структуры применителько к С++ также называют классами. И в реальности различия между ними не такие большие.
Для определения структуры применяется ключевое слово struct , а сам формат определения выглядит следующим образом:
Struct имя_структуры { компоненты_структуры };
Имя_структуры представляет произвольный идентификатор, к которому применяются те же правила, что и при наименовании переменных.
После имени структуры в фигурных скобках помещаются Компоненты_структуры , которые представляют набор описаний объектов и функций, которые составляют структуру.
Например, определим простейшую структуру:
#include
Здесь определена структура person , которая имеет два элемента: age (представляет тип int) и name (представляет тип string).
После определения структуры мы можем ее использовать. Для начала мы можем определить объект структуры - по сути обычную переменную, которая будет представлять выше созданный тип. Также после создания переменной структуры можно обращаться к ее элементам - получать их значения или, наоборот, присваивать им новые значения. Для обращения к элементам структуры используется операция "точка":
Имя_переменной_структуры.имя_элемента
По сути структура похожа на класс, то есть с помощью структур также можно определять сущности для использования в программе. В то же время все члены структуры, для которых не используется спецификатор доступа (public, private), по умолчанию являются открытыми (public). Тогда как в классе все его члены, для которых не указан спецификатор доступа, являются закрытыми (private).
Кроме того мы можем инициализировать структуру, присвоив ее переменным значения с помощью синтаксиса инициализации:
Person tom = { 34, "Tom" };
Инициализация структур аналогична инициализации массивов: в фигурных скобках передаются значения для элементов структуры по порядку. Так как в структуре person первым определено свойство, которое представляет тип int - число, то в фигурных скобках вначале идет число. И так далее для всех элементов структуры по порядку.
При этом любой класс мы можем представить в виде структуры и наоборот. Возьмем, к примеру, следующий класс:
Class Person { public: Person(std::string n, int a) { name = n; age = a; } void move() { std::cout << name << " is moving" << std::endl; } void setAge(int a) { if (a > 0 && a < 100) age = a; } std::string getName() { return name; } int getAge() { return age; } private: std::string name; int age; };
Данный класс определяет сущность человека и содержит ряд приватных и публичных переменных и функции. Вместо класса для определения той же сущности мы могли бы использовать структуру:
#include
И в плане конечного результата программы мы не увидели бы никакой разницы.
Когда использовать структуры? Как правило, структуры используются для описания таких данных, которые имеют только набор публичных атрибутов - открытых переменных. Например, как та же структура person, которая была определена в начале статьи. Иногда подобные сущности еще называют аггрегатными классами (aggregate classes).
Последнее обновление: 02.10.2018
Наряду с классами структуры представляют еще один способ создания обственных типов данных в C#. Более того многие примитивные типы, например, int, double и т.д., по сути являются структурами.
Например, определим структуру, которая представляет человека:
Struct User { public string name; public int age; public void DisplayInfo() { Console.WriteLine($"Name: {name} Age: {age}"); } }
Как и классы, структуры могут хранить состояние в виде переменных и определять поведение в виде методов. Так, в данном случае определены две переменные - name и age для хранения соответственно имени и возраста человека и метод DisplayInfo для вывода информации о человеке.
Используем эту структуру в программе:
Using System; namespace HelloApp { struct User { public string name; public int age; public void DisplayInfo() { Console.WriteLine($"Name: {name} Age: {age}"); } } class Program { static void Main(string args) { User tom; tom.name = "Tom"; tom.age = 34; tom.DisplayInfo(); Console.ReadKey(); } } }
В данном случае создается объект tom. У него устанавливаются значения глобальных переменных, и затем выводится информация о нем.
Конструкторы структуры
Как и класс, структура может определять констукторы. Но в отличие от класса нам не обязательно вызывать конструктор для создания объекта структуры:
User tom;
Однако если мы таким образом создаем объект структуры, то обязательно надо проинициализировать все поля (глобальные переменные) структуры перед получением их значений или перед вызовом методов структуры. То есть, например, в следующем случае мы получим ошибку, так как обращение к полям и методам происходит до присвоения им начальных значений:
User tom; int x = tom.age; // Ошибка tom.DisplayInfo(); // Ошибка
Также мы можем использовать для создания структуры конструктор по умолчанию, при вызове которого полям структуры будет присвоено значение по умолчанию (например, для числовых типов это число 0):
User tom = new User(); tom.DisplayInfo(); // Name: Age: 0
Также мы можем определить свои конструкторы. Например, изменим структуру User:
Using System; using System.Reflection; namespace HelloApp { struct User { public string name; public int age; public User(string name, int age) { this.name = name; this.age = age; } public void DisplayInfo() { Console.WriteLine($"Name: {name} Age: {age}"); } } class Program { static void Main(string args) { User tom = new User("Tom", 34); tom.DisplayInfo(); User bob = new User(); bob.DisplayInfo(); Console.ReadKey(); } } }
Важно учитывать, что если мы определяем конструктор в структуре, то он должен инициализировать все поля структуры, как в данном случае устанавливаются значения для переменных name и age.
Также, как и для класса, можно использовать инициализатор для создания структуры:
User person = new User { name = "Sam", age = 31 };
Но в отличие от класса нельзя инициализировать поля структуры напрямую при их объявлении, например, следующим образом:
Struct User { public string name = "Sam"; // ! Ошибка public int age = 23; // ! Ошибка public void DisplayInfo() { Console.WriteLine($"Name: {name} Age: {age}"); } }
Тип переменной определяет: её размер в памяти, тип данных, которые она может хранить и операции, которые можно производить с этой переменной.
Тип данных является категорией. В языке С++ программист может создать любой тип данных на основе базовых типов. Новые типы данных необходимо создавать для решения конкретных практических задач. Например: реализация работы деканата.
Успех программы часто зависит от удачного выбора способа представления данных. С помощью структур возможно моделировать сложные объекты, возникающие при решении задач. Структуры представляют средство для доступа к записям, которые содержат поля одного или нескольких типов.
Для использования структуры необходимо:
1. установить шаблон для структуры
2. объявить переменную, соответствующую этому шаблону
3. осуществить доступ к компонентам структуры.
Шаблон структуры
Шаблон - это схема, описывающая содержание структуры. Установка структурного шаблона телефонный справочник:
struct sprav {
char fio;
long num;
};
Данный шаблон описывает структуру с именем типа структуры sprav, состоящую из двух компонентов: строки fio и целой переменной num типа long. Имя типа структуры sprav необязательно и используется для ссылки на эту структуру. Компоненты структуры - данные любого типа, включая и другие структуры. Имя внутри структуры может быть таким же, как имя объекта вне структуры. Если шаблон описан внутри функции - он доступен только этой функции, если шаблон описан вне функции - он доступен любой функции программы. Установка шаблона не вызывает никаких действий в программе.
Структурные переменные
Объявление структурных переменных приводит к выделению памяти для компонент структуры, куда можно записать данные или откуда можно прочитать их. Для объявления структурных переменных имеются несколько способов.
1. Установить структурный шаблон:
struct sprav {
char fio;
long num;
};
Объявить простую переменную, массив структур, указатель на структуру: struct sprav tel1, tel2, *tel3;
2. Установить структурный шаблон с помощью макроопределения:
#define SPRAV struct sprav
SPRAV {
char fio;
long num;
};
Объявить переменные:
SPRAV sp1, sp2, *sp3;
3. Объявить переменные одновременно с установкой шаблона (если на данную структуру вы больше не ссылаетесь):
struct {
char fio;
long num;
} tel1, tel2, *tel3;
4. Ввести новый тип данных (TEL)-структура определенного вида:
typedef struct {
char fio;
long num;
} TEL;
Объявить переменные нового типа:
TEL tel1, tel2, *tel3;
Если программа достаточно объемна, представляется более удобным четвертый способ.
Инициализация структуры
Инициализировать можно только внешние или статические структуры.
static struct {
char fio;
long num;
} tel={
"Иванов Ф.А.", 456756,
"Петров В.П.", 632345
};
Доступ к компонентам структуры
Доступ к компонентам структуры продемонстрируем с помощью примеров.
/* Обращение к элементам структуры через имя переменной */
#include
#include
void main(void)
{
struct{
char fio; /* фамилия */
long num; /* телефон */
} tel1, tel2;
puts("введите фио абонента-");
gets(tel1.fio);
puts("введите его номер-");
scanf("%ld",&tel1.num);
tel2=tel1; /* нельзя так же сравнивать структуры */
puts("Введено:");
printf("Фамилия:%s номер: %ld\n",tel2.fio,tel2.num);
}
/* Динамическое выделение памяти для структуры */
#include
#include
#include
struct sprav {
char fio;
long num;
};
void main(void)
{
struct sprav *tel1, *tel2;
clrscr();
/* Выделение памяти для структуры */
tel1=(struct sprav *)malloc(sizeof(struct sprav));
tel2=(struct sprav *)malloc(sizeof(struct sprav));
gets(tel1->fio);
puts("введите его номер-");
scanf("%ld",&tel1->num);
*tel2= *tel1;
puts("Введено:");
printf("Фамилия:%s номер: %ld\n",(*tel2).fio,(*tel2).num);
}
Массив структур
/* Массив структур. Обращение к элементам структуры через */
/* имя элемента массива */
#include
#include
#include
#define SPRAV struct sprav
void main(void)
{
SPRAV{
char fio;
long num;
};
SPRAV tel; /* массив структур - 5 элементов */
char fio_tek;
int i;
clrscr();
/* ввод данных в массив структур */
for(i=0; i<5; i++)
{
puts("введите фио абонента-");
gets(tel[i].fio);
puts("введите его номер-");
scanf("%ld",&tel[i].num);
getchar();
}
gets(fio_tek);
/* поиск структуры по фамилии абонента */
for(i=0; i<5; i++)
if(!strcmp(fio_tek,tel[i].fio)) break;
if(i!=5) /* цикл закончен по break */
tel[i].num);
else /* цикл выполнился полностью */
puts("Абонент не найден");
}
/* Массив структур. Память выделяется динамически. */
/* Обращение к элементам структуры через указатель */
#include
#include
#include
#include
typedef struct{
char fio;
long num;
} TEL;
void main(void)
{
TEL *tel;
char fio_tek;
int i;
clrscr();
/* Выделение памяти для массива - 3 элемента */
tel=(TEL *)malloc(sizeof(TEL)*3);
for(i=0; i<3; i++)
{
puts("введите фио абонента-");
gets((tel+i)->fio);
puts("введите его номер-");
scanf("%ld",&(tel+i)->num);
getchar();
}
puts("Выбор телефона по фамилии");
gets(fio_tek);
for(i=0; i<5; i++,tel++)
if(!strcmp(fio_tek,tel->fio)) break;
if(i!=5)
printf("номер абонента %s равен %ld\n",fio_tek, \
tel->num);
else
puts("Абонент не найден");
}
Передача структуры в функцию
Непосредственный доступ к компонентам структуры - плохой стиль программирования. Все операции, которые разрешены применительно к структуре, должны быть при этом реализованы в виде отдельных функций. Не все компиляторы языка Си позволяют передавать структуры в функцию по значению, поэтому в примерах передача структуры идет через указатель.
/* Передача структуры в функцию через указатель на структуру */
/* Определение комплексного числа через структуру и действия */
/* над комплексными числами (ввод, вывод, вычисление суммы) */
#include
typedef struct { float a; /* действительная часть */
float b; /* мнимая часть */
} COMPLEX;
void vvod(COMPLEX *,float,float);
void sum(COMPLEX *,COMPLEX *,COMPLEX *);
void out(COMPLEX *);
void main(void)
{
COMPLEX x,y,z;
vvod(&x,2.5,6.7);
vvod(&y,6.89,8.45);
puts("Введены числа:");
out(&x);
out(&y);
sum(&x,&y,&z);
puts("Сумма комплексных чисел равна:");
out(&z);
}
/* Вывод комплексного числа */
void out(COMPLEX *p)
{
printf("(%.2f,%.2f)\n", (*p).a,(*p).b);
return;
}
/* Вычисление суммы двух комплексных чисел */
void sum(COMPLEX *p1,COMPLEX *p2,COMPLEX *p3)
{
(*p3).a=(*p1).a+(*p2).a;
(*p3).b=(*p1).b+(*p2).b;
return;
}
/* Ввод значений для элементов структуры */
void vvod(COMPLEX *p,float a, float b)
{
p->a=a;
p->b=b;
return;
}
Вложенные структуры
Структура, являющаяся компонентом другой структуры, называется вложенной.
/* Даны четыре точки - центры четырех окружностей. Заполнить структуру окружность, если все окружности проходят через начало координат. */
#include
#include
#include
#include
struct POINT {
float x;
float y;
};
struct CIRCLE {
struct POINT point; /* вложенная структура */
double r;
} circle, *p;
void main (void)
{
int i,j;
float a,b,c,d;
clrscr();
gotoxy(17,1);
cputs("ВВЕДИТЕ КООРДИНАТЫ ТОЧЕК:\r\n");
for(i=0;i<2;i++)
{
cprintf ("\n\n ВВЕДИТЕ X: ");
cprintf ("X[%d]= ",i+1);
cscanf("%f",&circle[i].point.x);
cprintf ("\n ВВЕДИТЕ Y: ");
cprintf ("Y[%d]= ",i+1);
cscanf ("%f",&circle[i].point.y);
}
p=circle;
gotoxy(17,12);
cputs("РЕЗУЛЬТАТ:\r\n\n");
for(i=0;i<2;i++)
{
a=p->point.x;
b=p->point.y;
c=sqrt(a*a+b*b);
p->r=c;
cprintf("\nРАДИУС: %lf ЦЕНТР (%f,%f)\r\n",p->r,a,b);
p++;
}