Структуры в С++
Если вы читаете эту статью, то конечно достаточно много уже знаете о типах данных языка С++. А теперь только представьте — вы сами можете создавать, своего рода, типы данных, которые вам необходимы и с которыми вам будет удобно работать! И это несложно!
Структура — это , некое объединение различных переменных (даже с разными типами данных), которому можно присвоить имя. Например можно объединить данные об объекте Дом: город (в котором дом находится), улица, количество квартир, интернет(проведен или нет) и т.д. в одной структуре. В общем, можно собрать в одну совокупность данные обо всем, что угодно, точнее обо всем, что необходимо конкретному программисту. Всем сразу стало понятно 🙂
Если вы только приступаете к знакомству со структурами в С++, сначала, вам необходимо ознакомиться с синтаксисом структур в языке С++. Рассмотрим простой пример, который поможет познакомиться со структурами и покажет, как с ними работать. В этой программе мы создадим структуру, создадим объект структуры, заполним значениями элементы структуры (данные об объекте) и выведем эти значения на экран. Ну что же, приступим!
Коментарии по коду программы:
В строках 4 — 10 мы создаем структуру. Чтобы ее объявить используем зарезервированное слово struct и даем ей любое, желательно логичное, имя. В нашем случае — building . С правилами именования переменных, вы можете ознакомиться в этой статье. Далее открываем фигурную скобку < , перечисляем 4 элемента структуры через точку с запятой ; , закрываем фигурную скобку >и в завершении ставим точку с запятой ; . Это будет нашим шаблоном (формой) структуры.
В строке 16 объявляем объект структуры. Как и для обычных переменных, необходимо объявить тип данных. В этом качестве выступит имя нашей созданной структуры — building .
Как же заполнить данными (инициализировать) элементы структуры? Синтаксис таков: Имя объекта далее оператор точка . и имя элемента структуры. Например: apartment1.owner . Таким образом, в строках 18-21 присваиваем данные элементам структуры.
И так, данные мы внесли. Следующий вопрос: «Как к ним обратиться, как работать и использовать их в программе?» Ответ — «Очень просто — так же, как и при инициализации, используя точку . и имя элемента структуры». В строках 23 — 26 выводим заполненные элементы структуры на экран.
И вот что мы увидим в результате, когда скомпилируем нашу программу:
Что ещё важно знать:
- Объект структуры можно объявить до функции main() . Это выглядело бы так:
- Инициализировать структуру можно и таким способом:
но так делают крайне редко;
- Структуру можно вкладывать в другие структуры (это мы рассмотрим в следующем примере).
Дополним предыдущий пример, чтобы увидеть дополнительные возможности работы со структурами.
Пример:
Коментарии по коду программы:
Строка 17 — создание объекта built типа date в определении структуры building . Строки 42 — 43: создаем указатель на структуру struct building *pApartment; и далее присваиваем ему адрес уже созданного и заполненного данными объекта pApartment = &apartment1; . Обращаясь к элементам структуры через указатель мы используем оператор -> (тире и знак >) . Это видно в строках 47 — 51.
В строке 62 показано, как можно инициализировать структуру. А именно, можно создать новый объект структуры и присвоить ему одним целым, уже созданный и заполненный данными, объект. В функцию show() передаем объект структуры, как параметр — строка 64. Результат:
Владелец квартиры: Денис
Квартира находится в городе: Симферополь
Количество комнат: 5
Стоимость: 150000 $
Дата постройки: январь 2013
Владелец квартиры: Игорь
Квартира находится в городе: Киев
Количество комнат: 4
Стоимость: 300000 $
Дата постройки: январь 2012
Для продолжения нажмите любую клавишу . . .
Разобрав этот пример, мы увидели на практике следующее:
- структуру можно вкладывать в другую структуру;
- увидели, как создаётся указатель на структуру;
- как нужно обращаться к элементу структуры через указатель. А именно, используя оператор -> ; В примере это было так: apartment0->owner , но можно и так (*apartment0).owner . Круглые скобки, во втором случае, обязательны.
- данные одной структуры можно присвоить другой структуре;
- можно структуру передать в функцию, как параметр (кстати, элементы структуры так же можно передавать в функцию, как параметры).
В дополнение ко всему, следует отметить, что функции могут так же возвращать структуры в результате своей работы. Например:
Вот так, вкратце, мы познакомились со структурами в языке С++, попрактиковались на примерах и узнали основы. Это только начало!
Как вывести структуру на экран c
Ввод информации о студенте в переменную st типа Student:
scanf(«%s %s %s %u %lf”, &st.surname, &st.name,
&st.patronymic, &st.number, &st.rate);
Вывод на экран списка группы, заданной в переменой gr типа Group:
for(unsigned i=0;i printf(«%2u: %15s %15s %15s %6u %.1lf\n”, i+1, gr.list[i].surname, gr.list[i].name, gr.list[i].patronymic, gr.list[i].number, gr.list[i].rate);
Для определения размера переменной структурного типа в байтах используется оператор определения типа sizeof. Например:
unsigned size = sizeof(struct Student); //size == 57
Структура — это совокупность переменных, объединенных под одним именем. С помощью структур удобно размещать в смежных полях связанные между собой элементы информации. Объявление структуры создает шаблон, который можно использовать для создания ее объектов (то есть экземпляров этой структуры). Переменные, из которых состоит структура, называются членами. (Члены структуры еще называются элементами или полями.)
Как правило, члены структуры связаны друг с другом по смыслу. Например, элемент списка рассылки, состоящий из имени и адреса логично представить в виде структуры. В следующем фрагменте кода показано, как объявить структуру, в которой определены поля имени и адреса. Ключевое слово struct сообщает компилятору, что объявляется (еще говорят, «декларируется») структура.
unsigned long int zip;
Обратите внимание, что объявление завершается точкой с запятой, потому что объявление структуры является оператором. Кроме того, тег структуры addr идентифицирует эту конкретную структуру данных и является спецификатором ее типа.
В данном случае на самом деле никакая переменная не создается. Всего лишь определяется вид данных. Когда вы объявляете структуру, то определяете агрегатный тип, а не переменную. И пока вы не объявите переменную этого типа, то существовать она не будет. Чтобы объявить переменную (то есть физический объект) типа addr, напишите следующее:
struct addr addr_info;
В этом операторе объявлена переменная типа addr, которая называется addr_info. Таким образом, addr описывает вид структуры (ее тип), a addr_info является экземпляром (объектом) этой структуры.
Когда объявляется переменная-структура, компилятор автоматически выделяет количество памяти, достаточное, чтобы разместить все ее члены. На рис. 7.1 показано, как addr_info размещена в памяти; в данном случае предполагается, что целые переменные типа long занимают по 4 байта.+——————————————+
|Name (имя) 30 байт |
|Street (улица) 40 байт |
|City (город) 20 байт |
|State (штат) 3 байта |
|Zip (код) 4 байта |
Рис. 7.1. Расположение в памяти структуры addr_info
Одновременно с объявлением структуры можно объявить одну или несколько переменных. Например,
unsigned long int zip;
> addr_info, binfo, cinfo;
определяет тип структуры, называемый addr, и объявляет переменные этого типа addr_info, binfo и cinfo. Важно понимать, что каждая переменная-структура содержит собственные копии членов структуры. Например, поле zip в binfo отличается от поля zip в cinfo. Изменения в zip из binfo не повлияют на содержимое поля zip, находящегося в cinfo.
Если нужна только одна переменная-структура, то тег структуры является лишним. В этом случае наш пример объявления можно переписать следующим образом:
unsigned long int zip;
В этом случае объявляется одна переменная с именем addr_info, причем ее поля указаны в структуре, которая предшествует этому имени.
Общий вид объявления структуры такой:
причем тег или переменные-структуры могут быть пропущены, но только не оба одновременно.
Доступ к членам структуры
Доступ к отдельным членам структуры осуществляется с помощью оператора . (который обычно называют оператором точка или оператором доступа к члену структуры). Например, в следующем выражении полю zip в уже объявленной переменной-структуре addr_info присваивается значение ZIP-кода, равное 12345:
Этот отдельный член определяется именем объекта (в данном случае addr_info), за которым следует точка, а затем именем самого этого члена (в данном случае zip). В общем виде использование оператора точка для доступа к члену структуры выглядит таким образом:
Поэтому, чтобы вывести ZIP-код на экран, напишите следующее:
Будет выведен ZIP-код, который находится в члене zip переменной-структуры addr_infо.
Точно так же в вызове gets() можно использовать массив символов addr_infо.name:
Таким образом, в начало name передается указатель на символьную строку.
Так как name является массивом символов, то чтобы получить доступ к отдельным символам в массиве addr_info.name, можно использовать индексы вместе с name. Например, с помощью следующего кода можно посимвольно вывести на экран содержимое addr_info.name:
for(t=0; addr_info.name[t]; ++t)
Обратите внимание, что индексируется именно name (а не addr_info). Помните, что addr_info — это имя всего объекта-структуры, a name — имя элемента этой структуры. Таким образом, если требуется индексировать элемент структуры, то индекс необходимо указывать после имени этого элемента.
Информация, которая находится в одной структуре, может быть присвоена другой структуре того же типа при помощи единственного оператора присваивания. Нет необходимости присваивать значения каждого члена в отдельности. Как выполняется присваивание структур, показывает следующая программа:
y = x; /* присваение одной структуры другой */
После присвоения в y.a будет храниться значение 10.
Структура — это объединение одного или нескольких объектов (переменных, массивов, указателей, других структур и т.д.). Как и массив, она представляет собой совокупность данных. Отличием является то, что к ее элементам необходимо обращаться по имени и что различные элементы структуры не обязательно должны принадлежать одному типу.
Объявление структуры осуществляется с помощью ключевого слова struct, за которым идет ее тип и далее список элементов, заключенных в фигурные скобки:
тип элемента_n имя элемента_n;
Именем элемента может быть любой идентификатор. Как и выше, в одной строке можно записывать через запятую несколько идентификаторов одного типа.
Следом за фигурной скобкой, заканчивающей список элементов, могут записываться переменные данного типа, например:
struct date <. >a, b, c;
(при этом выделяется соответствующая память). Описание без последующего списка не выделяет никакой памяти; оно просто задает форму структуры. Введенное имя типа позже можно использовать для объявления структуры, например:
struct date days;
Теперь переменная days имеет тип date.
При необходимости структуры можно инициализировать, помещая вслед за описанием список начальных значений элементов.
Разрешается вкладывать структуры друг в друга, например:
Определенный выше тип data включает три элемента: day, month, year, содержащий целые значения (int). Структура man включает элементы name, fam, bd и voz. Первые два — name[20] и fam[20] — это символьные массивы из 20 элементов каждый. Переменная bd представлена составным элементом (вложенной структурой) типа data. Элемент age содержит значения целого типа int). Теперь можно определить переменные, значения которых принадлежат введенному типу:
struct man man_[100];
Здесь определен массив man_, состоящий из 100 структур типа man.
Чтобы обратиться к отдельному элементу структуры, необходимо указать его имя, поставить точку и сразу же за ней записать имя нужного элемента, например:
При работе со структурами необходимо помнить, что тип элемента определяется соответствующей строкой описания в фигурных скобках. Например, массив man_ имеет тип man, year является целым числом и т.п. Поскольку каждый элемент структуры относится к определенному типу, его имя может появиться везде, где разрешено использование значений этого типа. Допускаются конструкции вида man_[i]=man_[j]; где man_[i] и man_[j] — объекты, соответствующие единому описанию структуры. Другими словами, разрешается присваивать одну структуру другой по их именам.
Унарная операция & позволяет взять адрес структуры. Предположим, что определена переменная day:
Здесь day — это структура типа date, включающая три элемента: d, m, у. Другое определение
устанавливает тот факт, что db — это указатель на структуру типа date.
В этом случае для выбора элементов d, m, у структуры необходимо использовать конструкции:
Действительно, db — это адрес структуры, *db — сама структура. Круглые скобки здесь необходимы, так как точка имеет более высокий, чем звездочка, приоритет. Для аналогичных целей в языке Си предусмотрена специальная операция ->. Эта операция выбирает элемент структуры и позволяет представить рассмотренные выше конструкции в более простом виде:
db -> d; db -> m; db -> у;
Рассмотрим описание структуры:
Здесь фактически вводится новый тип данных — data. Теперь его можно использовать для объявления конкретных экземпляров структуры, например:
struct data а, b, с;
В язык Си введено специальное средство, позволяющее назначать имена типам данных (переименовывать). Таким средством является оператор typedef. Он записывается в следующем виде:
typedef тип имя;
Здесь «тип» — любой разрешенный тип данных и «имя» — любой разрешенный идентификатор.
typedef int INTEGER;
После этого можно сделать объявление:
Оно будет выполнять то же самое, что и привычное объявление int a,b;. Другими словами, INTEGER можно использовать как
синоним ключевого слова int.
Особую разновидность структур представляют собой битовые поля. Битовое поле — это последовательность соседних битов внутри одного, целого значения. Оно может иметь тип signed int или unsigned int и занимать от 1 до 16 битов. Поля размещаются в машинном слове в направлении от младших к старшим разрядам. Например, структура:
unsigned d:5; > i, j;
обеспечивает размещение данных в двух байтах (в одном слове). Если бы последнее поле было задано так: unsigned d:6, то оно размещалось бы не в первом слове, а в разрядах 0 — 5 второго слова.
В полях типа signed крайний левый бит является знаковым.
Поля используются для упаковки значений нескольких переменных в одно машинное слово с целью экономии памяти. Они не могут
быть массивами и не имеют адресов, поэтому к ним нельзя применять унарную операцию &.
Объединение — это некоторая переменная, которая может хранить (в разное время) объекты различного типа и размера. В результате появляется возможность работы в одной и той же области памяти с данными различного вида. Для описания объединения используется ключевое слово union, а соответствующий синтаксис аналогичен структурам.
Пусть задано определение:
Здесь ir имеет размер 2 байта, fr — 4 байта, cr — 1 байт. Размер переменной z будет равен размеру самого большого из трех приведенных
типов (т.е. 4 байтам). В один и тот же момент времени z может иметь значение только одной из переменных ir, fr или cr.
Передача структур в функции.
Структуры могут передаваться в качестве параметра в функцию, как и любой из базовых типов данных. Следующий пример использует структуру открывается сегодняшний день, который передается isLeapYear функцию определения, если года високосного.
Обычно вы хотели передать только день, но и вся структура переходит к иллюстрации ближнего структур функций.
«February», «Feb», 28,
«September», «Sep», 30,
«November», «Nov», 30,
«December», «Dec», 31
int isLeapYear(struct date d);
printf(«Enter the date (eg: 11/11/1980): «);
printf(«The date %d %s %d is «, d.day,
if (isLeapYear(d) == 0)
puts(«a leap year»);
int isLeapYear(struct date d)
if ((d.year % 4 == 0 && d.year % 100 != 0) ||
Присваивание структур.
5. Структуры данных.
Структуры («записи») представляют собой агрегаты разнородных данных (полей разного типа); в отличие от массивов, где все элементы имеют один и тот же тип.
int x, y; /* два целых поля */
char s[10]; /* и одно — для строки */
Структурный тип может иметь имя:
int x, y; /* два целых поля */
char str[10]; /* и одно — для строки */
Здесь мы объявили тип, но не отвели ни одной переменной этого типа (хотя могли бы). Теперь опишем переменную этого типа и указатель на нее:
struct XYS s2, *sptr = &s2;
Доступ к полям структуры производится по имени поля (а не по индексу, как у массивов):
#define ВЕС 0 struct < int вес, рост; >x;
#define РОСТ 1 x.рост = 175;
int x[2]; x[РОСТ] = 175;
Структура может содержать структуры другого типа в качестве полей:
a1.xys.x = 71; a1.z = 12;
Структура того же самого типа не может содержаться в качестве поля — рекурсивные определения запрещены. Зато нередко используются поля — ссылки на структуры такого же типа (или другого). Это позволяет организовывать списки структур:
struct node *next;
Очень часто используются массивы структур:
struct XYS array[20]; int i = 5, j;
Статические структуры можно описывать с инициализацией, перечисляя значения их полей в <> через запятую:
extern struct node n2;
В этом примере n2 описано предварительно для того, чтобы &n2 в строке инициализации n1 было определено.
Структуры одинакового типа можно присваивать целиком (что соответствует присваиванию каждого из полей):
struct XYS s1, s2; .
в отличие от массивов, которые присваивать целиком нельзя:
int a[5], b[5]; a = b; /* ОШИБОЧНО ! */
Пример обращения к полям структуры:
typedef struct _Point <
short x, y; /* координаты точки */
char *s; /* метка точки */
Point p; Point *pptr; short *iptr;
Point points[25]; /* вершины ломанной */
int color; /* цвет линии */
> aLine[10], *linePtr = & aLine[0];
pptr = &p; /* указатель на структуру p */
p.x = 1; p.y = 2; p.s = «Grue»;
linePtr->points[2].x = 54; aLine[5].points[0].y = 17;
В ы р а ж е н и е значение
iptr= &p.x | iptr= &pptr->x | iptr= &(pptr->x) | адрес поля
*pptr->s | *(pptr->s) | *p.s | p.s[0] | ‘G’
(*pptr).s | pptr->s | p.s | «Grue»
Вообще (&p)->field = p.field
Объединения — это агрегаты данных, которые могут хранить в себе значения данных разных типов на одном и том же месте.
A: | A.x int | Три поля
| A.y int | Получается как бы
А у объединений поля расположены «параллельно»,
на одном месте в памяти.
B: | B.i int | B.s char * | B.aa : B.aa.x int |
Это как бы «ящик» в который можно поместить значение любого типа из перечисленных, но не ВСЕ ВМЕСТЕ («и то и это», как у структур), а ПО ОЧЕРЕДИ («или/или»). Размер его достаточно велик, чтоб вместить самый большой из перечисленных типов данных.
Мы можем занести в union значение и интерпретировать его как другой тип данных это иногда используется в машинно-зависимых программах. Вот пример, выясняющий порядок байтов в short числах:
char s[2]; short i;
hi = x.s[1]; lo = x.s[0];
printf( «%d %d\n», hi, lo);
unsigned char s[sizeof(int)];
unsigned char *p;
for(n=0, p=u.s; n < sizeof(int); n++, p++)<
или порядок слов в long числах:
short a; /* low word */
short b; /* high word */
c.ab.a = 1; c.ab.b = 2; printf(«%08lx\n», c.l );
Мы уже знаем, что массив — это переменная, которая дает возможность хранить в программе множество значений одного и того же типа. Другими словами, массив дает возможность сгруппировать связанную информацию в одном имени, например, рейтинг 100 человек или зарплата 50 служащих. По мере усложнения программы возникает необходимость группировать связанную информацию различных типов данных. Например, предположим, что программа работает с информацией о служащих. Для каждого служащего необходимо поддерживать следующие данные:
char name[64]; // Фамилия
int age; // Возраст
char ssan[ll]; // Социальный номер служащего
int pay_grade; // Разряд по оплате
float salary; // Зарплата
unsigned employee_number; // Номер служащего
Предположим, что в программе есть несколько различных функций, которые работают с этой информацией. Каждый раз при вызове любой из этих функций необходимо следить за тем, что ей переданы все параметры, причем в указанном порядке. Как обсуждалось в разделе «Функции», чем больше у функции параметров, тем труднее программа для восприятия и больше вероятность ошибки. Для упрощения программа может создать структуру, в которой связанная информация группируется под одним именем. Например, поля сведений о служащих можно сгруппировать в структуру Employee, объявление которой имеет вид:
char name[64]; // Фамилия
int age; // Возраст
char ssan[ll]; // Социальный номер служащего
int pay_grade; // Разряд по оплате
float salary; // Зарплата
unsigned employee_number; // Номер служащего
Описание такого рода создает структуру типа Employee.
532. Структура как шаблон для объявления переменных
В С531 обсуждалась возможность языка Си для группирования связанной информации в структуру. Само по себе объявление структуры не создает никаких переменных. Структура есть шаблон, который программа может использовать в дальнейшем для объявления переменных. Таким образом, объявление структуры само по себе не приводит к выделению памяти. Это объявление используется компилятором только в том случае, если в программе будет объявлена переменная типа структура.
533. Тег или имя структуры
В С531 обсуждалась возможность языка Си для группирования связанных данных в структуру. Используя ключевое слово struct, программа может объявить структуру следующим образом:
char name[64]; // Фамилия
int age; // Возраст
char ssan[ll]; // Социальный номер служащего
int pay_grade; // Разряд по оплате
float salary; // Зарплата
unsigned employee_number; // Номер служащего >;
В данном случае именем структуры является идентификатор Employee. Программисты-пользователи языка Си называют имя структуры тегом структуры. Как мы увидим в дальнейшем, тег структуры используется для объявления переменной указанного типа. Следующее объявление создает структуру с именем Shape:
int type; // 0 — окружность, 1 — квадрат, 2 — треугольник
534. Различные способы объявления переменных типа структура
Итак, язык Си дает возможность группировать связанные данные в структуру (С531). Само по себе объявление структуры не создает переменной, а служит шаблоном для ее объявления. Описать переменную можно двумя способами. Первый способ, пусть в программе объявлена структура типа 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;
Второй способ, объявление переменной может непосредственно следовать за объявлением структуры:
char name[64]; // Фамилия
int age; // Возраст
char ssan[ll]; // Социальный номер служащего
int pay_grade; // Разряд по оплате
float salary; // Зарплата
unsigned employee_number; // Номер служащего
535. Члены структуры
Как уже известно, Си дает возможность группировать связанные данные в структуру. Например, в следующем операторе создается переменная triangle с использованием структуры типа Shape:
int type; // 0 — окружность, 1 — квадрат, 2 — треугольник
Каждый выделенный элемент информации структуры представляет собой отдельный член этой структуры. Структура Shape состоит из 5 членов: type, color, radius, area и perimeter. Для обращения к члену структуры используется операция «точка»(.). Например, в следующем фрагменте членам структуры присваиваются конкретные значения:
536. Распределение памяти под структуры
При объявлении переменной типа заданной структуры выделяется память для всех членов структуры. Например, при объявлении переменной типа Employee с объявлением структуры из С535 выделяет память, как показано на рис. 536.
Рис. 536. Распределение памяти под структуру на примере Employee
537. Адрес структуры как фактический параметр функции
Как известно, язык Си позволяет группировать информацию в виде структуры. В разделе «Дата и время» используется функция geldate для определения текущей системной даты. Функция присваивает текущую дату членам структуры типа date:
int da_year // Год char
char da_mon // Месяц
В следующей программе DOSDATE.C функция getdate используется для присваивания переменной cwr_date текущей системной даты:
struct date 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 выводит на экран значения всех членов структуры:
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);
struct Shape circle;
circle.area == 22.0 / 7.0 * circle.radius * circle.radius;
circle.perimeter = 2.0* 22.0 / 7.0 * circle.radius;
539. Изменение структуры внутри функции
В С538 была продемонстрирована возможность передачи переменной типа структура как параметра функции так же, как и для переменных любого типа. Если оказывается необходимым изменять значения членов структуры, то переменная должна передаваться не по значению, а по адресу (так же, как и переменная любого другого типа, значение которой требуется изменить). В следующей программе CHGSTRUC.C определяется функция change„structure, которая изменяет значения членов структуры типа Shape:
void change structure(struct Shape * shape)
(*shape).area = 22.0 / 7.0 * (*shape).radius * (*shape).radius;
(*shape).perimeter = 2.0 * 22.0 / 7.0 * (*shape).radius;
struct Shape 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);
Для передачи структуры как параметра функции используется указатель на переменную типа структура. Внутри функции доступ к членам структуры осуществляется посредством операции «звездочка» (*) -извлечения значения по указателю:
Эта операция рассматривается в С540.
540. Операция (*указатель).член
Если в функции необходимо изменять значения переменной типа структура, то программа должна передавать указатель на эту переменную. Внутри функции содержимое по этому указателю определяется с помощью операции «звездочка» (*):
В этом выражении сначала выполняется операция в скобках — «звездочка» (*), которая определяет адрес в памяти, отведенной для переменной типа структура. По имени члена компилятор Си добавляет к адресу смещение. Если скобки не указаны:
Си предполагает, что член структуры сам является указателем и операция «звездочка» (*) применяется к члену структуры. Такой синтаксис может быть корректен в том случае, если член структуры объявлен как указатель, например:
Как видно, второй член структуры является указателем на значение типа int. Предполагая, что указатель был присвоен некоторому адресу памяти, следующий оператор заносит по этому адресу значение 5:
*planet.some pointer = 5;
541. Формат указатель->член
В С540 мы видели, что если в функции необходимо изменять значения переменной типа структура, то программа должна передавать указатель на эту переменную. Для нахождения содержимого адреса внутри функции Си поддерживает два формата. Первый формат был использован в С540 — обращение к члену структуры следующим образом:
Второй формат имеет вид:
В следующей программе CHMEMBER.C функция change Jstructure использует второй формат для обращения к членам структуры, передаваемой функции по адресу.
void change_structure(struct Shape *shape)
shape->area = 22.0 / 7.0 * shape->radius * shape->radius;
shape->perimeter =2.0 * 22.0 / 7.0 * shape->radius;
struct Shape 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. Структура без тега
Тег структуры — это имя структуры. Тег структуры используется при объявлении переменной типа этой структуры. Однако при объявлении переменной типа структура тег указывать необязательно. Например, в следующем фрагменте объявлены две переменные типа структуры:
int type; // 0 — окружность, 1 — квадрат, 2 — треугольник
Если в программе не требуется обращения к структуре по имени (например, в прототипе функции или формальном параметре), то тег структуры можно не указывать. Однако наличие тега дает возможность другим программистам, читающим вашу программу, понять назначение структуры. Включение содержательных имен структур делает программу более удобочитаемой.
543. Область видимости объявлений структуры
В разделе «Функции» понятие область видимости раскрывалось как область программы, в которой объявление идентификатора (будь то переменная или функция) является доступным. При определении структуры необходимо иметь в виду область ее видимости. Можно было обратить внимание на то, что в программах, которые работают со структурами и функциями, объявления структур предшествуют объявлениям функций. В результате объявления структуры имеют глобальную область видимости, что дает возможность любой функции.обращаться к нему. Если структура объявлена внутри main, то единственной функцией, в которой доступно это объявление, является main. Если необходимо использовать объявление структуры в нескольких функциях, структура должна быть объявлена вне функций.
544. Инициализация структуры
Подобно массивам, Си дает возможность инициализировать структуры непосредственно при их объявлении. В следующей программе INITSTRU.C объявляется и инициализируется переменная типа структуры Shape:
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. Вложенные структуры
char name[64]; // Фамилия
int age; // Возраст
char ssan[ll]; // Социальный номер служащего
int pay_grade; // Разряд по оплате
float salary; // Зарплата
unsigned employee_number; // Номер служащего
Для обращения к члену вложенной структуры используется операция «точка» (.), сначала указывается член внешней структуры, затем член вложенной структуры:
547. Структура, содержащая массив
Типы членов структуры могут быть любыми, в том числе структурами или массивами. Если член структуры — массив, то обращаться к нему можно по имени, которому должна предшествовать операция «точка» (.). В следующей программе STRUARRA.C инициализируются поля структуры, включая массив. В программе выполняется цикл для вывода на экран всех элементов массива:
> current_date = ( «Июль», 7, 4, 1994 );
for (i = 0; current_date.month_name[i]; i++)
548. Создание массива структур
Как известно, использование массивов дает возможность хранить множество значений одного и того же типа. Большинство массивов этого раздела имеет тип int, float или char. Однако Си дает возможность объявить массив типа структуры. Например, в следующем объявлении создается массив, в котором хранятся сведения о 100 служащих:
char name[64]; // Фамилия
int age; // Возраст
char ssan[ll]; // Социальный номер служащего
int pay grade; // Раэряд по оплата
float salary; // Зарплата
unsigned employee_number; // Номер служащего
Если предположить, что значения для каждого служащего уже проинициализированы, то в следующем фрагменте внутри цикла for выводится имя и номер каждого служащего:
for (emp = 0; emp < 100; emp++)
printf(«Номер служащего %s — %d\n»,
При использовании массива структур операция «точка» (.) применяется ко всем элементам массива.
4.12. Структуры и объединения
В языках Си/Си++ понятие структуры аналогично понятию записи (record) в Паскале. Это структурированный тип данных, представляющий собой поименованную совокупность разнотипных элементов. Тип структура обычно используется при разработке информационных систем, баз данных.
Правила использования структур обсудим на примере, аналогичном тому, который рассматривался в разделе 3.20 применительно к Паскалю.
Сведения о выплате студентам стипендии требуется организовать в виде, показанном на рис. 46.
Элементы такой структуры (фамилия, курс, группа, стипендия) называются полями. Каждому полю должно быть поставлено в соответствие имя и тип.
Формат описания структурного типа следующий:
В конце обязательно ставится точка с запятой (это оператор). Для рассмотренного примера определение соответствующего структурного типа может быть следующим:
После этого student становится именем структурного типа, который может быть назначен некоторым переменным. В соответствие со стандартом Си это нужно делать так:
struct student stud1, stud2;
Правила Си++ разрешают в этом случае служебное слово struct опускать и писать
student stud1, stud2;
Здесь stud1 и stud2 — переменные структурного типа. Допускаются и другие варианты описания структурных переменных. Можно вообще не задавать имя типа, а описывать сразу переменные:
struct (char fam[30];
> studi, stud2, *pst;
В этом примере кроме двух переменных структурного типа объявлен указатель pst на такую структуру. В данном описании можно было сохранить имя структурного типа student.
Обращение к элементам (полям) структурной величины производится с помощью уточненного имени следующего формата:
Снова все похоже на Паскаль. Примеры уточненных имен для описанных выше переменных:
Значения элементов структуры могут определяться вводом, присваиванием, инициализацией. Пример инициализации в описании:
Пусть в программе определен указатель на структуру
student *pst, stud1;
Тогда после выполнения оператора присваивания
к каждому элементу структурной переменной studi можно обращаться тремя способами. Например, для поля fam
stud1.fam или (*pst).fam или pst→fam
В последнем варианте используется знак операции доступа к элементу структуры: —>. Аналогично можно обращаться и к другим элементам этой переменной:
Поля структуры могут сами иметь структурный тип. Такие величины представляют многоуровневые деревья.
Допускается использование массивов структур. Например, сведения о 100 студентах могут храниться в массиве, описанном следующим образом:
Тогда сведения об отдельных студентах будут обозначаться, например, так: stud[l].fam, stud [5].kurs и т.п. Если нужно взять первую букву фамилии 25-го студента, то следует писать:
Пример 1. Ввести сведения об N студентах. Определить фамилии студентов, получающих самую высокую стипендию.
Элемент структуры типа поля битов. Использование структуры в программе на Си позволяет работать с отдельными битами, т.е. с разрядами двоичного кода. Для этого используются элементы структуры типа поля битов. Формат структуры, содержащий поля битов, следующий:
В качестве типа полей могут использоваться спецификаторы int, unsigned, signed. Минимальной величиной такого типа может быть структура, состоящая всего из одного битового поля. Пример описания такой структуры:
Конечно, для переменной cod в памяти будет выделено 8 бит (1 байт), но использоваться будет только один первый бит.
Объединение. Объединение — это еще один структурированный тип данных. Объединение похоже на структуру и в своем описании отличается от структуры тем, что вместо ключевого слова struct используется слово union.
Объединение отличается от структуры способом организации во внутренней памяти. Все элементы объединения в памяти начинаются с одного байта.
Как вывести содержимое C-структуры?
После нескольких лет джаваскрипта осваиваю сишечку. Не хватает аналога console.dir(object), который выводит на экран содержимое объекта в виде:
Возможно ли такое в си для структур?
Возможно ли такое в си для структур?
Нет. В C нет рефлексии.
Только если через gdb
Значит-таки придется осваивать gdb)

ОП, Си — язык со строгой типизацией
Если у тебя есть структура, то ты наверняка знаешь её тип, вот и выводи её содержимое printf’ом
Если структуру задавал не ТС, можно сделать какую-нибудь фигню вроде

В сишечке? Ну толсто же.
В Си слабая типизация.

Re: ОП, Си — язык со строгой типизацией
А я всегда думал, что С — язык со слабой типизацией.
Может имелась ввиду статическая типизация?
а чего без плюсиков? там можно для структур определить оператор << в исходящий поточек.
там можно для структур определить оператор << в исходящий поточек
Человек хочет рефлексию, то, что ты ему предлагаешь, — это просто другой способ сделать то же самое, что и с принтфом

а есть нормальные парсеры для стандартного Си?
эту хреноту можно было бы генерить совершенно автоматически
Что ты подразумеваешь под "нормальными парсерами"?
эту хреноту можно было бы генерить совершенно автоматически
Т.к. это никому нахрен не нужно, то "стандартных" библиотек для этих извращений наверняка нет!

так для плюсиков-то есть решение
откуда-то с просторов интернета, не мое
Сначала, для чистоты кода, будем юзать типизированные выражения. Т.е. вместо int x будем писать (int) x. Вот нужный макрос:
Предполагается что мы будем юзать макрос REFLECTABLE, используемый вот так:
Используя Boost.PP будем проходить по аргументам и генерировать данные:
Чтобы получить доступ к полям, которые приватные, этим кодом добавляется во френды вот такой класс:
Теперь пройдемся по полям, и отдадим эти данные в visitor:
ну и наконец можно использовать.
И наконец, чтобы распечатать поля как хочется ТСу, можно написать такую штуку:
name=
sin_a
credo=«ватник»
action=«язабан»
ура, мы надругались на C++, теперь в нём всё работает!
теперь нужно всё это стереть и перейти на нормальный язык, т.е. на Java или C#

Какое говно. Сколько раз тебе уже предлагали убить себя?

Если запилишь свою рефлексию (через DWARF или что там на венде) — да.

Что ты подразумеваешь под «нормальными парсерами»?
ну например генератор парсеров AntLR автор в оригинале написал как раз для парсанья Си-кода. Он сдавал заказчику огромный кусок работы, написанный на Си. А заказчик сказал, что в комментариях нужно к вообще всему коду, над каждой функцией надписать, какие переменные какого типа в этом коде используются и сколько раз. Автор был достаточно ленив, чтобы ручками всё это выщитывать, поэтому он написал парсер, который раскладывал Си-код в AST, добывал из него нужную информацию и сваливал ее в комментарии.
Хотелось бы что-то такое, только реально использующееся в продакшене, гарантированно хорошо работающее =)
Вывод любых структур на экран
Понятно, без больших мучений — никак. А как можно хотя бы немного упростить/автоматизировать? Или использовать может язык c#? синтаксис там предельно близок.
Думаю, что самым простым для Вас будет определить операторы вывода для структур:
В C# это можно написать метод, вроде:
который будет выводить значения всех публичных экземплярных полей структуры или класса. Вот небольшой пример:
Сомневаюсь, что удобство вывода структур может являться основанием для выбора языка программирования.
sin_a