Структуры си: С | Структуры

Содержание

Программирование на C и C++

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

Обычно все члены структуры связаны друг с другом. Например, информация об имени и адресе, находящаяся в списке рассылки, обычно представляется в виде структуры. Следующий фрагмент кода объявляет шаблон структуры, определяющий имя и адрес. Ключевое слово struct сообщает компилятору об объявлении структуры.

struct addr {
char name[30];
char street [40]; char city[20];
char state[3];
unsigned long int zip;
};

Объявление завершается точкой с запятой, поскольку объявление структуры — это оператор. Имя структуры addr идентифицирует структуру данных и является спецификатором типа. Имя структуры часто используют как ярлык.

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

struct addr addr_info;

В данной строке происходит объявление переменной addr_info типа addr. При объявлении структуры определяется переменная смешанного типа. До тех пор, пока не будет объявлена переменная данного типа, она не будет существовать.

Когда объявлена структурная переменная, компилятор автоматически выделяет необходимый участок памяти для размещения всех ее членов. Рис. показывает размещение addr_info в памяти.

Рисунок: Размещение структуры addr_info в памяти

При объявлении структуры можно одновременно объявить одну или несколько переменных.

Например:

struct addr {
char name[30];
char street[40];
char city[20];
char state[3];
unsigned long int zip;
} addr_info; binfo, cinfo;

объявляет структуру addr и объявляет переменные addr_info, binfo, cinfo данного типа.

Важно понять, что каждая вновь создаваемая структурная переменная содержит свои собственный копии переменных, образующих структуру. Например, поле zip переменной binfo отделено от поля zip переменной cinfo. Фактически, единственная связь между binfo и cinfo заключается в том, что они обе являются экземплярами одного типа структуры. Больше между ними нет связи.

Если необходима только одна структурная переменная, то нет необходимости в ярлыке структуры. Это означает, что

struct {
char name[30];
char street[40];
char city[20];
char state[3];
unsigned long int zip;
} addr_info;

объявляет одну переменную addr_info с типом, определенным предшествующей ей структурой. Стандартный вид объявления структуры следующий:

struct ярлык {
тип имя переменной;
тип имя переменной;
тип имя переменной;
} структурные переменные;

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

Структурный тип данных. Урок 16 курса «Основы языка C»

Объявление и определение структур

Примером структуры может послужить любой объект, для которого описывается ряд его характеристик, имеющих значение в данной программе. Например, для книг это может быть название, автор, количество страниц; для окружности — координаты центра, диаметр, цвет. На языке программирования 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 <stdio.h>
#include <math.h>
 
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 <stdio.h>
 
struct circle { int x, y; float dia; };
void inversion (struct circle *);
 
int main () {   
    struct circle cir, *pc = &cir;
 
    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 <stdio.h>
 
#define N 5
 
struct computer { 
    char *type; 
    char *proc; 
    int qty; };
 
void viewer (struct computer *);
void changer (struct computer *);
 
int main () {
    struct computer comps[N]= {
    "Desktop", "earlier then P4", 10, 
    "Desktop", "P4", 30 ,
    "Desktop", "Core", 20 ,
    "Desktop", "AMD", 2 ,
    "Notebook", "Core", 1 };
 
    viewer(comps);
    changer(comps);
    viewer(comps);  
}
 
void viewer (struct computer *comp) {
    int i;
 
    for (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() связано с тем, что индексация начинается с нуля, а номера элементов массива, которые пользователь видит на экране, с единицы.
  • Для того, чтобы уменьшить количество компьютеров, при запросе надо ввести отрицательное число.

Пример результата работы программы:

Придумайте свою программу, в которой бы использовался массив структур, или перепишите приведенную выше программу и дополните ее функцией, которая позволяет добавлять в массив новый элемент-структуру.

Курс с решением части задач:
android-приложение, pdf-версия

Искусство упаковки структур в C

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

Упаковка структур в C — старая, почти забытая, но все еще актуальная тема, если вы занимаетесь низкоуровневыми приложениями.

Кому предназначается данная статья

В этой статье я рассмотрю технику, с помощью которой можно уменьшить потребление памяти в программах на C путем реорганизации объявляемых структур. Вам потребуются базовые знания языка C.

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

И, наконец, этот способ — первый шаг к некоторым магическим приемам в С. Вы не можете называть себя опытным C-программистом, если не овладели им. Вы не можете называться мастером C, пока не сможете написать подобную статью самостоятельно и грамотно ее критиковать.

Почему я написал эту статью

В 2013 мне пришлось использовать технику оптимизации, о которой я узнал более 20 лет назад и с тех пор практически не использовал. Мне понадобилось уменьшить потребление памяти программой, которая использовала тысячи, а иногда и десятки тысяч С-структур. Это был cvs-fast-export, и очень часто он рушился из-за недостатка памяти.

Существует способ значительно уменьшить потребление памяти в таких ситуациях путем грамотной реорганизации элементов структур. В моем случае программа стала потреблять на 40% меньше памяти и стала способна переработать бо́льшие репозитории без падений.

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

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

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

Выравнивание

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

На процессорах x86 и ARM примитивные типы не могут находиться в произвольной ячейке памяти. Каждый тип, кроме char, требует выравнивания. char может начинаться с любого адреса, однако двухбайтовый short должен начинаться только с четного адреса, четырехбайтный int или float — с адреса, кратного 4, восьмибайтные long или double — с адреса, кратного 8. Наличие или отсутствие знака значения не имеет. Указатели — 32-битные (4 байта) или 64-битные (8 байт) — также выравниваются.

Выравнивание ускоряет доступ к памяти за счет генерации кода, в котором на чтение и запись ячейки памяти требуется по одной инструкции. Без выравнивания мы можем столкнуться с ситуацией, когда процессору придется использовать две и более инструкции для доступа к данным, расположенным между адресами, кратными размеру машинного слова. char — особый случай, они занимают ровно одно машинное слово и всегда требуют одинакового количества инструкций для доступа. Поэтому для них нет предпочтительного выравнивания.

Я специально упомянул, что это происходит на современных процессорах, потому что на некоторых старых процессорах небезопасный код (например, приведение некратного адреса в указатель на int и его использование) не просто замедлит работу процессора, он упадет с ошибкой о невалидной инструкции. Так вел себя, например, Sun SPARK. На самом деле такое поведение можно воспроизвести на x86 с правильным флагом (e18) в детерминированной ситуации.

Иногда можно дать указание процессору не использовать выравнивание, к примеру, с помощью директивы #pragma pack. (Не делайте этого без серьезной необходимости, потому что результатом будет более ресурсоемкий и медленный код.) Обычно это позволяет сохранить почти столько же памяти, сколько и описываемый мной способ.

Единственная хорошая причина использования #pragma pack — если вы хотите, чтобы расположение данных в памяти точно соответствовало требованиям низкоуровневого протокола или оборудования, как, например, порт с прямым доступом к памяти, и нарушение соглашения необходимо для работы программы. Если вы с этим столкнулись и не знакомы с тем, что описано в этой статье, я вам сочувствую, вы действительно в трудной ситуации.

Заполнение

Давайте посмотрим на простой пример расположения переменных в памяти. Допустим, у нас есть следующие строки в C-коде:

char *p;
char c;
int x;

Если вы не знакомы с выраниванием, вы можете предположить, что эти три значения располагаются в памяти последовательно. Таким образом, на 32-битной машине за четырьмя байтами указателя сразу расположится 1 байт char, а следом за ним четыре байта целого. На 64-битной машине отличие будет в размере указателя — 8 байт вместо 4.

А вот что происходит на самом деле (на x86, ARM или другом процессоре с выравниваением данных). Память для p начинается с адреса, кратного 4. Выравнивания указателя — самое строгое.

Следом за ним идет c. Но четырехбайтный x требует заполнения (padding) пустыми байтами. Происходит примерно то же самое, как если бы мы добавили еще одну переменную:

char *p /* 4 or 8 байт */
char c /* 1 байт */
char pad[3] /* 3 байт */
int x /* 4 байт */

Массив символов pad[3] в данном случае указывает на то, что мы заполняем пространство тремя пустыми байтами. Раньше это называли «мусор» («slop»).

Если тип x будет short, который занимает 2 байта, данные будут располагаться так:

char *p /* 4 or 8 байт */
char c /* 1 байт */
char pad[1] /* 1 байт */
short x /* 2 байт */

С другой стороны, на 64-битной машине эти данные расположатся в памяти следующим образом:

char *p /* 8 байт */
char c /* 1 байт */
char pad[7] /* 7 байт */
long x /* 8 байт */

У вас наверняка уже возник вопрос: а что, если переменная с меньшим размером будет объявлена в начале:

char c;
char *p;
int x;

Если мы представим расположение структуры в памяти как:

char c;
char pad1[M];
char *p;
char pad2[N];
int x;

что мы можем сказать о M и N?

Для начала, N в данном случае будет равно 0. Адрес x гарантированно будет выравниваться по адресу указателя p, выравнивание которого, в свою очередь, более строгое.

Значение M менее предсказуемо. Если компилятор расположит c в последнем байте машинного слова, следующий за ним байт (первый байт p) будет находиться в начале следующего машинного слова. В этом случае M будет равен 0.

Более вероятно, что c расположится в первом байте машинного слова. В этом случае размер M будет таким, чтобы p был выровнен по началу следующего слова — 3 на 32-битной машине, 7 на 64-битной.

Возможны промежуточные ситуации. M может быть от 0 до 7 (от 0 до 3 на 32-битной машине), потому что char может начинаться на любом байте машинного слова.

Если вы хотите, чтобы эти переменные занимали меньше памяти, вы можете поменять местами x и c.

char *p /* 8 байт */
long x /* 8 байт */
char c /* 1 байт */

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

Прежде чем мы коснемся структур, следует упомянуть массивы скалярных величин. На платформе с выравниванием данных массивы char/short/int/long или указателей располагаются в памяти последовательно, без заполнения.

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

Выравнивание и заполнение структур

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

Также, в C адрес структуры совпадает с адресом ее первого элемента, без сдвига в начале. Осторожно: в C++ классы, которые выглядят как структуры, могут нарушать это правило. (Это зависит от того, как устроен базовый класс и реализованы виртуальные методы, и может отличаться в различных компиляторах.)

(В случае сомнений вы можете использовать макрос offsetof() из ANSI C, с помощью которого можно рассмотреть расположение элементов струтур в памяти.)

Давайте рассмотрим следующую структуру:

struct foo1 {
    char *p; /* 8 байт */
    char c; /* 1 байт */
    char pad[7]; /* 7 байт */
    long x; /* 8 байт */
};

На 64-битной машине любой экземпляр foo1 будет выравниваться по 8 байтам. Расположение в памяти идентично тому, которое мы получили бы, если бы в памяти находились скалярные величины. Однако, если мы перенесем c в начало, все поменяется:

struct foo2 {
    char c; /* 1 байт */
    char pad[7]; /* 7 байт */
    char *p; /* 8 байт */
    long x; /* 8 байт */
};

Если бы мы рассматривали отдельные переменные, c мог бы начинаться с произвольного байта и размер заполнения мог бы варьироваться. Но выравнивание структуры foo2 идет по указателю, c также должен быть выровнен по указателю. В итоге мы получаем фиксированное заполнение в 7 байт.

Рассмотрим теперь концевое заполнение структур. Общее правило такое: компилятор заполняет все место до адреса следующей структуры так, чтобы она была выровнена по самому длинному элементу структуры. sizeof() возвращает размер структуры с учетом заполнения.

Например, на 64-битном x86 процессоре или ARM:

struct foo3 {
    char *p; /* 8 байт */
    char c; /* 1 байт */
};

struct foo3 singleton;
struct foo3 quad[4];

Вы можете подумать, что sizeof(struct foo3) вернет 9, однако верным ответом будет 16. Адрес следующей структуры будет (&p)[2]. Таким образом, в массиве из 4 элементов у каждого будет заполнение в 7 пустых байт, поскольку первые элементы каждой структуры должны быть выровнены в данном случае по 8 байтам. Расположение в памяти такое, какое было бы, если бы структура была объявлена следующим образом:

struct foo3 {
    char *p; /* 8 байт */
    char c; /* 1 байт */
    char pad[7];
};

Для сравнения, рассмотрим такой пример:

struct foo4 {
    short s; /* 2 байт */
    char c; /* 1 байт */
    char pad[1];
};

Поскольку s требует 2-байтового выравнивания, следующий адрес будет отстоять от c на один байт, вся структура foo4 будет заполнена одним пустым байтом в конце и sizeof(struct foo4) вернет 4.

Выравнивание битовых полей и вложенных структур

Битовые поля позволяют объявить переменные, занимающие меньшую, чем char память, вплоть до 1 бита. Например:

struct foo5 {
    short s;
    char c;
    int flip:1;
    int nybble:4;
    int septet:7;
};

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

С точки зрения компилятора битовые поля структуры foo5 выглядят как двухбайтовые значения, из 16 бит которых используются только 12. Место после них заполняется так, чтобы размер структуры был кратен sizeof(short) — размеру наибольшего элемента.

struct foo5 {
    short s; /* 2 байт */
    char c; /* 1 байт */
    int flip:1; /* всего 1 бит */
    int nybble:4; /* всего 5 бит */
    int septet:7; /* всего 12 бит */
    int pad1:4; /* всего 16 бит = 2 байт */
    char pad2; /* 1 байт */
};

Ограничения битового поля на выход за пределы машинного слова приведет к тому, что на 32-битной машине первые две структуры поместятся в одно или два слова, но третья (foo8) займет три слова, причем у последнего будет занят только первый бит. С другой стороны, структура foo8 поместится в одно 64-битное слово.

struct foo6 {
    int bigfield:31; /* Начало первого 32-битного слова */
    int licodelefield:1;
};

struct foo7 {
    int bigfield1:31; /* Начало первого 32-битного слова */
    int licodelefield1:1;
    int bigfield2:31; /* Начало второго 32-битного слова */
    int licodelefield2:1;
};

struct foo8 {
    int bigfield1:31; /* Начало первого 32-битного слова */
    int bigfield2:31; /* Начало второго 32-битного слова */
    int licodelefield1:1;
    int licodelefield2:1; /* Начало третьего 32-битного слова */
};

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

struct foo9 {
    char c;
    
    struct foo9_inner {
        char *p;
        short x;
    } inner;
};

char *p во внутренней структуре требует выравнивания по указателю как во внутренней, так и во внешней структуре. Реальное расположение в памяти на 64-битной машине будет примерно такое:

struct foo9 {
    char c; /* 1 байт*/
    char pad1[7]; /* 7 байт */

    struct foo9_inner {
        char *p; /* 8 байт */
        short x; /* 2 байт */
        char pad2[6]; /* 6 байт */
    } inner;
};

Эта структура дает нам подсказку, где и как мы можем сэкономить память переупаковкой. Из 24 байт 13 заполняющие. Это больше 50% потерянного места!

Реорганизация структур

Теперь, когда мы знаем как и зачем компилятор выравнивает данные в памяти, посмотрим, как мы можем уменьшить количество «мусора». Это и будет называться «искусство упаковки структур».

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

Наиболее простой способ избавиться от мусора — расположить элементы структуры по уменьшению размера. Таким образом, указатели будут располагаться в начале, поскольку на 64-битной машине они займут по 8 байт. Потом 4-битовые int; 2-байтовые short; затем char.

Рассмотрим, например, односвязный список:

struct foo10 {
    char c;
    struct foo10 *p;
    short x;
};

Или, если мы явно укажем заполняющие байты:

struct foo10 {
    char c; /* 1 byte */
    char pad1[7]; /* 7 bytes */
    struct foo10 *p; /* 8 bytes */
    short x; /* 2 bytes */
    char pad2[6]; /* 6 bytes */
};

Всего 24 байта. Однако, если мы перепишем следующим образом:

struct foo11 {
    struct foo11 *p;
    short x;
    char c;
};

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

struct foo11 {
    struct foo11 *p; /* 8 bytes */
    short x; /* 2 bytes */
    char c; /* 1 byte */
    char pad[5]; /* 5 bytes */
};

Переупаовкой мы добились сокращения занимаемого места с 24 до 16 байт. Это может показаться незначительным, но что, если у вас односвязный список из 200 тысяч элементов? Эффект растет быстро, особенно на встроенных системах с ограниченым объемом памяти или в критических участках ядра ОС.

Замечу, что реорганизация не всегда позволяет сохранить память. Если мы применим этот прием к нашей структуре foo9, мы получим следующее:

struct foo12 {
    struct foo12_inner {
        char *p; /* 8 bytes */
        int x; /* 4 bytes */
    } inner;
    
    char c; /* 1 byte*/
};

Явно укажем заполнение:

struct foo12 {
    struct foo12_inner {
        char *p; /* 8 bytes */
        int x; /* 4 bytes */
        char pad[4]; /* 4 bytes */
    } inner;

    char c; /* 1 byte*/
    char pad[7]; /* 7 bytes */
};

Размер foo12 — также 24 байта, потому что c выравнивается по внутренней структуре. Для уменьшения занимаемой памяти нам придется изменить дизайн самой структуры данных.

Когда я выложил первый вариант этой статьи, меня спросили почему, если реогранизация для уменьшения «мусора» настолько проста, компилятор не делает ее автоматически. Ответ: C изначально разрабатывался для написания ОС и низкоуровневого кода. Автоматическая реорганизация структур будет мешать программисту организовать структуру с учетом требований оборудования к расположению битов и байтов в памяти.

Выравнивания перечислений и целочисленных типов

Использование перечислений («enumerated types») вместо директивы #define — отличная идея, если отладчик может их отличить от целых чисел. Перечисления гарантировано совместимы с целочисленным типом, однако стадарт C не специфицирует, с помощью какого именно типа они реализованы.

Будьте осторожны: несмотря на то, что обычно в перечислениях используется int, в некоторых компиляторах по умолчанию может использоваться short, long или char. Также в компиляторе может быть предусмотрена директива или опция для явного указания размера.

Тип long double также может создавать некоторые проблемы. Некоторые платформы реализуют его с помощью 80 бит, некоторые — 128 бит, некоторые 80-битные реализации заполняют место после данных до 96 или 128 бит.

В обоих случаях лучше использовать sizeof() для проверки занимаемого места.

Наконец, иногда на x86 Linux-машинах double может быть исключением: 8-байтный double может требовать выравнивания по 4 байтам в структуре, несмотря на то, что стоящий отдельно выравнивается по 8 байтам. Это зависит от компилятора и его опций.

Читаемость кода и локальность кэша

Несмотря на то, что реорганизация структур — самый простой способ избавиться от мусора в памяти, он не всегда будет оптимальным. Необходимо также учесть читаемость кода и локальность кэша («cache locality»).

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

Бездумная механическая реструктуризация может серьезно снизить читаемость. Лучше оставлять семантически связанные поля структур вместе. В идеале, дизайн программы должен быть взаимосвязан с дизайном структур данных.

При написании программы, которой требуется частый доступ к данным или их части, стоит помнить, что произволительность будет выше, если запрашиваемые данные помещяются в кэш — блок памяти, целиком читаемый процессором при доступе к адресу в нем. На 64-битной машине он обычно занимает 64 байта, на других платформах — обычно 32 байта.

Группировка связанных данных не только улучшает читаемость, но и способствует локализации данных в кэше. Это дополнительная причина реорганизовывать структуры с учетом особенностей доступа к данным в вашей программе.

Если в вашем коде есть конкурентный (concurrent) доступ к данным, появляется третья проблема: «cache line bouncing». Для минимизации трафика в шине следует располагать данные так, чтобы чтение из одного кэша и запись в другой производились с меньшим промежутком.

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

Другие способы упаковки

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

Это освободит вам столько памяти, что эффект от уменьшения количества промахов в кэше перевесит увеличенное время доступа к данным.

В общем случае старайтесь уменьшить размер полей с данными. В cvs-fast-export, например, я воспользовался знанием от том, что CVS и RCS репозитории не существовали до 1982 года. Я заменил 64-битное Unix-время time_t (начинающееся с 1970) на 32-битное время с начальной точкой 1982-01-01T00:00:00; это должно покрыть даты до 2118 года. (Если вы применяете подобный трюк, не забудьте проверять границы устанавливаемого значения, чтобы избежать трудноуловимых багов.)

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

Самый рискованый метод упаковки — использование union. Если вы уверены, что в вашей структуре данных поля никогда не будут использоваться совместно, можете рассмотреть этот вариант. Но будьте предельно осторожны, тщательно тестируйте свой код, потому что даже маленькая неточность может привести не только к трудноуловимым багам, но и к порче данных.

Инструменты

Компилятор clang имеет опцию -Wpadded, которая будет генерировать сообщения о заполнении.

Я слышал много хороших отзывов о программе pahole, хотя сам ее не использовал. Она работает вместе с компилятором и позволяет генерировать отчет, описывающий реальное расположение данных в памяти.

Доказательства и исключения

Исходный код небольшой программы, которая иллюстрирует вышеописанное можно скачать здесь: packtest.c.

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

Следующий уровень владения данной техникой — знать, где и когда ожидать, что правила будут нарушены. В то время, когда я изучал ее (ранние 80-е), людей, которые не освоили эту технику называли «жертвами VAX» («all-the-world’s-a-VAX syndrome»). Помните, что не все компьютеры в мире — PC.

Полезные ссылки

Здесь я привожу ссылки на статьи и эссе, которые я считаю хорошим дополнением к данному материалу.

A Guide to Undefined Behavior in C and C++
Time, Clock, and Calendar Programming In C

Источник: The Lost Art of C Structure Packing (Eric S. Raymond, [email protected])

Структуры и указатели. Онлайн учебник по языку Си

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

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

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

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

  • tab_nom — табельный номер;
  • fio — фамилия, имя, отчество;
  • pol — пол;
  • summa — зарплата;

Все эти понятия можно объединить в такую, например, структуру:

  • struct anketa
  • {
    • int tab_nom;
    • char fio[30];
    • char data[10];
    • int pol;
    • char adres[40];
    • float summa;
  • };

Эта запись называется описанием структуры. Она начинается с ключевого слова struct и состоит из заключенного в фигурные скобки списка описаний. За словом struct может следовать необязательное имя, которое называется именем типа структуры (иногда его называют тэгом или ярлыком структуры). Этот ярлык именует структуру и в дальнейшем может использоваться для сокращения подробного описания. Переменные, упоминающиеся в записи, называются элементами. Следом за правой фигурной скобкой, заканчивающей список элементов, может следовать список переменных, так же, как и в случае базисных типов. Вот почему в приведенном выше описании структуры после закрывающей фигурной скобки стоит точка с запятой; она завершает пустой список. Описание struct {….} p1, p2, p3; синтаксически аналогично int p1, p2, p3; в том смысле, что каждый из операторов описывает p1, p2, p3 как переменные соответствующего типа и приводит к выделению для них памяти. Описание же структуры без последующего списка переменных не выделяет никакой памяти. Оно только определяет форму сируктуры и действует как шаблон. Если такое описание снабжено ярлыком (именем типа), то его можно позже использовать при определении фактических экземпляров структуры. Например, используя указание выше описане anketa, можно с помощью строки

struct anketa a0, a1 ,a2;

описать структурные переменные a0, a1, a2, каждая из которых строится по шаблону, введенному структурой anketa. Любая переменная a0, a1, a2 содержит в строго определенном порядке элементы tab_nom, fio, data, pol, adres, summa. Все переменные , как и все остальные переменные языкаЮ получают места в памяти.

Внешние и статические структуры можно инициировать, помещая следом за определением список начальных значений элементов:

struct anketa a0={ 1024, «Макагон В.М», «10.01.1943»,0 , «Одесса, Варневская, 23/99», 175.00};

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

имя_структуры.имя_елемента

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

  • a0.data=»10.01.43″;
  • a1.summa=0.0;
  • if(a2.pol==1) man=man+1;

Действия над структурами, в общем, ограничены. Все, что можно делать со структурой, — это взять ее адресс с помощью операции & и обращаться к ее элементам, как показано выше. Записи нельзя копировать или присваивать как единое целое; их нельзя передавать в функцию или получить оттуда целиком. Однако, к указателям на структуры это замечание не относится.

На практике структурные переменные обычно появляются в виде массива или списка. Нетрудно видеть, что наши три переменные a0, a1, a2 будет проще использовать, если их объединить в массив, сосотоящий из элементов типа struct anketa. Применив в программе описание

struct anketa a[3];

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

  • a[0].fio =»Макагон В.М»;
  • if(a[i].tab_nom>a[i+1].tab_nom)
  • {
    • p=a[i].tab_nom;
    • a[i].tab_nom=a[i+1].tab_nom;
    • a[i+1].tab_nom=p;
  • }

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

struct anketa *uk;

говорит, что uk — указатель на структуру типа anketa. Обозначение относится к конкретному элементу структуры и означает выборку этого элемента, например: uk-> tab_nom. Поскольку uk есть указатель на структуру anketa, то к элементу tab_nom можно обращаться и так:

(*uk).tab_nom;

если учесть, что указатель установлен на начало массива структур. Имя массива, как обычно, эквивалентно адресу его начального элемента и при добавлении к указателю на структуру или вычитании из него целого числа размер структуры учитывается автоматически Так, оператор uk=a; устанавливает указатель на первый экземпляр массива структур, а запись ++a; обеспечивает автоматический переход к следующему экземпляру. В выражении (*uk).fio скобки обязательны, так как приоритет операции выделения элемента » . » выше чем у «*».

Урок 23. Паттерн 15. Рост размеров структур

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

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

Рассмотрим следующую структуру:

struct MyStruct
{
  bool m_bool;
  char *m_pointer;
  int m_int;
};

На 32-битной системе эта структура займет 12 байт, и сократить этот размер не представляется возможным. Каждое поле выровнено по границе 4 байта. Даже если m_bool перенести в конец, это ничего не изменит. Компилятор все равно сделает размер структуры кратным 4 байтам для выравнивания таких структур в массивах.

В случае 64-битной сборки структура MyStruct займет уже 24 байта. Это понятно. В начале идет один байт под m_bool и 7 неиспользуемых байт для выравнивания, так как указатель занимает 8 байт и должен быть выровнен по границе 8 байт. Затем 4 байта для m_int и 4 неиспользуемых байта, для выравнивания структуры по границе 8 байт.

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

struct MyStructOpt
{
  char *m_pointer;
  int m_int;
  bool m_bool;
};

Структура MyStructOpt займет уже не 24, а 16 байт. Визуально расположение полей представлено на рисунке 1. Весьма существенная экономия, если мы будем использовать, например, 10 миллионов элементов. В этом случае мы сэкономим 80 мегабайт памяти, но что еще более важно, можем повысить производительность. Если структур будет немного, то нет разницы, какого они размера. Доступ будет происходить с одинаковой скоростью. Но когда элементов много, то начинает играть роль кэш, количество обращений к памяти и так далее. И можно с уверенностью утверждать, что обработка 160 мегабайт данных займет меньше времени, чем 240 мегабайт. Даже простой доступ ко всем элементам массива для чтения, уже будет более быстр.

Рисунок 1 — Расположение полей в структурах MyStruct и MyStructOpt

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

Вы, наверное, зададите вопрос, по каким правилам компилятор выравнивает данные. Мы ответим кратко, а если интересно познакомиться с этой темой более подробно, то мы отсылаем вас к книге Джеффри Рихтер — Создание эффективных WIN32-приложений с учетом специфики 64-разрядной версии Windows. Там этот вопрос рассматривается достаточно подробно.

В целом правило выравнивание следующее: каждое поле выравнивается по адресу, кратному размеру данного поля. Поле типа size_t на 64-битной системе будет выровнено по границе 8 байт, int по границе 4 байта, short по границе 2 байта. Поля типа char не выравниваются. Размер структуры выравнивается до размера, кратному размеру его максимального элемента. Поясним это выравнивание на примере:

struct ABCD
 {
  size_t m_a;
  char m_b;
 };

Элементы займут 8 + 1 = 9 байт. Но если размер структуры будет 9 байт, то, если мы захотим создать массив структур ABCD[2], поле m_a второй структуры будет лежать по не выровненному адресу. Вследствие этого компилятор дополнит структуру 7 пустыми байтами до размера 16 байт.

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

struct MyStruct
{
  int m_int;
  size_t m_size_t;
  short m_short;
  void *m_ptr;
  char m_char;
};

и простой сортировкой последовательности полей по убыванию размера:

struct MyStructOpt
{
  void *m_ptr;
  size_t m_size_t;
  int m_int;
  short m_short;
  char m_char;
};

мы сделаем из нее структуру размером всего 24 байт.

Диагностика

Инструмент PVS-Studio позволяет обнаружить структуры в коде 64-битных приложений, перестановка полей в которых, позволит сократить их размер. На неоптимальные структуры анализатор выдает диагностическое сообщение V802.

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

class MyWindow : public CWnd {
  bool m_isActive;
  size_t m_sizeX, m_ sizeY;
  char m_color[3];
  ...
};

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

Авторы курса: Андрей Карпов ([email protected]), Евгений Рыжков ([email protected]).

Правообладателем курса «Уроки разработки 64-битных приложений на языке Си/Си++» является ООО «Системы программной верификации». Компания занимается разработкой программного обеспечения в области анализа исходного кода программ. Сайт компании: http://www.viva64.com.

Сортировка массива структур — задача #9

#include <stdio.h>

#include <Windows.h>

#include <conio.h>

 

// количество элементов в массиве структур

#define N 6

 

// структура, описывающая студента. содержит фамилию и массив из трех оценок

struct Student

{

    char lastName[25];

    int marks[3];

};

 

// функция возвращает сумму оценок студента по структуре

int getMarksSum(Student student)

{

    return student.marks[0] + student.marks[1] + student.marks[2];

}

 

// функция сортирует массив структур по возрастанию суммы оценок студента

void sortAndPrint(Student students[])

{

    // сортировка пузырьком

    Student tmp;

 

    for (int i = N — 1; i >= 0; i—)

    {

        for (int j = 0; j < i; j++)

        {

            // сравниваем элементы массива структур по сумме баллов студента

            if (getMarksSum(students[j]) > getMarksSum(students[j + 1]))

            {

                tmp = students[j];

                students[j] = students[j + 1];

                students[j + 1] = tmp;

            }

        }

    }

 

    // в цикле выводим в консоль отсортированный массив структур

    for (int i = 0; i < N; i++)

    {

        printf(«%s %d %d %d sum = %d\n», students[i].lastName, students[i].marks[0], students[i].marks[1], students[i].marks[2], getMarksSum(students[i]));

    }

}

 

 

int main()

{

    // включаем русский язык в консоли Си

    SetConsoleCP(1251); // установка кодовой страницы win-cp 1251 в поток ввода

    SetConsoleOutputCP(1251); // установка кодовой страницы win-cp 1251 в поток вывода

 

    // создаем массив структур из N элементов

    Student students[N];

 

    // читаем данные для массива из консоли

    for (int i = 0; i < N; i++)

    {

        printf(«Input the last name[%d]: «, i + 1);

        gets_s(students[i].lastName);

 

        printf(«Input his mark[1]: «);

        scanf_s(«%d», &students[i].marks[0]);

        printf(«Input his mark[2]: «);

        scanf_s(«%d», &students[i].marks[1]);

        printf(«Input his mark[3]: «);

        scanf_s(«%d», &students[i].marks[2]);

 

        getchar();

    }

 

    printf(«\n»);

 

    // сортируем массив структур и выводим его в консоль

    sortAndPrint(students);

}

1.2.7. Структуры | Электроника для всех

Содержание
Возврат к предыдущей части 1.2.6 Массивы

Cтруктуры — это составной объект, в который входят элементы любых типов, за исключением функций. В отличие от массива, который является однородным объектом, структура может быть неоднородной. Тип структуры определяется записью вида:

1
struct { список определений }

struct { список определений }

В структуре обязательно должен быть указан хотя бы один компонент. Определение структур имеет следующий вид:

тип-данных описатель;

где тип-данных указывает тип структуры для объектов, определяемых в описателях. В простейшей форме описатели представляют собой идентификаторы или массивы.

Пример:

1
2
3
            struct { double x,y; } s1, s2, sm[9];
            struct {  int   year;
                      char  moth, day; } date1, date2;

struct { double x,y; } s1, s2, sm[9];
struct { int year;
char moth, day; } date1, date2;

Переменные s1, s2 определяются как структуры, каждая из которых состоит из двух компонент х и у. Переменная sm определяется как массив из девяти структур. Каждая из двух переменных date1, date2 состоит из трех компонентов year, moth, day. Существует и другой способ ассоциирования имени с типом структуры, он основан на использовании тега структуры. Тег структуры аналогичен тегу перечислимого типа. Тег структуры определяется следующим образом:

1
struct тег { список описаний; };

struct тег { список описаний; };

где тег является идентификатором.

В приведенном ниже примере идентификатор student описывается как тег структуры:

1
2
3
     struct student { char name[25];
                      int  id, age;
                      char  prp;         };

struct student { char name[25];
int id, age;
char prp; };

Тег структуры используется для последующего объявления структур данного вида в форме:

struct тег список-идентификаторов;

Пример:

1
struct studeut st1,st2;

struct studeut st1,st2;

Использование тегов структуры необходимо для описания рекурсивных структур. Ниже рассматривается использование рекурсивных тегов структуры.

1
2
        struct node { int data;
                      struct node * next; }   st1_node;

struct node { int data;
struct node * next; } st1_node;

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

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

1
2
3
          st1.name="Иванов";
          st2.id=st1.id;
          st1_node.data=st1.age;

st1.name=»Иванов»;
st2.id=st1.id;
st1_node.data=st1.age;

Читать далее. Раздел 1.2.8 Обьединения. Union.
Содержание

Динамика конструкций в единицах СИ — Анил К. Чопра — 9781292249186 — Гражданское и экологическое проектирование

Описание

Для курсов по структурной динамике.

Структурная динамика и инженерия землетрясений для студентов и профессиональных инженеров

Эксперт в области структурной динамики и сейсмологической инженерии, Анил К. Чопра заполняет важную нишу, объясняя материал в манере, подходящей как для студентов, так и для профессионалов. инженеры с его пятым изданием в единицах СИ Динамика конструкций: теория и применение в сейсмостойкой инженерии .Не предполагается никаких предварительных знаний о структурной динамике, а презентация достаточно подробна и интегрирована, чтобы сделать текст пригодным для самостоятельного изучения.

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

Характеристики

Обновления, улучшения и расширенные разделы в пятом издании в единицах СИ.

  • НОВИНКА! В главу 20 были добавлены два новых раздела, которые теперь более чем в два раза превышают предыдущий размер. Эти дополнения были мотивированы все более широким использованием нелинейного анализа истории отклика (RHA) зданий в профессиональной практике.
  • ОБНОВЛЕНО! Обновления и исправления отдельных глав включают:
    • Глава 13 была расширена за счет включения метода на основе спектра реакции для оценки основных напряжений в сплошных конструкциях, таких как бетонные плотины.
    • Главы 7, 9 и 18 были расширены, чтобы включить P-анализ эффектов гравитационной нагрузки и введение в их влияние на поперечный отклик, остаточную деформацию и обрушение конструкций.
    • Глава 11 была пересмотрена и теперь включает последние данные о коэффициентах демпфирования, определенных путем системно-идентификационного анализа движений 203 зданий, зарегистрированных во время землетрясений.Представлены новые рекомендации по оценке коэффициентов демпфирования, которые будут использоваться при сейсмических расчетах конструкций.
    • Главы 22 и 23 были обновлены, чтобы отразить текущие редакции строительных норм для проектирования новых зданий, а также руководящие принципы и стандарты оценки существующих зданий, основанные на характеристиках.
  • НОВИНКА! Добавлено много новых рисунков, несколько проработанных примеров и задачи конца главы .

Педагогические особенности Расширенное обучение

  • Включен раздел о применении спектра неупругого проектирования к проектированию конструкций.
  • Включены примеры динамики мостов и их реакции на землетрясения .
  • В текст включены три строительных норм и правил: и Еврокод.
  • Теория динамического отклика конструкций представлена ​​таким образом, чтобы подчеркнуть физическое понимание аналитических процедур.
  • Теория структурной динамики применяется для проведения параметрических исследований, которые выявляют несколько фундаментальных проблем, связанных с реакцией на землетрясения и проектированием многоэтажных зданий.
  • Более 100 проработанных примеров иллюстрируют аналитические процедуры.
  • В текст включено более 400 рисунков , тщательно разработанных и выполненных для обеспечения педагогической эффективности.
  • Фотографии конструкций и их отклики , записанные во время землетрясений, включены, чтобы связать презентацию с реальным миром.

Новое в этом выпуске

Обновления, улучшения и расширенные разделы в пятом издании в единицах СИ.

  • В главу 20 были добавлены два новых раздела, которые теперь более чем в два раза превышают предыдущий размер. Эти дополнения были мотивированы все более широким использованием нелинейного анализа истории отклика (RHA) зданий в профессиональной практике.
  • Обновления и изменения отдельных глав включают:
    • Глава 13 была расширена и теперь включает метод на основе спектра реакции для оценки основных напряжений в непрерывных конструкциях, таких как бетонные плотины.
    • Главы 7, 9 и 18 были расширены, чтобы включить P-анализ эффектов гравитационной нагрузки и введение в их влияние на поперечный отклик, остаточную деформацию и обрушение конструкций.
    • Глава 11 была пересмотрена и теперь включает последние данные о коэффициентах демпфирования, определенных путем системно-идентификационного анализа движений 203 зданий, зарегистрированных во время землетрясений. Представлены новые рекомендации по оценке коэффициентов демпфирования, которые будут использоваться при сейсмических расчетах конструкций.
    • Главы 22 и 23 были обновлены, чтобы отразить текущие редакции строительных норм для проектирования новых зданий, а также руководящие принципы и стандарты оценки существующих зданий, основанные на характеристиках.
  • Добавлено много новых рисунков, несколько проработанных примеров и задачи конца главы .

Содержание

I. Системы с одной степенью свободы

1. Уравнения движения, постановка задачи и методы решения

2.Свободная вибрация

3. Реакция на гармонические и периодические возбуждения

4. Реакция на произвольные, ступенчатые и импульсные возбуждения

5. Численная оценка динамического отклика

6. Реакция линейных систем на землетрясение

7. Реакция на землетрясение Неэластичные системы

8. Обобщенные системы с одной степенью свободы

II. Системы с несколькими степенями свободы

9. Уравнения движения, постановка задачи и методы решения

10.Свободная вибрация

11. Демпфирование в конструкциях

12. Динамический анализ и реакция линейных систем

13. Анализ землетрясений линейных систем

14. Анализ неклассически затухающих линейных систем

15. Снижение степеней свободы

16. Численная оценка динамического отклика

17. Системы с распределенной массой и упругостью

18. Введение в метод конечных элементов

III.Реакция на землетрясение, проектирование и оценка многоэтажных зданий

19. Реакция на землетрясение линейно-упругих зданий

20. Анализ землетрясений и реакция неупругих зданий

21. Динамика землетрясений в зданиях с изоляцией от фундамента

22. Динамика конструкций в строительных нормах

23. Структурная динамика в руководящих принципах оценки зданий

КОНСТРУКЦИИ: СИСТЕМЫ СИ. 2-ОЕ ИЗДАНИЕ

В этой книге рассматривается использование матриц в структурном анализе и основное внимание уделяется каркасным конструкциям.Во втором издании был сохранен весь исходный материал, за исключением некоторого вступительного текста, который был удален. Глава, посвященная гибкости, была в значительной степени переписана, а в главу, посвященную элементам сжатия, были добавлены краткие разделы, посвященные пластинам жесткости и функциям устойчивости. Пятнадцать глав посвящены: 1, введению; 2 — анализ статически детерминированных структур; 3, простые системы стресса; 4 — деформация балок; 5 — деформация конструкций; 6 — статически детерминированные конструкции; 7, метод гибкости; 8, метод жесткости; 9 — последовательные приближения; 10, энергетические теоремы; 11, использование моделей в структурном анализе; 12, элементы сжатия и проблемы устойчивости; 13, оболочечные конструкции; 14, предельный анализ плоских кадров; 15 — арка и подвесной мост.(TRRL)

  • Наличие:
  • Корпоративных авторов:

    Pitman Books Limited

    128 Long Acre
    Лондон,
    Англия
  • Авторов:

    • Маршалл, W T
    • Нельсон, H M
  • Дата публикации: 1977

Информация для СМИ

Предмет / указатель терминов

Информация для подачи

  • Регистрационный номер: 00386051
  • Тип записи:
    Публикация
  • Агентство-источник: Транспортная исследовательская лаборатория
  • ISBN: 0 273 00827 7
  • Номер отчета / статьи: Монография
  • Файлы: ITRD, TRIS
  • Дата создания:
    30 июля 1984 г., 00:00

Матричный анализ конструкций, издание SI — 9780357448304

1.ВСТУПЛЕНИЕ.
Историческая справка. Классический, матричный и конечно-элементный методы структурного анализа. Методы гибкости и жесткости. Классификация каркасных конструкций. Аналитические модели. Фундаментальные отношения для структурного анализа. Сравнение линейного и нелинейного анализа. Программное обеспечение. Резюме.
2. МАТРИЧНАЯ АЛГЕБРА.
Определение матрицы. Типы матриц. Матричные операции. Метод исключения Гаусса-Жордана. Резюме. Проблемы.
3. ПЛОСКИЕ ФЕРМЫ.
Глобальные и локальные системы координат.Степени свободы. Отношения жесткости элементов в локальной системе координат. Построение конечных элементов с использованием виртуальной работы. Преобразования координат. Отношения жесткости элементов в глобальной системе координат. Отношения жесткости конструкции. Процедура анализа. Резюме. Проблемы.
4. КОМПЬЮТЕРНАЯ ПРОГРАММА ДЛЯ АНАЛИЗА ПЛОСКИХ ФЕРМ.
Ввод данных. Присвоение координатных номеров конструкции. Построение матрицы жесткости конструкции. Формирование вектора совместной нагрузки. Решение для суставных смещений.Расчет сил стержней и опорных реакций. Резюме. Проблемы.
5. БАЛКИ.
Аналитическая модель. Отношения жесткости члена. Построение конечных элементов с использованием виртуальной работы. Постоянные силы элемента из-за нагрузок. Отношения жесткости конструкции. Структурируйте фиксированные шарнирные силы и эквивалентные шарнирные нагрузки. Процедура анализа. Компьютерные программы. Резюме. Проблемы.
6. САМОЛЕТНАЯ РАМА.
Аналитическая модель. Отношения жесткости элементов в локальной системе координат. Преобразования координат.Отношения жесткости элементов в глобальной системе координат. Отношения жесткости конструкции. Процедура анализа. Компьютерная программа. Резюме. Проблемы.
7. ВЫПУСК ЧЛЕНОВ И ДОПОЛНИТЕЛЬНЫЕ ДЕЙСТВИЯ.
Выступы стержней в плоских каркасах и балках. Компьютерная реализация анализа релизов участников. Поддержка смещения. Компьютерная реализация эффектов смещения опоры. Изменения температуры и ошибки изготовления. Резюме. Проблемы.
8. ТРЕХМЕРНЫЕ КАРКАСНЫЕ КОНСТРУКЦИИ.
Космические фермы.Сетки. Космические рамки. Резюме. Проблемы.
9. СПЕЦИАЛЬНЫЕ ТЕМЫ И ПРИЕМЫ МОДЕЛИРОВАНИЯ.
Матрица жесткости конструкции, включая ограниченные координаты — альтернативная формулировка метода жесткости. Приближенный матричный анализ прямоугольных каркасов зданий. Конденсация степеней свободы и субструктурирование. Наклонные роликовые опоры. Офсетные соединения. Полужесткие связи. Деформации сдвига. Непризматические элементы. Решение больших систем уравнений жесткости. Резюме. Проблемы.
10. ВВЕДЕНИЕ В НЕЛИНЕЙНЫЙ СТРУКТУРНЫЙ АНАЛИЗ.
Основные понятия геометрически нелинейного анализа. Геометрически нелинейный анализ плоских ферм. Резюме. Проблемы.

Аслам Кассимали

Университет Южного Иллинойса, Карбондейл

Аслам Кассимали — профессор и выдающийся преподаватель кафедры гражданской и экологической инженерии Университета Южного Иллинойса, где он преподает линейный и нелинейный структурный анализ, а также структурную динамику и устойчивость.Доктор Кассимали родился в Карачи, Пакистан, где получил степень бакалавра искусств. в области гражданского строительства из Университета Карачи. Он получил степень магистра гражданского строительства в Университете штата Айова. После завершения дальнейших исследований и исследований в Университете Миссури в Колумбии он получил степень магистра наук. и к.т.н. в гражданском строительстве.
Практический опыт доктора Кассимали включает работу инженером-проектировщиком в Lutz, Daily and Brain, инженерами-консультантами в Shawnee Mission, штат Канзас, а также работу специалистом по проектированию конструкций и аналитиком в Sargent & Lundy Engineers в Чикаго, штат Иллинойс.Он поступил на работу в Университет Южного Иллинойса — Карбондейл в качестве доцента и был повышен до звания профессора в 1993 году. Постоянно признанный за высокое качество преподавания, доктор Кассимали получил более 20 наград за выдающееся преподавание в Университете Южного Иллинойса в Карбондейле и удостоен звания выдающегося учителя в 2004 году. Доктор Кассимали является автором или соавтором четырех учебников по структурному анализу и механике и опубликовал ряд статей в области нелинейного структурного анализа.Он является пожизненным членом Американского общества инженеров-строителей (ASCE) и входил в состав комитетов структурного подразделения ASCE по ударным и вибрационным воздействиям, специальным конструкциям и методам анализа.

Матричный анализ конструкций Версия SI — 9781111426224

1. ВВЕДЕНИЕ.
Историческая справка. Классический, матричный и конечно-элементный методы структурного анализа. Методы гибкости и жесткости. Классификация каркасных конструкций. Аналитические модели. Фундаментальные отношения для структурного анализа.Сравнение линейного и нелинейного анализа. Программное обеспечение. Резюме.
2. МАТРИЧНАЯ АЛГЕБРА.
Определение матрицы. Типы матриц. Матричные операции. Метод исключения Гаусса-Жордана. Резюме. Проблемы.
3. ПЛОСКИЕ ФЕРМЫ.
Глобальные и локальные системы координат. Степени свободы. Отношения жесткости элементов в локальной системе координат. Построение конечных элементов с использованием виртуальной работы. Преобразования координат. Отношения жесткости элементов в глобальной системе координат. Отношения жесткости конструкции.Процедура анализа. Резюме. Проблемы.
4. КОМПЬЮТЕРНАЯ ПРОГРАММА ДЛЯ АНАЛИЗА ГЛАВНЫХ ФЕРМ.
Ввод данных. Присвоение координатных номеров конструкции. Построение матрицы жесткости конструкции. Формирование вектора совместной нагрузки. Решение для суставных смещений. Расчет сил стержней и опорных реакций. Резюме. Проблемы.
5. БАЛКИ.
Аналитическая модель. Отношения жесткости члена. Формирование методом конечных элементов с использованием виртуальной работы. Постоянные силы элемента из-за нагрузок. Отношения жесткости конструкции.Структурируйте фиксированные шарнирные силы и эквивалентные шарнирные нагрузки. Процедура анализа. Компьютерные программы. Резюме. Проблемы.
6. САМОЛЕТНАЯ РАМА.
Аналитическая модель / Отношения жесткости элементов в локальной системе координат / Преобразования координат / Отношения жесткости элементов в глобальной системе координат / Отношения жесткости структур / Процедура анализа / Компьютерная программа / Резюме / Проблемы
7. ВЫПУСКНИКИ И ВТОРИЧНЫЕ ЭФФЕКТЫ УЧАСТНИКОВ.
Выступы стержней в плоских каркасах и балках.Компьютерная реализация анализа релизов участников. Поддержка смещения. Компьютерная реализация эффектов смещения опоры. Изменения температуры и ошибки изготовления. Резюме. Проблемы.
8. ТРЕХМЕРНЫЕ КАРКАСНЫЕ КОНСТРУКЦИИ.
Космические фермы. Сетки. Космические рамки. Резюме. Проблемы.
9. СПЕЦИАЛЬНЫЕ ТЕМЫ И ПРИЕМЫ МОДЕЛИРОВАНИЯ.
Матрица жесткости конструкции, включая ограниченные координаты — альтернативная формулировка метода жесткости. Приближенный матричный анализ прямоугольных каркасов зданий.Кондан степеней свободы и субструктурирования. Наклонные роликовые опоры. Офсетные соединения. Полужесткие связи. Деформации сдвига. Непризматические элементы. Решение больших систем уравнений жесткости. Резюме. Проблемы.
10. ВВЕДЕНИЕ В НЕЛИНЕЙНЫЙ СТРУКТУРНЫЙ АНАЛИЗ.
Основные понятия геометрически нелинейного анализа. Геометрически нелинейный анализ плоских ферм. Резюме. Проблемы.

Аслам Кассимали

Университет Южного Иллинойса, Карбондейл

Аслам Кассимали — профессор и выдающийся преподаватель кафедры гражданской и экологической инженерии Университета Южного Иллинойса, где он преподает линейный и нелинейный структурный анализ, а также структурную динамику и устойчивость.Доктор Кассимали родился в Карачи, Пакистан, где получил степень бакалавра искусств. в области гражданского строительства из Университета Карачи. Он получил степень магистра гражданского строительства в Университете штата Айова. После завершения дальнейших исследований и исследований в Университете Миссури в Колумбии он получил степень магистра наук. и к.т.н. в гражданском строительстве.
Практический опыт доктора Кассимали включает работу инженером-проектировщиком в Lutz, Daily and Brain, инженерами-консультантами в Shawnee Mission, штат Канзас, а также работу специалистом по проектированию конструкций и аналитиком в Sargent & Lundy Engineers в Чикаго, штат Иллинойс.Он поступил на работу в Университет Южного Иллинойса — Карбондейл в качестве доцента и был повышен до звания профессора в 1993 году. Постоянно признанный за высокое качество преподавания, доктор Кассимали получил более 20 наград за выдающееся преподавание в Университете Южного Иллинойса в Карбондейле и удостоен звания выдающегося учителя в 2004 году. Доктор Кассимали является автором или соавтором четырех учебников по структурному анализу и механике и опубликовал ряд статей в области нелинейного структурного анализа.Он является пожизненным членом Американского общества инженеров-строителей (ASCE) и входил в состав комитетов структурного подразделения ASCE по ударным и вибрационным воздействиям, специальным конструкциям и методам анализа.

Иерархические структуры пористого кремния с необычайной механической прочностью в качестве анодов высокоэффективных литий-ионных аккумуляторов

Структурная конструкция иерархических микросфер CNT @ Si @ C

На рисунке 1 показана схема процесса синтеза (рис. 1a) и структурные характеристики ключа промежуточные продукты CNT @ SiO 2 (рис.1b – e) и конечные иерархические пористые микросферы CNT @ Si @ C с углеродным покрытием (рис. 1f – i). Микросферы CNT @ SiO 2 (рис. 1b) были приготовлены эмульсией коаксиальных кабелей CNT @ SiO 2 ядро-оболочка (рис. 1c), которые были приготовлены золь-гель методом 48 . Слой SiO 2 толщиной ~ 10-20 нм был равномерно нанесен на поверхность функционализированных высококачественных УНТ (дополнительный рис. 1, внешний диаметр 20-40 нм) и сформированы коаксиальные кабели ядро-оболочка толщиной ~ 60-80 нм. (Рисунок.1c и дополнительный рис. 2). Сканирующая просвечивающая электронная микроскопия (STEM) и энергодисперсионная рентгеновская спектроскопия (EDS) с линейным сканированием (рис. 1d – e) подтвердили однородность покрытия при незначительном изменении толщины. Покрытие SiO 2 было разработано путем регулирования процентного содержания тетраэтоксисилана (TEOS) таким образом, чтобы конечные аноды CNT @ Si @ C имели заданную емкость ~ 1000 мАч г -1 в зависимости от общего веса электрода (см. Рис. 3 и детали конструкции в дополнительных материалах).

Рис. 1: Схема процесса синтеза и структурные характеристики основных промежуточных продуктов CNT @ SiO 2 и конечных микросфер CNT @ Si @ C.

a Схема синтеза микросфер CNT @ Si @ C. b Типичное СЭМ-изображение CNT @ SiO 2 микросфер (масштабная линейка = 5 мкм). c Типичное изображение ПЭМ коаксиального кабеля CNT @ SiO 2 ядро-оболочка (шкала = 5 мкм). d СТЭМ-изображение CNT @ SiO 2 кабелей (масштабная полоса = 20 мкм). e Линейное сканирование EDS кабеля CNT @ SiO 2 с маркировкой d . Небольшое расхождение Si и O из-за лучевого эффекта от длительного сканирования. f Типичное СЭМ-изображение микросфер CNT @ Si @ C (масштабная линейка = 3 мкм). г ПЭМ-изображение с малым увеличением композитного кабеля из микросферы CNT @ Si @ C (масштабная полоса = 20 мкм), ч ВРЭМ-изображение кабеля в г (масштабная полоса = 5 мкм). i EDS отображение отмеченной области в г (шкала = 50 мкм).

Благодаря уникальной конструкции микросферы CNT @ SiO 2 имеют равномерно распределенные Si, кислород и углерод (дополнительный рис. 4), даже несмотря на то, что диаметр частицы составляет ~ 6–8 мкм (дополнительный рис. 5). ). Кроме того, микросферы CNT @ Si, полученные из микросфер CNT @ SiO 2 путем алюминотермического восстановления, сохраняют сферическую морфологию и аналогичный размер (дополнительный рис. 6). Si и углерод также равномерно распределены в микросферах CNT @ Si (дополнительный рис.6). Образование Si было подтверждено дифракцией рентгеновских лучей (XRD) (дополнительный рис. 7) и дальнейшими характеристиками конечного продукта CNT @ Si @ C (рис. 1f – i). Никакой сигнал, связанный с Al, не обнаруживается с помощью рентгеновской фотоэлектронной спектроскопии (XPS) для CNT @ Si, что отражает полное удаление Al во время стадий промывки (дополнительный рис. 8).

CNT @ Si была покрыта слоем углерода с использованием метода химического осаждения из паровой фазы (CVD), чтобы помочь предотвратить окисление нано-Si на воздухе и контролировать площадь поверхности и, следовательно, электрохимические характеристики (см. Ниже).Углерод CVD, хотя и не такой прочный, как уголь из пека 49 , имеет преимущество в легком заполнении пор и формировании тонкого и равномерного углеродного покрытия на максимально возможной поверхности пористого Si. CNT @ Si @ C имеет уменьшенную площадь поверхности и пористость (дополнительные рисунки 9–11 и дополнительная таблица 1) и, таким образом, приводит к уменьшению паразитных реакций разложения электролита и улучшенным электрохимическим характеристикам. Плотность утряски УНТ @ Si @ C составляет 0,5 г куб.см -1 , что более чем в три раза превышает плотность утряски порошка нано-Si (~ 0.15 г куб.см -1 ) 19 . Анализ XPS (дополнительный рис. 12) показал ~ 6–8 мас.% Кислорода в CNT @ Si и CNT @ Si @ C, что аналогично результатам измерения EDS (дополнительный рис. 13, 7,3–8,5 мас.%). Дополнительный рис. 14 представляет собой спектры комбинационного рассеяния света, показывающие, что высококачественный углерод с отношением G / D, подобным таковому у CNT, был пропитан в поры иерархической пористой CNT @ Si и нанесен на поверхность. Картирование ПЭМ и ЭДС показывает подробную структуру композита CNT @ Si @ C (рис. 1g – i). Рисунок 1 и дополнительный рисунок.15 показывают, что на УНТ были декорированы частицы Si различных размеров до десятков нанометров. Анализ XRD показывает, что Si имеет средний размер кристаллитов ~ 30-40 нм, что согласуется с гистограммой распределения из ПЭМ (дополнительный рис. 16). ПЭМ высокого разрешения (рис. 1h) выявляет кристаллическую структуру некоторых крупных наночастиц с полосами решетки 3,1 Å, соответствующими плоскости Si (111), в то время как на карте виден аморфный Si или мелкие кластеры. Слой углерода толщиной ~ 4 нм равномерно покрывает наночастицы Si (рис.1h – i и дополнительный рис. 15). Исследование инфракрасной спектроскопии с преобразованием Фурье (дополнительный рис. 17) показывает химическое связывание между Si, CNT или углеродным покрытием CVD. Улучшенная механическая или электрическая целостность обусловлена ​​химической связью между УНТ, Si и C, а также физическим прикреплением и эффектом углеродного покрытия CVD.

Пористая структура необходима для компенсации объемного расширения Si, однако она должна быть хорошо адаптирована для сохранения достаточной объемной плотности емкости и высокой механической прочности, которые могут выдерживать каландрирование и другие процедуры сборки батареи.Эволюция пористой структуры от микросферы CNT @ SiO 2 до CNT @ Si и CNT @ Si @ C контролировалась с помощью сканирующей электронной микроскопии (SEM) поперечного сечения и газового поглощения (рис. 2 и дополнительные рисунки 9–11). Исследование SEM показало улучшенное распределение пор и пористую структуру для CNT @ Si по сравнению с CNT @ SiO 2 . Поглощение газа показало увеличение площади поверхности, объема пор и среднего диаметра пор от CNT @ SiO 2 до CNT @ Si, тогда как эти параметры уменьшаются для CNT @ Si @ C (дополнительная таблица 1).Конечные микросферы CNT @ Si с углеродным покрытием, CNT @ Si @ C, имеют площадь поверхности (~ 60 м 2 г -1 ), меньшую, чем у многих материалов нано-Si, но при этом имеют средний диаметр пор 20 нм, чтобы приспособиться к расширению наночастиц Si, закрепленных на УНТ.

Рис. 2: Характеристики пористой структуры CNT @ Si @ C, набухание частиц и электрохимические характеристики.

a Поперечное сечение типичной микросферы CNT @ Si @ C (шкала = 1 мкм). b Распределение диаметров пор УНТ @ Si @ C в результате абсорбции газа. c e In-situ ПЭМ-изображения частицы CNT @ Si @ C в различных состояниях лития (масштабная шкала = 0,5 мкм). Единица чисел на рисунках — нм. f Кривая объемного расширения частицы, записанная в процессе литирования. г ч Выбранная область электронограмм (SAED) CNT @ Si @ C в различных состояниях лития. j Долговременное циклирование CNT @ Si @ C, nano-Si и micro-Si. k Длительное циклирование УНТ @ Si @ C при различных массовых нагрузках.

Влияние пористой структуры CNT @ Si и CNT @ Si @ C на увеличение объема Si было исследовано с помощью измерений набухания частиц на месте и электрохимических испытаний. Обладая уникальной высокопористой шарообразной структурой, микросферы CNT @ Si и CNT @ Si @ C обеспечивают ожидаемый эффект по подавлению набухания пористых частиц Si при литировании при сохранении общей структурной целостности. In-situ ПЭМ-характеристика типичной микросферы CNT @ Si с ~ 1.Диаметр 12 мкм расширился до сферы размером ~ 1,14 мкм при частичном литиировании и до ~ 1,21 мкм при полном литировании (дополнительный рис. 18a – c и дополнительный фильм 1). Трехмерное (3D) расширение, рассчитанное по объему сферы, составляет ~ 27%, трещин не наблюдалось. Иерархическая пористая структура эффективна для уменьшения набухания и поддержания структурной целостности. Аналогичным образом были проверены множественные частицы, и среднее набухание структуры составляет ~ 30%, что составляет 1/10 расширения объемных частиц Si (дополнительный рис.18d – f и дополнительный фильм 2). В случае CNT @ Si @ C измерение набухания частиц на месте показало, что типичная сфера CNT @ Si @ C при полном литировании имеет объемное расширение ~ 44% (рис. 2c – f и дополнительный ролик 3). На выбранных площадных электронограммах во время литирования показано, что пористый Si изменился с поликристаллического (рис. 2g) на аморфный (рис. 2h), а затем в кристаллическую фазу Li 15 Si 4 после полного литирования (рис. 2i). . Повышенное объемное расширение CNT @ Si @ C происходит из-за уменьшения размера пор и объема пор после нанесения углеродного покрытия (дополнительная таблица 1).In-situ ПЭМ (дополнительный рис. 19) также показал, что размер частиц Si увеличивается по мере продолжения процесса литирования, в то время как разрушения не наблюдались даже после полного литирования. Частицы демонстрируют хорошую гибкость на протяжении всего процесса литирования, а связь между УНТ и Si остается хорошей во время повторяющихся циклов (см. Дополнительный фильм 4).

Электрохимические характеристики CNT @ Si @ C в параллельном сравнении с нано-Si или микронным Si, как показано на рис. 2j и рис.2k, соответственно, при различных нагрузках на электроды, является окончательным и лучшим доказательством достижения желаемой пористой структуры. На рис. 2j показана гораздо лучшая устойчивость CNT @ Si @ C к циклированию, чем у нано-Si или микронного Si при аналогичной низкой нагрузке на электрод (0,75 мАч см -2 ). CNT @ Si @ C почти не выцветали после циклов формирования. Сохранение емкости составляет> 87% (по сравнению с первым циклом после формирования) в течение ~ 1500 циклов. Также были проведены контрольные эксперименты с использованием традиционных наноструктурированных композитов CNT-Si-углерод и микросферных анодов CNT @ Si.Наноструктурированные композиты CNT-Si-углерод демонстрируют быстрое исчезновение емкости из-за плохой адгезии между электродами и токосъемниками, тогда как микросферы CNT @ Si демонстрируют начальную емкость 2441 мАч g -1 с сохранением емкости 72% после 240 циклов (Дополнительный рис. 20). Вышеприведенное сравнение по бокам дополнительно подтверждает преимущество конструкции микросфер CNT @ Si @ C в форме шарика из пряжи и важность углеродного покрытия. Рисунок 2k демонстрирует, что электрод CNT @ Si @ C может по-прежнему обеспечивать отличную циклируемость при повышенных нагрузках на электрод.Электрод с практической нагрузкой ~ 3 мАч / см -2 все еще сохраняет ~ 90% емкости после 100 циклов.

Подробный анализ характеристик батареи еще раз подтвердил успех нашей первоначальной конструкции конструкции. Во-первых, первая делитирующая способность электрода CNT @ Si @ C составляет ~ 1900 мА · ч г -1 при низкой плотности тока 0,1 мА · см -2 и ~ 1500 мА · ч г -1 при 1 мА · см — 2 (Рис. 2j и Дополнительный Рис. 21). С ~ 14 мас.% УНТ и 20 мас.% Углерода CVD (термогравиметрический анализ на дополнительном рис.22) Si в композите имеет удельную емкость ~ 2850 мАч g −1 ; это почти способность к делитированию коммерческого нано-Si, подтверждающая конструкцию структуры (дополнительный рис. 3). Во-вторых, постмодернистский анализ электродов показал, что микросферы CNT @ Si @ C сохранили исходную морфологию даже после 500 глубоких циклов (дополнительный рис. 23). В-третьих, средняя толщина исходных УНТ @ Si @ C составляет 42,5 мкм, которая увеличивается до 53 мкм после начального литирования (дополнительные рис.24–26). Таким образом, набухание электрода CNT @ Si @ C при 100% -ном состоянии заряда (SOC) составляет ~ 24,7% [(53 — 42,5) / 42,5]. По сравнению с набуханием частиц из-за ПЭМ на месте меньшее набухание электрода на уровне электрода объясняется дополнительной пористостью в электроде. Образование SEI, которое, как известно, приводит к увеличению толщины электродов, не было серьезным для анодов микросфер CNT @ Si @ C. Наконец, полные ячейки CNT @ Si @ C-NMC333 демонстрируют хорошую стабильность при циклическом воздействии с сохранением емкости> 91,7% за 100 циклов (дополнительный рис.27).

Механическая прочность микросфер CNT @ Si @ C

Механическая прочность CNT @ Si @ C была измерена с помощью in-situ атомно-силовой микроскопии (АСМ) и SEM-экспериментов. На рис. 3а показана экспериментальная схема прижатия отдельной частицы к наконечнику АСМ с соответствующей жесткостью пружины. Одиночная микросфера была приварена к жесткому W-зонду, который соединен с зондом OMNI. Микросфера и зонд W не имеют относительного движения и движутся как единое целое медленно, чтобы прижать фиксированный наконечник АСМ.Усилие на наконечнике АСМ и деформация микросфер регистрировались на месте для оценки модуля Юнга, коэффициента Пуассона и механической прочности УНТ @ Si @ C. На дополнительном рис. 28 показан типичный процесс изготовления наконечника АСМ для этого измерения механической прочности. Знание диаметра и площади наконечника АСМ так же важно, как и значение жесткости пружины для точного расчета силы сопротивления. Рисунки 3б – г были записаны в начале, в середине и до разрушения микросферы диаметром ~ 7.23 мкм (см. Процесс прессования в дополнительном ролике 5 и дополнительном рис. 28). Смещение наконечника АСМ / отклонение кантилевера и деформация микросферы (∆ d ) были тщательно измерены (см. Результаты на дополнительных рисунках 29 и 30). Сжимающая сила, приложенная к частице, которая рассчитывается из смещения наконечника AFM / отклонения кантилевера и жесткости пружины, показана на рис. 3h. Результаты на рис. 3h и дополнительном рис. 29 показывают, что частица может выдержать общую силу 705 мкН или эквивалентное давление ~ 181 МПа до разрушения.Подобные механические измерения систематически исследовались на частицах разного размера и с использованием разных наконечников АСМ для получения надежных результатов и минимизации разброса образца / измерения (дополнительный рисунок 31 и дополнительный ролик 6). Из этих результатов ясно видно, что CNT @ Si @ C обладает замечательной механической прочностью, которая может выдерживать давление ~ 200 МПа без трещин или серьезных деформаций. Учитывая, что расчеты проводились в моменты непосредственно перед тем, как произошло разрушение микросферы, сила не достигла точки разрушения, но уже соответствует давлению ~ 200 МПа, демонстрируя высокую механическую прочность наших иерархических пористых микросфер.

Рис. 3. Механическая прочность микросфер CNT @ Si @ C.

a Схема эксперимента in situ AFM – SEM для измерения механической прочности. b d Смещение наконечника АСМ / отклонение кантилевера типичной сферы CNT @ Si @ C под действием приложенной силы: b начало прессования; c средняя точка нажатия; d прямо перед распадом частицы. e g Различные состояния деформации сферы CNT @ Si @ C: e d = 0.05 мкм; f d = 0,27 мкм; г d = 0,49 мкм, и соответствующее распределение напряжения фон Мизеса (МПа) из моделирования методом КЭ (вставка) с модулем Юнга 0,5 ГПа и коэффициентом Пуассона 0,3. Масштабная линейка для рис. 3б – ж составляет 10 мкм. Единица измерения чисел на рисунках — мкм. h График зависимости силы от времени в период от b до d . i Экспериментальные данные (точки) и кривая КЭ моделирования зависимости силы от деформации частицы Δ d с коэффициентом Пуассона 0.3 и разные модули Юнга.

Для оценки модуля Юнга был использован метод конечных элементов (КЭ) для моделирования деформации микросферы с точно такими же граничными условиями, что и в экспериментах с АСМ – СЭМ на месте, т.е. площадь, заданная деформация (∆ d ) граничное условие на контактирующей области между микросферой и наконечником АСМ и граничное условие отсутствия напряжений на остальной поверхности микросферы. Распределение напряжений по Мизесу для последовательности различных деформационных состояний показано на рис.3e-g. Напряжение фон Мизеса под прижимным наконечником намного больше, чем у основания, из-за различных граничных условий внизу и вверху частицы. Высокое напряжение по Мизесу должно вызывать разрывы возле наконечника пресса, как показано в дополнительном фильме S6. Кривые зависимости силы от времени и деформации частицы (Δ d ), рассчитанные на основе моделирования методом конечных микроскопов для различных упругих свойств, показаны на рис. 3h – i. Сравнивая результаты моделирования и экспериментов, модуль Юнга и коэффициент Пуассона были определены равными 0.5 ГПа и 0,3 соответственно. Настройки модели и результаты моделирования подробно представлены в дополнительных материалах (дополнительные рисунки 32 и 33 и дополнительный ролик 7).

Эксперимент с плоским штампом (подложка и зонд представляют собой две бесконечные плоскости по сравнению с микросферой) с использованием отдельно стоящей микросферы также был проведен для дальнейшей оценки механической прочности материала CNT @ Si @ C (дополнительные рисунки 34 и 35). Модуль Юнга, измеренный в эксперименте с плоским пуансоном, составляет ~ 0.5–0,8 ГПа, что хорошо согласуется с результатами измерений АСМ – СЭМ на месте и моделирования КЭ. Это подтверждает надежность результатов наших механических измерений и моделирования КЭ. Напряжение в конце упругой деформации в эксперименте с плоским пуансоном составляет ~ 48,4 МПа, тогда как в конце уплотнения оно составляет ~ 91,2 МПа. Механическая прочность микросфер из экспериментов с плоским пуансоном находится между двумя числами, но меньше, чем значение, измеренное с помощью конструкции АСМ – СЭМ на месте, из-за различных настроек эксперимента и граничных условий (ограничение смещения в экспериментах АСМ – СЭМ на месте должно уменьшить деформацию и привести к увеличению прочности на излом).Следует отметить, что прочность на упругую деформацию 48,4 МПа все же больше, чем прочность на изгиб структур ядро-оболочка 17 и, возможно, других пористых структур. Считается, что высокая механическая прочность УНТ @ Si @ C обусловлена ​​превосходными свойствами УНТ и структурой микросферической пряжи-шариковой матрицы. Известно, что УНТ имеют высокие значения модуля Юнга от 12 до 50 ГПа, и их трудно сжать в осевом или радиальном направлении 42,50 . Кроме того, клубок пряжи из УНТ может немного деформироваться и потерять некоторую пористость при очень высокой силе сжатия, но он не сломается.Соответствующее отношение Si к УНТ к С также полезно для прочности структуры, но необходимы дальнейшие исследования для корреляции конструкции структуры и механических свойств.

Замечательная механическая прочность позволяет CNT @ Si @ C подвергаться процессу каландрирования, что особенно важно для композитных электродов Gr и практического применения (некаландрированные электроды имеют высокую пористость, что приведет к увеличению количества электролита и, следовательно, к снижению плотность энергии элемента, даже несмотря на то, что удельная емкость / стабильность анода может быть хорошей.В большинстве предыдущих исследований аккумуляторов этому в значительной степени пренебрегали.) На дополнительном рис. 36 показан вид сверху и поперечный разрез электродов с различной каландровой обработкой. Несмотря на уплощенную поверхность, УНТ @ Si @ C в электродах, каландрированных до ~ 1,2 г / см -3 (толщиной 20 мкм), все еще сохраняет сферическую морфологию, аналогичную морфологии литых электродов.

Электрохимические характеристики композитных анодов CNT @ Si @ C и графита

Обладая уникальной наноструктурой с выдающимися механическими и электрохимическими характеристиками, микросферы CNT @ Si @ C были смешаны с Gr для получения практичных композитных электродов.На рис. 4 показаны электрохимические характеристики композитного электрода с высокой нагрузкой CNT @ Si @ C-Gr (30 мас.%: 58 мас.%), Полученного в полуячейках. Кривые заряда-разряда (рис. 4a) показали типичные профили для анодов Si-Gr между 0,02 и 1,5 В, аналогичные тем, которые ранее были опубликованы в литературе 27,41,51 . CE первого цикла композитного электрода CNT @ Si @ C-Gr составляет ~ 84%, что выше, чем у CNT @ Si @ C (75%; см. Рис. 2 и дополнительный рис. 21). Удельная емкость составляет ~ 844 мАч g −1 (второй цикл формирования) при низкой плотности тока 0.1 мА · см −2 и ~ 718 мА · ч g −1 при 0,75 мА · см −2 , что указывает на хорошие показатели скорости даже при практической нагрузке ~ 3 мА · ч · см −2 .

Рис. 4: Электрохимические характеристики графитовых анодов CNT @ Si @ C (30 мас.%: 58 мас.%) В полуячейках.

a Типичные кривые напряжения CNT @ Si @ C и графитового композитного анода с нагрузкой ~ 3 мАч см −2 . Массовая нагрузка электродов на рис. 4 составляет 4,0 мг / см −2 . b Циклическое поведение того же анода в a . c Измерение набухания электрода во время разряда – заряда на месте при 0,2 мА см −2 . d СЭМ-изображение чистого электрода (масштабная линейка = 30 мкм). e СЭМ-изображение полностью литированного электрода после 1 цикла (шкала = 30 мкм). f Циклическая стабильность электрода, каландрированного до 1,2 г см −3 . г СЭМ-изображение электрода после каландрирования до 1.2 г см −3 до цикла (шкала = 30 мкм). ч СЭМ-изображение каландрированного электрода в состоянии полного литирования после 120 циклов (шкала = 30 мкм).

Исходя из того факта, что CNT @ Si @ C имеет хорошую циклическую стабильность (рис. 2) и минимальное набухание частиц ~ 40% (рис. 2), можно ожидать, что композитные аноды из CNT @ Si @ C-Gr будут обладают отличными характеристиками цикличности и минимальным разбуханием электродов. На рисунке 4b показан превосходный срок службы CNT @ Si @ C-Gr при циклической работе с сохранением емкости> 92% за 500 циклов.КЭ при стабильной циклировке составляет ~ 99,9%.

Электрохимические дилатометрические измерения in-situ и исследования ex-situ SEM исходного электрода и электрода при 100% SOC показали стабильный результат небольшого набухания электрода (рис. 4c – e). На рисунке 4c показано набухание (красный цвет) типичного электрода с высокой нагрузкой при циклировании (черный). Наибольшее расширение электрода составляет ~ 21% при полном литировании и ~ 6% в состояниях делитирования. На рис. 4d – e представлены изображения поперечного сечения чистого электрода и электрода после первого литирования до 100% SOC.Толщина электрода перед испытанием составляет в среднем ~ 50,7 мкм [61,7 мкм (общая толщина в среднем) — 11 мкм (толщина медной фольги)], тогда как средняя толщина электрода при первом полном литировании составляет ~ 59,7 мкм (70,7 мкм. — 11 мкм). Расширение электрода при полном литировании составляет ~ 18,3% [(59,7 — 50,7) / 50,7], что соответствует результатам дилатометра на месте.

Исключительная механическая прочность наноструктуры CNT @ Si @ C позволяет каландрировать электроды до высокой плотности.На дополнительном рис. 37 показаны каландрированные электроды со сферической морфологией CNT @ Si @ C, сохраняющейся даже при прессовании электродов до плотности до 1,4 г / см −3 . Электроды показали одинаковую циклическую стабильность с каландрированием и без него. Электрод, каландрированный до ~ 1,2 г / см -3 , показывает 96% -ное сохранение емкости за 120 циклов (рис. 4f), тогда как электрод, каландрированный до ~ 1,4 г / см -3 , имеет сохранение ~ 91% емкости (дополнительный рис. 38). ). Удельная емкость также аналогична некаландрированным электродам, хотя нагрузка на электрод составляет ~ 3 мАч см -2 .Поляризация, вносимая каландрированием, в этом случае незначительна. Объемные плотности емкости электродов 1,2 и 1,4 г см −3 составляют ~ 844 и 980 мАч см −3 (дополнительная таблица 2), рассчитанная по второй разрядной емкости, ~ 799 и 796 мАч г — 1 соответственно (рис. 4е и дополнительный рис. 38). Это примерно в 1,4–1,7 раза больше, чем у электродов Gr 52 .

Набухание SOC и EOL каландрированных электродов можно оценить с помощью SEM-анализа изменения толщины электрода до и после циклирования.Для электрода, каландрированного до плотности 1,2 г / см −3 , набухание при 100% SOC после первого цикла составляет ~ 23,5% [(42,6 — 34,5) / 34,5] (дополнительный рис. 39). В случае набухания EOL толщина увеличилась с 33,8 мкм (44,8 (средняя толщина всего электрода) — 11 (толщина медной фольги), рис. 4g и дополнительная таблица 2) до ~ 45,4 мкм (56,4 — 11, Рис. 4h и дополнительная таблица 2) после 120 циклов. Набухание EOL составляет ~ 34% [(45,4 — 33,8) / 33,8]. Электрод каландрирован до 1.4 г / см -3 показывает увеличение толщины с ~ 28,1 мкм (дополнительный рисунок 26 и дополнительная таблица 2) до ~ 40,7 мкм (дополнительный рисунок 38) после 120 циклов. Набухание EOL составляет ~ 45%.

Композитные аноды

CNT @ Si @ C и Gr также показывают отличные характеристики в полных ячейках по отношению к катодам Li (Ni 1/3 Mn 1/3 Co 1/3 ) O 2 (NMC). Катоды NMC были подготовлены с соответствующей нагрузкой (подробности см. В экспериментальной части) и испытаны при различных плотностях тока в конфигурации полуячейки.При поверхностной нагрузке ∼2,4 мАч · см −2 катод NMC обеспечивает удельную емкость ∼150 мА · ч g −1 в диапазоне от 2,7 до 4,3 В (дополнительный рисунок 40). Типичная полная ячейка с чистым анодом CNT @ Si @ C-Gr имеет ~ 77% CE первого цикла и 76% сохранения емкости за 120 циклов (дополнительный рисунок 41). Чтобы еще больше улучшить циклические характеристики полных ячеек, аноды были сначала циклированы в конфигурации полуэлементов с металлическим Li перед испытанием полных ячеек (подробности см. В разделе «Методы»).Хорошие характеристики для полной ячейки были получены при соотношении n / p ~ 1,1: 1 (отношение масс анода / катода = 0,22). На рис. 5 представлены типичные характеристики полной ячейки с катодом NMC333 и предварительно зацикленным анодом CNT @ Si @ C-Gr с емкостью ~ 3 мА · ч. На рисунке 5a показаны кривые напряжения заряда / разряда для типичного полного элемента при циклическом изменении от 2,8 до 4,2 В. Удельная разрядная емкость катода NMC составляет ~ 145 мА ч г -1 , аналогично измерениям полуячейки. . Он также демонстрирует превосходный срок службы при нагрузке 92% за 500 циклов (рис.5б).

Рис. 5: Электрохимические характеристики типичного полного элемента с Li (Ni 1/3 Mn 1/3 Co 1/3 ) O 2 катод и предварительно обработанные CNT @ Si @ C и графитовый композитный анод.

a Профили напряжения. b Долгосрочные данные цикла полной ячейки в a .

Этот сайт использует файлы cookie для повышения производительности. Если ваш браузер не принимает файлы cookie, вы не можете просматривать этот сайт.


Настройка вашего браузера для приема файлов cookie

Существует множество причин, по которым cookie не может быть установлен правильно. Ниже приведены наиболее частые причины:

  • В вашем браузере отключены файлы cookie. Вам необходимо сбросить настройки своего браузера, чтобы он принимал файлы cookie, или чтобы спросить вас, хотите ли вы принимать файлы cookie.
  • Ваш браузер спрашивает вас, хотите ли вы принимать файлы cookie, и вы отказались.
    Чтобы принять файлы cookie с этого сайта, нажмите кнопку «Назад» и примите файлы cookie.
  • Ваш браузер не поддерживает файлы cookie. Если вы подозреваете это, попробуйте другой браузер.
  • Дата на вашем компьютере в прошлом. Если часы вашего компьютера показывают дату до 1 января 1970 г.,
    браузер автоматически забудет файл cookie. Чтобы исправить это, установите правильное время и дату на своем компьютере.
  • Вы установили приложение, которое отслеживает или блокирует установку файлов cookie.
    Вы должны отключить приложение при входе в систему или проконсультироваться с системным администратором.

Почему этому сайту требуются файлы cookie?

Этот сайт использует файлы cookie для повышения производительности, запоминая, что вы вошли в систему, когда переходите со страницы на страницу. Чтобы предоставить доступ без файлов cookie
потребует, чтобы сайт создавал новый сеанс для каждой посещаемой страницы, что замедляет работу системы до неприемлемого уровня.


Что сохраняется в файле cookie?

Этот сайт не хранит ничего, кроме автоматически сгенерированного идентификатора сеанса в cookie; никакая другая информация не фиксируется.

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

Этот сайт использует файлы cookie для повышения производительности.Если ваш браузер не принимает файлы cookie, вы не можете просматривать этот сайт.


Настройка вашего браузера для приема файлов cookie

Существует множество причин, по которым cookie не может быть установлен правильно. Ниже приведены наиболее частые причины:

  • В вашем браузере отключены файлы cookie. Вам необходимо сбросить настройки своего браузера, чтобы он принимал файлы cookie, или чтобы спросить вас, хотите ли вы принимать файлы cookie.
  • Ваш браузер спрашивает вас, хотите ли вы принимать файлы cookie, и вы отказались.Чтобы принять файлы cookie с этого сайта, нажмите кнопку «Назад» и примите файлы cookie.
  • Ваш браузер не поддерживает файлы cookie. Если вы подозреваете это, попробуйте другой браузер.
  • Дата на вашем компьютере в прошлом. Если часы вашего компьютера показывают дату до 1 января 1970 г.,
    браузер автоматически забудет файл cookie. Чтобы исправить это, установите правильное время и дату на своем компьютере.
  • Вы установили приложение, которое отслеживает или блокирует установку файлов cookie.Вы должны отключить приложение при входе в систему или проконсультироваться с системным администратором.

Почему этому сайту требуются файлы cookie?

Этот сайт использует файлы cookie для повышения производительности, запоминая, что вы вошли в систему, когда переходите со страницы на страницу. Чтобы предоставить доступ без файлов cookie
потребует, чтобы сайт создавал новый сеанс для каждой посещаемой страницы, что замедляет работу системы до неприемлемого уровня.


Что сохраняется в файле cookie?

Этот сайт не хранит ничего, кроме автоматически сгенерированного идентификатора сеанса в cookie; никакая другая информация не фиксируется.

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *