Вывести структуру на экран
Подскажите как вывести на экран структуру? Попробовал вот так:
std::copy( records.begin(), records.end(), std::ostream_iterator( std::cout, " ") );
Серьезность Код Описание Проект Файл Строка Ошибка C2679 бинарный «=»: не найден оператор, принимающий правый операнд типа «CRecord» (или приемлемое преобразование отсутствует) AdmHyTech c:\program files (x86)\microsoft visual studio 14.0\vc\include\xutility 2148
Как вывести содержимое C-структуры?
После нескольких лет джаваскрипта осваиваю сишечку. Не хватает аналога console.dir(object), который выводит на экран содержимое объекта в виде:
поле: значение .
Возможно ли такое в си для структур?
makoven ★★★★★
25.09.14 06:36:34 MSK
← 1 2 →

Возможно ли такое в си для структур?
ymn ★★★★★
( 25.09.14 07:11:49 MSK )
Нет. В C нет рефлексии.
anonymous
( 25.09.14 07:12:42 MSK )
Только если через gdb
gh0stwizard ★★★★★
( 25.09.14 07:13:35 MSK )
Ответ на: комментарий от gh0stwizard 25.09.14 07:13:35 MSK
Значит-таки придется осваивать gdb)
makoven ★★★★★
( 25.09.14 07:18:34 MSK ) автор топика

ОП, Си — язык со строгой типизацией
Если у тебя есть структура, то ты наверняка знаешь её тип, вот и выводи её содержимое printf’ом
Northsoft ★★
( 25.09.14 07:48:23 MSK )
Ответ на: ОП, Си — язык со строгой типизацией от Northsoft 25.09.14 07:48:23 MSK
Если структуру задавал не ТС, можно сделать какую-нибудь фигню вроде
typedef enum < TYPE_END = 0, TYPE_CHAR, TYPE_SHORT, TYPE_INT, TYPE_LONG, . >struct_types; typedef struct < char name[128]; struct_types type; >struct_entry;
потом для нужных структур задать
struct_entry some_struct_def[size] = < , , . >;
и выводить их как-то так (правда, могут быть косяки, если у тебя часть структур с выравниванием, а часть — без):
void printstruct(struct_entry *E, void *struct_ptr) < while(*E.name && *E.type)< switch(E.type)< case TYPE_INT: printf("%s: %d\n", E.name, (int)*struct_ptr); struct_ptr += 4; break; . >E++; >
Eddy_Em ☆☆☆☆☆
( 25.09.14 08:41:09 MSK )
anonymous
( 25.09.14 09:12:04 MSK )
Ответ на: комментарий от anonymous 25.09.14 09:12:04 MSK

В сишечке? Ну толсто же.
UVV ★★★★★
( 25.09.14 10:01:58 MSK )
Ответ на: ОП, Си — язык со строгой типизацией от Northsoft 25.09.14 07:48:23 MSK
В Си слабая типизация.
Deleted
( 25.09.14 10:04:43 MSK )
Ответ на: ОП, Си — язык со строгой типизацией от Northsoft 25.09.14 07:48:23 MSK

Re: ОП, Си — язык со строгой типизацией
А я всегда думал, что С — язык со слабой типизацией.
Может имелась ввиду статическая типизация?
Freyr69 ★★★
( 25.09.14 10:05:37 MSK )
а чего без плюсиков? там можно для структур определить оператор
Sanik
( 25.09.14 10:09:22 MSK )
Ответ на: комментарий от Sanik 25.09.14 10:09:22 MSK
Человек хочет рефлексию, то, что ты ему предлагаешь, — это просто другой способ сделать то же самое, что и с принтфом
Gvidon ★★★★
( 25.09.14 10:36:30 MSK )
Ответ на: комментарий от Eddy_Em 25.09.14 08:41:09 MSK

а есть нормальные парсеры для стандартного Си?
эту хреноту можно было бы генерить совершенно автоматически
stevejobs ★★★★☆
( 25.09.14 11:52:41 MSK )
Ответ на: комментарий от stevejobs 25.09.14 11:52:41 MSK
Что ты подразумеваешь под «нормальными парсерами»?
эту хреноту можно было бы генерить совершенно автоматически
Т.к. это никому нахрен не нужно, то «стандартных» библиотек для этих извращений наверняка нет!
Eddy_Em ☆☆☆☆☆
( 25.09.14 12:03:21 MSK )
Ответ на: комментарий от Sanik 25.09.14 10:09:22 MSK

так для плюсиков-то есть решение
откуда-то с просторов интернета, не мое
Сначала, для чистоты кода, будем юзать типизированные выражения. Т.е. вместо int x будем писать (int) x. Вот нужный макрос:
#define REM(. ) __VA_ARGS__ #define EAT(. ) // Retrieve the type #define TYPEOF(x) DETAIL_TYPEOF(DETAIL_TYPEOF_PROBE x,) #define DETAIL_TYPEOF(. ) DETAIL_TYPEOF_HEAD(__VA_ARGS__) #define DETAIL_TYPEOF_HEAD(x, . ) REM x #define DETAIL_TYPEOF_PROBE(. ) (__VA_ARGS__), // Strip off the type #define STRIP(x) EAT x // Show the type without parenthesis #define PAIR(x) REM x
Предполагается что мы будем юзать макрос REFLECTABLE, используемый вот так:
REFLECTABLE ( (const char *) name, (int) age )
Используя Boost.PP будем проходить по аргументам и генерировать данные:
// A helper metafunction for adding const to a type template struct make_const < typedef T type; >; template struct make_const < typedef typename boost::add_const::type type; >; #define REFLECTABLE(. ) \ static const int fields_n = BOOST_PP_VARIADIC_SIZE(__VA_ARGS__); \ friend struct reflector; \ template \ struct field_data <>; \ BOOST_PP_SEQ_FOR_EACH_I(REFLECT_EACH, data, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) #define REFLECT_EACH(r, data, i, x) \ PAIR(x); \ template \ struct field_data \ < \ Self & self; \ field_data(Self & self) : self(self) <>\ \ typename make_const::type & get() \ < \ return self.STRIP(x); \ >\ typename boost::add_const::type & get() const \ < \ return self.STRIP(x); \ >\ const char * name() const \ \ >; \
Чтобы получить доступ к полям, которые приватные, этим кодом добавляется во френды вот такой класс:
struct reflector < //Get field_data at index N templatestatic typename T::template field_data get_field_data(T& x) < return typename T::template field_data(x); > // Get the number of fields template struct fields < static const int n = T::fields_n; >; >;
Теперь пройдемся по полям, и отдадим эти данные в visitor:
struct field_visitor < templatevoid operator()(C& c, Visitor v, I) < v(reflector::get_field_data(c)); > >; template void visit_each(C & c, Visitor v) < typedef boost::mpl::range_c::n> range; boost::mpl::for_each(boost::bind(field_visitor(), boost::ref(c), v, _1)); >
ну и наконец можно использовать.
struct Person < Person(const char *name, int age) : name(name), age(age) < >private: REFLECTABLE ( (const char *) name, (int) age ) >;
И наконец, чтобы распечатать поля как хочется ТСу, можно написать такую штуку:
struct print_visitor < templatevoid operator()(FieldData f) < std::cout >; template void print_fields(T & x)
int main()

name= sin_a
credo=«ватник»
action=«язабан»
ура, мы надругались на C++, теперь в нём всё работает!
теперь нужно всё это стереть и перейти на нормальный язык, т.е. на Java или C#
stevejobs ★★★★☆
( 25.09.14 12:18:17 MSK )
Ответ на: комментарий от stevejobs 25.09.14 12:18:17 MSK

Какое говно. Сколько раз тебе уже предлагали убить себя?
tailgunner ★★★★★
( 25.09.14 12:21:14 MSK )

Если запилишь свою рефлексию (через DWARF или что там на венде) — да.
tailgunner ★★★★★
( 25.09.14 12:23:32 MSK )
Ответ на: комментарий от Eddy_Em 25.09.14 12:03:21 MSK

Что ты подразумеваешь под «нормальными парсерами»?
ну например генератор парсеров AntLR автор в оригинале написал как раз для парсанья Си-кода. Он сдавал заказчику огромный кусок работы, написанный на Си. А заказчик сказал, что в комментариях нужно к вообще всему коду, над каждой функцией надписать, какие переменные какого типа в этом коде используются и сколько раз. Автор был достаточно ленив, чтобы ручками всё это выщитывать, поэтому он написал парсер, который раскладывал Си-код в AST, добывал из него нужную информацию и сваливал ее в комментарии.
Хотелось бы что-то такое, только реально использующееся в продакшене, гарантированно хорошо работающее =)
stevejobs ★★★★☆
( 25.09.14 12:24:40 MSK )
Ответ на: комментарий от stevejobs 25.09.14 12:18:17 MSK

Всё делается ещё проще простым препроцессором
Структурный тип данных в языке Си
Примером структуры может послужить любой объект, для которого описывается ряд его характеристик, имеющих значение в данной программе. Например, для книг это может быть название, автор, количество страниц; для окружности — координаты центра, диаметр, цвет. На языке программирования C объявление вышеупомянутых структурных типов данных может выглядеть так:
struct book char title[50]; char author[30]; int pages; >; struct circle int x, y; float dia; char color[10]; >;
В данном случае мы как бы создаем новый тип данных, но еще не объявляем переменных этих типов. Обратите внимание на точку с запятой в конце объявлений.
Чаще переменные структур объявляются так:
struct circle a, b, c; struct book mybook;
Здесь объявляются три структуры типа circle и одна структура типа book . Можно объявлять типы структур и их переменные по-иному, но мы для избежания путаницы рассматривать другие способы не будем.
Каждая переменная типа circle содержит четыре элемента (или поля) — x , y , dia , color . Можно сказать, что они представляют собой вложенные переменные. Причем эти переменные разных типов. Таким образом переменная-структура позволяет объединить под одним именем ряд разнородных данных. Обычно это нужно для удобства обработки данных. Если нельзя было бы создавать структуры, то пришлось бы создавать множество независимых переменных или ряд массивов, явной взаимосвязи между которыми не было бы. Структуры же позволяют объединять взаимосвязанные данные. Это конечно еще не объектно-ориентированное программирование, но уже взгляд в его сторону.
Объявив переменную структурного типа, мы можем получить доступ к каждому ее элементу для присваивания, изменения или получения значения:
a.x = 10; a.dia = 2.35; printf("%.2f ", a.dia);
Значение элементов структуры можно сразу определять при объявлении переменной, что похоже на инициализацию массивов:
struct book lang_c = {"Language C", "Ritchi", 99};
Значение переменной-структуры можно присвоить переменной того же типа:
struct book { char *title, *author; int pages; }; struct book old, new; old.title = "GNU/Linux"; old.author = "people"; old.pages = 20213; new = old; new.pages += 2000; printf("%d, %d\n", old.pages, new.pages);
В примере данные переменной old присваиваются new . В итоге вторая структура содержит копию данных первой. То, что можно выполнять присваивание по отдельным полям должно быть понятно.
Структуры и функции
Структуры-переменные можно передавать в функции в качестве аргументов. Также функция может возвращать структуру. Структуры передаются по значению, как обычные переменные, а не по ссылке, как массивы.
Рассмотрим программу, в которой одна функция возвращает структуру, а другая — принимает структуру в качестве параметра:
#include #include struct circle { int x, y; float dia; char color[10]; }; struct circle new_circle(); void cross (struct circle); int main () { struct circle a; a = new_circle(); cross(a); } struct circle new_circle() { struct circle new; printf("Координаты: "); scanf("%d%d", &new.x, &new.y); printf("Диаметр: "); scanf("%f", &new.dia); printf("Цвет: "); scanf("%s", new.color); return new; } void cross (struct circle c) { double hyp; hyp = sqrt((double) c.x * c.x + (double) c.y * c.y); printf("Расстояние: %.2lf\n", hyp); if (hyp c.dia / 2) puts("Пересекает"); else puts("Не пересекает"); }
Примечание. При компиляции программы в GNU/Linux команда выглядит так: gcc program.c -lm . Это связано с использованием библиотеки с математическими функциями.
- Объявляется структура circle как глобальный тип данных. Таким образом любая, а не только main , функция может создавать переменные этого типа.
- Функция new_circle() возвращает структуру, а функция cross() принимает структуру по значению. Следует отметить, что можно создавать функции, которые как принимают (возможно, несколько структур) так и возвращают структуру.
- В функции new_circle создается переменная new типа struct circle , поля которой заполняются пользователем. Функция возвращает значение переменной new в функцию main , где это значение присваивается переменной a , которая также принадлежит типу sctruct circle .
- Функция cross определяет, пересекает ли круг начало координат. В ее теле вычисляется расстояние от центра круга до начала координат. Это расстояние является гипотенузой прямоугольного треугольника, длина катетов которого равна значениям x и у . Далее, если гипотенуза меньше радиуса, то круг пересекает начало координат, т.е. точку (0, 0).
- В функции main при вызове cross() данные, содержащиеся в переменной a , копируются и присваиваются переменной c .
Указатели и структуры
В отличие от массивов, структуры передаются в функции по значению. Это не всегда рационально, т.к. структуры могут быть достаточно большого размера, и копирование таких участков памяти может замедлять работу программы. Поэтому часто структуры в функцию передают по ссылке, при этом можно использовать как указатель, так и операцию получения адреса.
// переменная-структура struct book new; // указатель на структуру struct book *pnew; // передаем адрес reader(&new); pnew = &new; // передаем указатель reader(pnew);
Тогда функция reader должна иметь примерно такое объявление:
void reader(struct book *pb);
Возникает вопрос, как при использовании указателей обращаться к элементам структур? Во первых надо получить саму структуру, т.е. если pnew указатель, то сама структура будет *pnew . Далее можно уже обращаться к полям через точку: *pnew.title . Однако это выражение не верно, т.к. приоритет операции «точка» (обращение к полю) выше операции «звездочка» (получить значение по адресу). Таким образом приведенная запись сначала пытается получить значение поля title у указателя pnew , но у pnew нет такого поля. Проблема решается с помощью скобок, которые изменяют порядок выполнения операций: (*pnew).title . В таком случае сначала извлекается значение по адресу pnew , это значение является структурой. Затем происходит обращение к полю структуры.
В языке программирования C записи типа (*pnew).title часто заменяют на такие: pnew->title , что позволяет синтаксис языка. Когда в программе вы видите стрелку (тире и скобка) всегда помните, то, что написано до стрелки, — это указатель на структуру, а не переменная-структура.
Пример кода с использованием указателей:
#include struct circle { int x, y; float dia; }; void inversion (struct circle *); int main () { struct circle cir, *pc = ○ pc->x = 10; pc->y = 7; pc->dia = 6; inversion(pc); printf("x = %d, y = %d\n", cir.x, cir.y); } void inversion(struct circle *p) { p->x = -p->x; p->y = -p->y; }
Массивы структур
Обычно создание в программе одной переменной структурного типа не имеет особого смысла. Чаще структурами пользуются, когда необходимо описать множество похожих объектов, имеющих разные значения признаков. Значения каждого объекта следует объединить вместе (в структуру) и тем самым отделить от значений других объектов. Например, описание ряда книг или множества людей. Таким образом мы можем организовать массив, где каждый элемент представляет собой отдельную структуру, а все элементы принадлежат одному и тому же структурному типу.
Напишем программу для учета персональных компьютеров в организации. Каждая структура будет описывать определенные модели и содержать поле, в котором будет указано количество таких объектов. Поэтому при объявлении структурного типа данных следует описать такие поля как тип компьютера, модель процессора, количество.
Программа будет предоставлять возможность получать информацию о всех моделях и изменять количество компьютеров указанной пользователем модели. В программе будут определены две функции (помимо main ): для вывода всей информации и для изменения количества компьютеров.
#include #define N 4 struct computer { char *type; char *proc; int qty; }; void viewer (struct computer *); void changer (struct computer *); int main () { struct computer comps[N]= { "Desktop", "Core", 20, "Notebook", "Core", 5, "Desktop", "AMD", 10, "Notebook", "AMD", 2, }; viewer(comps); changer(comps); viewer(comps); } void viewer (struct computer *comp) { for (int i = 0; i N; i++, comp++) printf("%2d) %-8s - %-15s: %3d\n", i+1, comp->type, comp->proc, comp->qty); } void changer (struct computer *comp) { int i, differ; printf("Введите номер модели: "); scanf("%d", &i); i--; printf("На сколько уменьшить или увеличить: "); scanf("%d", &differ); (comp+i)->qty += differ; }
- Массив структур инициализируется при его объявлении.
- Функции viewer() и changer() принимают указатели на структуру computer .
- В теле viewer указатель инкрементируется в заголовке цикла; таким образом указывая на следующий элемент массива, т.е. на следующую структуру.
- В выражении (comp+i)->qty скобки необходимы, т.к оператор -> имеет более высокий приоритет. Скобки позволяют сначала получить указатель на i-ый элемент массива, а потом обратиться к его полю.
- Декрементирование i в функции changer связано с тем, что индексация начинается с нуля, а номера элементов массива, которые пользователь видит на экране, с единицы.
- Для того, чтобы уменьшить количество компьютеров, при запросе надо ввести отрицательное число.
Пример результата работы программы:
1) Desktop - Core : 20 2) Notebook - Core : 5 3) Desktop - AMD : 10 4) Notebook - AMD : 2 Введите номер модели: 3 На сколько уменьшить или увеличить: -2 1) Desktop - Core : 20 2) Notebook - Core : 5 3) Desktop - AMD : 8 4) Notebook - AMD : 2
Перепишите приведенную выше программу и дополните ее функцией, которая позволяет добавлять в массив новый элемент-структуру.
Курс с решением задач:
pdf-версия
Структуры
Теги: Си структуры, инициализация структуры, инициализация вложенных структур, указатель на структуру, вложенные структуры, указатели на вложенные структуры, указатели на поля структуры, выравнивание полей структуры, pragma pack.
Введение
Мир вокруг можно моделировать различными способами. Самым естественным из них является представление о нём, как о наборе объектов. У каждого объекта есть свои свойства. Например, для человека это возраст, пол, рост, вес и т.д. Для велосипеда – тип, размер колёс, вес, материал, изготовитель и пр. Для товара в магазине – идентификационный номер, название, группа, вес, цена, скидка и т.д.
У классов объектов набор этих свойств одинаковый: все собаки могут быть описаны, с той или иной точностью, одинаковым набором свойств, но значения этих свойств будут разные.
Все самолёты обладают набором общих свойств в пределах одного класса. Если же нам надо более точное описание, то можно выделить подклассы: самолёт амфибии, боевые истребители, пассажирские лайнеры – и в пределах уже этих классов описывать объекты. Например, нам необходимо хранить информацию о сотрудниках компании. Каждый сотрудник, в общем, обладает большим количеством разных свойств. Мы выберем только те, которые нас интересуют для решения прикладной задачи: пол, имя, фамилия, возраст, идентификационный номер. Для работы с таким объектом нам необходима конструкция, которая бы могла агрегировать различные типы данных под одним именем. Для этих целей в си используются структуры.
- Объявление структуры
- Начальная инициализация структур
- Определение нового типа
- Указатели на структуру
- Устройство структуры в памяти
- Приведение типов
- Вложенные структуры
- Указатели на поля и вложенные структуры
- Примеры
Объявление структуры
Синтаксис объявления структуры
struct < ; ; . ; >;
struct point_t < int x; int y; >; //Тут стоит точка с запятой!
Полями структуры могут быть любые объявленные типы, кроме самой структуры этого же типа, но можно хранить указатель на структуру этого типа:
struct node < void* value; struct node next; >;
struct node < void* value; struct node *next; >;
В том случае, если несколько полей имеют один тип, то их можно перечислить через запятую:
struct Point3D < int x, y, z; >;
После того, как мы объявили структуру, можно создавать переменную такого типа с использованием служебного слова struct. Доступ до полей структуры осуществляется с помощью операции точка:
#include #include #include struct point_t < int x; int y; >; void main()
Структура, объявленная в глобальном контексте, видна всем. Структура также может быть объявлена внутри функции:
#include #include #include void main() < struct point_t < int x; int y; >; struct point_t A; float distance; A.x = 10; A.y = 20; distance = sqrt((float) (A.x*A.x + A.y*A.y)); printf("x = %.3f", distance); getch(); >
Можно упростить пример: синтаксис языка позволяет создавать экземпляры структуры сразу же после определения:
struct point_t < int x; int y; >A; float distance;
Структура также может быть анонимной. Тогда мы не сможем использовать имя структуры в дальнейшем.
#include #include #include void main() < struct < int x; int y; >A; float distance; A.x = 10; A.y = 20; distance = sqrt((float) (A.x*A.x + A.y*A.y)); printf("x = %.3f", distance); getch(); >
В этом примере мы создали переменную A. Она является структурой с двумя полями.
Начальная инициализация структур
Структуру можно инициализировать во время создания как массив. Поля в этом случае будут присваиваться по порядку.
#include #include #include struct gasket < float weight; unsigned height; unsigned diameter; >; void main() < struct gasket obj = < 12.f, 120, 30 >; printf("gasket info:\n"); printf("-------------------\n"); printf("weight: %4.3f kg\n", obj.weight); printf("height: %6d cm\n", obj.height); printf("diameter: %4d cm\n", obj.diameter); getch(); >
Замечание: таким образом можно только иницализировать структуру. Присваивать значение всей структуре таким образом нельзя.
Современный стандарт си позволяет инициализировать поля структуры по имени. Для этого используется следующий синтакис:
#include typedef struct thing < int a; float b; const char *c; >thing_t; int main() < thing_t t = < .a = 10, .b = 1.0, .c = "ololololo" >; printf("%s\n", t.c); printf("%d\n", t.a); printf("%f\n", t.b); _getch(); >
Определение нового типа
Когда мы определяем новую структуру с помощью служебного слова struct, в пространстве имён структур (оно не имеет ничего общего с пространствами имён С++) создаётся новый идентификатор. Для доступа к нему необходимо использовать служебное слово struct. Можно определить новый тип с помощью служебного слова typedef. Тогда будет создан псевдоним для нашей структуры, видимый в глобальном контексте.
#include #include //Определяем новую структуру struct point_t < int x; int y; >; //Определяем новый тип typedef struct point_t Point; void main() < //Обращение через имя структуры struct point_t p = ; //Обращение через новый тип Point px = ; getch(); >
Теперь при работе с типом Point нет необходимости каждый раз писать слово struct. Два объявления можно объединить в одно
typedef struct point_t < int x; int y; >Point;
Замечание. Если мы создаём новый тип-структуру, полем которого является указатель на этот же тип, то его необходимо объявлять явно с использованием служебного слова struct
typedef struct Node < int value; struct Node *next; >Node;
Указатели на структуру
Указатель на структуру создаётся как обычно. Отличие заключается в том, что можно обращаться к полям структуры через указатель с помощью операции «стрелка» (минус + больше). Пример – пользователь вводит число – размер массива пользователей. Поле этого вводит для каждого из них логин и пароль. Третье поле — идентификатор – задаётся автоматически. После этого все пользователи выводятся на экран.
#include #include #include #include #define MAX_SIZE 20 typedef struct User < char *login; char *password; int id; >User; void jsonUser(User *user) < printf("\n", user->id, user->login, user->password); > void freeUsersArray(User** users, unsigned size) < unsigned i; for (i = 0; i < size; i++) < free((*users)[i].login); free((*users)[i].password); >free(*users); > void main() < User *users = NULL; unsigned size; char buffer[128]; unsigned i; printf("Enter number of users: "); scanf("%d", &size); size = size for (i = 0; i < size; i++) < jsonUser(&users[i]); >freeUsersArray(&users, size); getch(); >
Обратите внимание на удаление массива структур: при удалении экземпляра структуры он не удаляет своих полей самостоятельно, поэтому необходимо сначала удалять поля, после этого удалять сам массив.
При вызове функции jsonUser мы передаём указатель на экземпляр структуры, поэтому внутри функции доступ до полей осуществляется с помощью оператора стрелка.
Устройство структуры в памяти
Поля структуры расположены в памяти друг за другом. Тип поля определяет сдвиг относительно предыдущего поля. Имя поля — это сдвиг относительно адреса экземпляра. На самом деле размер структуры не всегда равен сумме размеров её полей: это связано с тем, что компилятор оптимизирует расположение структуры в памяти и может поля небольшого размера подгонять до чётных адресов.
#include #include struct Test1 < char a; char b; int c; >A; struct Test2 < int x; int y; >B; struct Test3 < char a; char b; char c; int d; >C; void main()
Первая структура должна иметь размер 6 байт, вторая 8 байт, третья 7 байт, однако на 32-разрядной машине компилятор VC сделает их все три равными 8 байт. Стандарт гарантирует, что поля расположены друг за другом, но не гарантирует, что непрерывно.
Есть возможность изменить упаковку структур в памяти. Можно явно указать компилятору каким образом производить упаковку полей структуры, объединений или полей класса. Каким образом это делать, зависит от компилятора. Один из самых распространённых способов прагма pack()
#pragma pack(n)
У неё есть несколько разновидностей, рассмотрим только одну. pragma pack(n) указывает значение в байтах, используемое для упаковки. Если параметр компилятора не заданы для модуля значения по умолчанию n 8. Допустимыми значениями являются 1, 2, 4, 8 и 16. Выравнивание поля происходит по адресу, кратному n или сумме нескольких полей объекта, в зависимости от того, какая из этих величин меньше.
Использование #pragma pack не приветствуется: логика работы программы не должна зависить от внутреннего представления структуры (если, конечно, вы не занимаетесь системным программированием или ломаете чужие программы и сети).
Приведение типов
Стандартом поведение при приведении одной структуры к другой не определено. Это значит, что даже если структуры имеют одинаковые поля, то нельзя явно кастовать одну структуру до другой.
#include #include struct Point < int x; int y; >; struct Apex < int a; int b; >; void main() < struct Point point = ; struct Apex apex; apex = (*(struct Apex*)(&point)); printf("a = %d, b = %d", apex.a, apex.b); getch(); >
Этот пример работает, но это хак, которого необходимо избегать. Правильно писать так
void main() < struct Point point = ; struct Apex apex; apex.a = point.x; apex.b = point.y; printf("a = %d, b = %d", apex.a, apex.b); getch(); >
Привести массив к структуре (или любому другому типу) по стандарту также невозможно (хотя в различных компиляторах есть для этого инструменты).
Но в си возможно всё.
#include #include struct Point < int x; int y; >; void main() < struct Point point = ; int x[3] = ; point = (*(struct Point*)(x)); printf("a = %d, b = %d", point.x, point.y); getch(); >
Но запомните, что в данном случае поведение не определено.
Вложенные структуры
Структура сама может являться полем структуры. Пример: структура Model – модель автомобиля, имеет название, номер, год выпуска и поле Make, которое в свою очередь хранит номер марки и её название.
#include #include #include #include #define YEAR_OFFSET 1890 typedef struct Model < int id; struct < int id; char *name; >make; char *name; unsigned char year; //year is an offset to 1890 > Model; char* mallocByString(const char *str) < char* p = (char*) malloc(strlen(str) + 1); strcpy(p, str); return p; >void freeModel(Model* model) < free(model->make.name); free(model->name); > void xmlModel(Model *model) < printf( "
Вложенные структуры инициализируются как многомерные массивы. В предыдущем примере можно произвести начальную инициализацию следующим образом:
#include #include typedef struct Model < int id; struct < int id; char *name; >make; char *name; unsigned char year; //year is an offset to 1890 > Model; void main() < Model m = , "CL", 112>; printf("Model name = %s\n", m.name); printf("Make name = %s\n", m.make.name); getch(); >
P.S. подобным образом инициализировать строки не стоит, здесь так сделано только для того, чтобы упростить код.
Указатели на поля структуры и на вложенные структуры
Указатели на поля структуры определяются также, как и обычные указатели. Указатели на вложенные структуры возможны только тогда, когда структура определена. Немного переделаем предыдущий пример: «деанонимизируем» вложенную безымянную структуру и возьмём указатели на поля структуры Model:
#include #include #include #include #define YEAR_OFFSET 1890 //Отдельно выделили структуру "Марка" typedef struct Make < int id; char *name; >Make; //Теперь полем структуры "Модель" является структура "Марка" typedef struct Model < int id; Make make; char *name; unsigned char year; //year is an offset to 1890 >Model; char* mallocByString(const char *str) < char* p = (char*) malloc(strlen(str) + 1); strcpy(p, str); return p; >void freeModel(Model* model) < free(model->make.name); free(model->name); > void main() < Make *make = NULL; Model cl; int *id; cl.id = 2; cl.make.id = 1; cl.make.name = mallocByString("Acura"); cl.name = mallocByString("CL"); cl.year = (2003 - YEAR_OFFSET); //Получаем указатель на вложенную структуру make = &cl.make; //Получаем указатель на поле структуры id = &cl.id; printf("make.name = %s\n", make->name); printf("make.id = %d\n", make->id); printf("model.id = %d\n", *id); freeModel(&cl); scanf("1"); >
Как уже говорилось ранее, в си, даже если у двух структур совпадают поля, но структуры имеют разные имена, то их нельзя приводить к одному типу. Поэтому приходится избавляться от анонимных вложенных структур, если на них нужно взять указатель. Можно попытаться взять указатель типа char* на поле структуры, но нет гарантии, что поля будут расположены непрерывно.
Примеры
1. Стек, реализованный с помощью структуры «Узел», которая хранит значение (в нашем примере типа int) и указатель на следующий узел. Это неэффективная реализация, которая требует удаления и выделения памяти под узел при каждом вызове операции push и pop.
#include #include #include #define STACK_OVERFLOW 3 #define STACK_UNDERFLOW 4 /** Узел ссылается на предыдущий элемент стека. Если узел ссылается на NULL, то это последний элемент стека */ typedef struct Node < int value; struct Node *next; >Node; /** Создаём новый узел и делаем его вершиной стека. */ void push(Node *head, int val) < Node *tmp = NULL; if (!(tmp = (Node*) malloc(sizeof(Node)))) < exit(STACK_OVERFLOW); >tmp->next = head->next; tmp->value = head->value; head->value = val; head->next = tmp; > /** Возвращаем значение текущей вершиныи удаляем её */ int pop(Node **head) < Node *tmp = (*head)->next; int val = (*head)->value; if ((*head)->next == NULL) < exit(STACK_UNDERFLOW); >free(*head); (*head) = tmp; return val; > /** Удаляем все вершины */ void freeList(struct Node **head) < Node *tmp = NULL; while ((*head)->next) < tmp = (*head)->next; free(*head); (*head) = tmp; > > /** Для того, чтобы получить последний элемент, необходимо по цепочке пройти до него */ Node* getLast(struct Node *head) < Node* tmp; tmp = head ->next; while (tmp) < tmp = tmp ->next; > return tmp; > void main() < Node *head = NULL; int i; head = (Node*)malloc(sizeof(Node)); head ->next = NULL; push(head, 100); push(head, 300); printf("%d\n", pop(&head)); push(head, 200); push(head, 1000); while (head->next) < printf("%d\n", pop(&head)); >free(head); getch(); >
2. Реализуем структуру — массив, и некоторые операции для работы с массивами. Тип массива зададим макроподстановкой.
#include #include #include #define TYPE int #define OUT_OF_MEMORY_EXCEPTION 3 #define INDEX_OUT_OF_BOUNDS_EXCEPTION 4 typedef struct IntArr < TYPE *data; unsigned size; >IntArr; IntArr* create(unsigned size) < IntArr *tmp = NULL; if (!(tmp = (IntArr*) malloc(sizeof(IntArr)))) < exit(OUT_OF_MEMORY_EXCEPTION); >if (!(tmp->data = (int*) malloc(size * sizeof(TYPE)))) < exit(OUT_OF_MEMORY_EXCEPTION); >tmp->size = size; return tmp; > //Получаем элемент массива. Если индекс положительный, //то возвращаем элемент по индексу, если отрицательный, то //возвращаем по индексу с конца TYPE get(IntArr* arr, int index) < if (index < 0) < unsigned realIndex = arr->size + index; if (realIndex < arr->size) < return arr->data[realIndex]; > else < exit(INDEX_OUT_OF_BOUNDS_EXCEPTION); >> else < if (index < arr->size) < return arr->data[index]; > else < exit(INDEX_OUT_OF_BOUNDS_EXCEPTION); >> > TYPE set(IntArr* arr, int index, TYPE value) < if (index < 0) < unsigned realIndex = arr->size + index; if (realIndex < arr->size) < return (arr->data[realIndex] = value); > else < exit(INDEX_OUT_OF_BOUNDS_EXCEPTION); >> else < if (index < arr->size) < return (arr->data[index] = value); > else < exit(INDEX_OUT_OF_BOUNDS_EXCEPTION); >> > //Заполнить массив нулями void zeros(IntArr* arr) < unsigned i; for (i = 0; i < arr->size; i++) < arr->data[i] = 0; > > //Удаление элементов массива void clearArr(IntArr* arr) < free(arr->data); arr->data = NULL; > void freeArr(IntArr **arr) < clearArr(*arr); free(*arr); arr = NULL; >void resizeArr(IntArr* arr, unsigned newSize) < if (!(arr->data = (int*) realloc(arr->data, newSize * sizeof(TYPE)))) < exit(OUT_OF_MEMORY_EXCEPTION); >arr->size = newSize; > void main() < int i; IntArr *arr = create(20); for (i = 0; i < 20; i++) < set(arr, i, i*i); >resizeArr(arr, 10); for (i = 0; i < 10; i++) < printf("%d ", get(arr, i)); >printf("\nelement -3 is %d", get(arr, -3)); printf("\nelement 0 is %d", get(arr, 0)); set(arr, 0, 555); printf("\nelement 0 is %d", get(arr, 0)); freeArr(&arr); getch(); >
3. Структура Линия, состоит из двух структур точек. Для краткости реализуем только пару операций
#include #include #include #include typedef struct Point < int x; int y; >Point; typedef struct Line < Point *begin; Point *end; >Line; Line* lineByCrds(int x1, int y1, int x2, int y2) < Line *tmp = (Line*) malloc(sizeof(Line)); tmp->begin = (Point*) malloc(sizeof(Point)); tmp->end = (Point*) malloc(sizeof(Point)); tmp->begin->x = x1; tmp->begin->y = y1; tmp->end->x = x2; tmp->end->y = y2; return tmp; > /** Мы храним указатели на Point, поэтому нельзя просто скопировать p1 и p2, необходимо копировать содержимое аргументов, чтобы в случае их удаления содержимое нашей линии не исчезло */ Line* lineByPoints(const Point* p1, const Point* p2) < Line *tmp = (Line*) malloc(sizeof(Line)); tmp->begin = (Point*) malloc(sizeof(Point)); tmp->end = (Point*) malloc(sizeof(Point)); *(tmp->begin) = *p1; *(tmp->end) = *p2; return tmp; > Line* add(const Line* line1, const Line* line2) < Line *tmp = (Line*) malloc(sizeof(Line)); tmp->begin = (Point*) malloc(sizeof(Point)); tmp->end = (Point*) malloc(sizeof(Point)); tmp->begin->x = line1->begin->x; tmp->begin->y = line1->begin->y; tmp->end->x = line2->end->x; tmp->end->y = line2->end->y; return tmp; > void addTo(Line *line1, const Line* line2) < line1->end->x = line2->end->x; line1->end->y = line2->end->y; > Line* copyLine(const Line* line) < Line * tmp = (Line*) malloc(sizeof(Line)); tmp->begin = (Point*) malloc(sizeof(Point)); tmp->end = (Point*) malloc(sizeof(Point)); *(tmp->begin) = *(line->begin); *(tmp->end) = *(line->end); return tmp; > float length(const Line* line) < int x = line->end->x - line->begin->x; int y = line->end->y - line->begin->y; return sqrt((float) (x*x + y*y)); > float tangent(const Line* line) < return (float) (line->end->y - line->begin->y) / (float) (line->end->x - line->begin->x); > void freeLine(Line **line) < free((*line)->begin); free((*line)->end); free(*line); *line = NULL; > void main() < Point *p1 = (Point*) malloc(sizeof(Point)); Point *p2 = (Point*) malloc(sizeof(Point)); Line *line = NULL; Line *other = NULL; p1->x = 10; p1->y = 10; p2->x = 20; p2->y = 20; //Создадим линию из точек other = lineByPoints(p1, p2); p1->x = 3000; p1->y = 4000; //Удаляем точки. Так как в линию было скопировано //содержимое, а не указатели, то она не изменилась free(p2); free(p1); //Создали копию линии. Так как мы скопировали содержимое //полностью, то line после удаления other не изменится line = copyLine(other); freeLine(&other); printf("%d %d %d %d", line->begin->x, line->begin->y, line->end->x, line->end->y); printf("\nlength = %.3f", length(line)); printf("\ntan = %.3f", tangent(line)); freeLine(&line); getch(); >
Обратите внимание на операции создания и копирования линии. Обязательно нужно копировать содержимое, иначе при изменении или удалении объектов, которые мы получили в качестве аргументов, наша линия также изменится. Если структура содержит другие структуры в качестве полей, то необходимо проводить копирование содержимого всех полей. Глубокое копирование позволяет избежать неявных зависимостей.
4. Структура комплексное число и функции для работы с ней.
#include #include #include #include typedef struct Complex < float Re; float Im; >Complex; Complex* complex(float Re, float Im) < Complex* _this = (Complex*) malloc(sizeof(Complex)); _this->Im = Im; _this->Re = Re; return _this; > Complex* copy(const Complex *origin) < Complex* _this = complex(origin->Re, origin->Im); return _this; > void set(Complex *_this, float Re, float Im) < _this->Re = Re; _this->Im = Im; > void addTo(Complex *_this, const Complex *summand) < _this->Re += summand->Re; _this->Im += summand->Im; > void subFrom(Complex *_this, const Complex *subtrahend) < _this->Re -= subtrahend->Re; _this->Im -= subtrahend->Im; > void mulBy(Complex *_this, const Complex *factor) < float tmpRe = (_this->Re * factor->Re) - (_this->Im * factor->Im); _this->Im = (_this->Im * factor->Re) + (_this->Re * factor->Im); _this->Re = tmpRe; > void divBy(Complex *_this, const Complex *divisor) < float tmp = divisor->Im * divisor->Im + divisor->Re * divisor->Re; float tmpRe = _this->Re = (_this->Re*divisor->Re + _this->Im * divisor->Im) / tmp; _this->Im = (_this->Im*divisor->Re - _this->Re * divisor->Im) / tmp; _this->Re = tmpRe; > void deleteComplex(Complex **cmp) < free(*cmp); cmp = NULL; >void main() < Complex *a = complex(1, 3); Complex *b = complex(-1, 2); addTo(a, b); printf("a + b = %.3f, %.3fi>\n", a->Re, a->Im); set(a, 1, 3); subFrom(a, b); printf("a - b = %.3f, %.3fi>\n", a->Re, a->Im); set(a, 1, 3); mulBy(a, b); printf("a * b = %.3f, %.3fi>\n", a->Re, a->Im); set(a, 1, 3); divBy(a, b); printf("a / b = %.3f, %.3fi>", a->Re, a->Im); set(a, 1, 3); deleteComplex(&a); deleteComplex(&b); getch(); >
5. Биномиальная куча и сортировка на куче
#include #include #include #include #include #include const size_t MIN_LIMIT = 10; const size_t MIN_FACTOR = 10; /** @data - массив для хранения элементов @size - текущее количество элементов в куче @factor - значение, на которое будет увеличиваться массив в случае переполнения @limit - максимальное количество элементов в куче. При превышении будет происходить увеличение массива */ typedef struct heap < int *data; size_t size; size_t factor; size_t limit; >heap; void checkSize(heap **h)< if ((*h)->size >= (*h)->limit) < (*h)->limit += (*h)->factor; (*h)->data = (int*) realloc((*h)->data, (*h)->limit*sizeof(int)); > > int deleteRoot(struct heap *h)< unsigned int i; unsigned int c; int t; int k; int retVal = h->data[0]; t = h->data[0]; h->data[0] = h->data[h->size - 1]; h->data[h->size - 1] = t; h->size--; i = 0; while (i < h->size) < c = 2*i + 1; if (c>=h->size) break; if (c+1 < h->size) < if (h->data[c] < h->data[c+1]) < c++; >> if (h->data[c]>h->data[i]) < int temp = h->data[c]; h->data[c]=h->data[i]; h->data[i]=temp; i = c; > else < break; >> return retVal; > void insert(heap *h, int value) < unsigned int i = h->size; unsigned int p; checkSize(&h); h->data[i] = value; while (i>0) < p = (unsigned int)floor((i-1)/2.0); if (h->data[p] < h->data[i]) < int temp = h->data[i]; h->data[i] = h->data[p]; h->data[p] = temp; i = p; > else < break; >> h->size++; > heap* createHeap(size_t limit, size_t factor) < heap *tmp; tmp = (heap*)malloc(sizeof(heap)); tmp->limit = (limit>0)? limit :MIN_LIMIT; tmp->factor = (factor>0)? factor: MIN_FACTOR; tmp->data = (int*) malloc(tmp->limit * sizeof(int)); tmp->size = 0; > void sort(heap *h) < unsigned i; unsigned oldSize = h->size-1; for (i = 0; i < oldSize; i++) < h->data[oldSize-i] = deleteRoot(h); > h->size = oldSize; > void deleteHeap(heap **h) < free((*h)->data); free(*h); *h = NULL; > int main()< int i; unsigned long begin; struct heap *h; h = createHeap(2, 10); for (i = 60; i >= -10; i--) < insert(h, i); >for (i = 0; i < 71; i++) < printf("%i ", h->data[i]); > printf("\n\n"); sort(h); for (i = 0; i < 71; i++) < printf("%i ", h->data[i]); > deleteHeap(&h); getch(); >
ru-Cyrl 18- tutorial Sypachev S.S. 1989-04-14 sypachev_s_s@mail.ru Stepan Sypachev students

Всё ещё не понятно? – пиши вопросы на ящик