Си struct – Структуры в си

Расставим точки над структурами C/C++ / Habr

Недавно познакомился со структурами C/C++ — struct. Господи, да «что же с ними знакомиться» скажете вы? Тем самым вы допустите сразу 2 ошибки: во-первых я не Господи, а во вторых я тоже думал что структуры — они и в Африке структуры. А вот как оказалось и — нет. Я расскажу о нескольких жизненно-важных подробностях, которые кого-нибудь из читателей избавят от часовой отладки…


Выравнивание полей в памяти


Обратите внимание на структуру:
struct Foo
{
    char ch;
    int value;
};

Ну во-первых какой у этой структуры размер в памяти? sizeof(Foo)?
Размер этой структуры в памяти зависит от настроек компилятора и от директив в вашем коде…

В общем выравниваются в памяти поля по границе кратной своему же размеру. То есть 1-байтовые поля не выравниваются, 2-байтовые — выравниваются на чётные позиции, 4-байтовые — на позиции кратные четырём и т.д. В большинстве случаев (или просто предположим что сегодня это так) выравнивание размера структуры в памяти составляет 4 байта. Таким образом, sizeof(Foo) == 8. Где и как прилепятся лишние 3 байта? Если вы не знаете — ни за что не угадаете…

  • 1 байт: ch
  • 2 байт: пусто
  • 3 байт: пусто
  • 4 байт: пусто
  • 5 байт: value[0]
  • 6 байт: value[1]
  • 7 байт: value[2]
  • 8 байт: value[3]

Посмотрим теперь размещение в памяти следующей структуры:
struct Foo
{
    char ch;
    short id;
    int value;
};

Оно выглядит вот так:
  • 1 байт: ch
  • 2 байт: пусто
  • 3 байт: id[0]
  • 4 байт: id[1]
  • 5 байт: value[0]
  • 6 байт: value[1]
  • 7 байт: value[2]
  • 8 байт: value[3]

То есть, то что можно впихнуть до выравнивания по 4 байта — впихивается на ура (без увеличения размера структуры в памяти), добавим ещё одно поле:
struct Foo
{
    char ch;
    short id;
    short opt;
    int value;
};

Посмотрим на размещение полей в памяти:
  •   1 байт: ch
  •   2 байт: пусто
  •   3 байт: id[0]
  •   4 байт: id[1]
  •   5 байт: opt[0]
  •   6 байт: opt[1]
  •   7 байт: пусто
  •   8 байт: пусто
  •   9 байт: value[0]
  • 10 байт: value[1]
  • 11 байт: value[2]
  • 12 байт: value[3]

Всё это ой как печально, но есть способ бороться с этим прямо из кода:
#pragma pack(push, 1)
struct Foo
{
    // ...
};
#pragma pack(pop)

Мы установили размер выравнивания в 1 байт, описали структуру и вернули предыдущую настройку. Возвращать предыдущую настройку — категорически рекомендую. Иначе всё может закончиться очень плачевно. У меня один раз такое было — падало Qt. Где-то заинклюдил их .h-ник ниже своего .h-ника…

Битовые поля


В комментариях мне указали на то, что битовые поля в структурах по стандарту являются «implementation defined» — потому их использования лучше избежать, но для меня соблазн слишком велик…

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

unsigned field = 0x00530000;

// ...

field &= 0xFFFF00FF;
field |= (id) << 8;

// ...

field &= 0xFFFFFF83;
field |= (proto) << 2;

Всё это пахнет такой печалью и такими ошибками и их отладкой, что у меня сразу же начинается мигрень! И тут из-за кулис выходят они — Битовые Поля. Что самое удивительное — были они ещё в языке C, но кого ни спрашиваю — все в первый раз о них слышат. Этот беспредел надо исправлять. Теперь буду давать им всем ссылку, ну или хотя бы ссылку на эту статью.

Как вам такой кусок кода:

#pragma pack(push,1)
struct IpHeader
{
    uint8_t header_length:4;
    uint8_t version:4;
    uint8_t type_of_service;
    uint16_t total_length;
    uint16_t identificator;

    // Flags
    uint8_t _reserved:1;
    uint8_t dont_fragment:1;
    uint8_t more_fragments:1;

    uint8_t fragment_offset_part1:5;
    uint8_t fragment_offset_part2;
    uint8_t time_to_live;
    uint8_t protocol;
    uint16_t checksum;

    // ...
};
#pragma pack(pop)

А дальше в коде мы можем работать с полями как и всегда работаем с полями в C/C++. Всю работу по сдвигам и т.д. берет на себя компилятор. Конечно же есть некоторые ограничения… Когда вы перечисляете несколько битовых полей подряд, относящихся к одному физическому полю (я имею ввиду тип который стоит слева от имени битового поля) — указывайте имена для всех битов до конца поля, иначе доступа к этим битам у вас не будет, иными словами кодом:
#pragma pack(push,1)
stuct MyBitStruct
{
    uint16_t a:4;
    uint16_t b:4;
    uint16_t c;
};
#pragma pack(pop)

Получилась структура на 4 байта! Две половины первого байта — это поля a и b. Второй байт не доступен по имени и последние 2 байта доступны по имени c. Это очень опасный момент. После того как описали структуру с битовыми полями обязательно проверьте её sizeof!

Также порядок размещения битовых болей в байте зависит от порядка байтов. При порядке LITTLE_ENDIAN битовые поля раздаются начиная со первых байтов, при BIG_ENDIAN — наоборот…

Порядок байтов


Меня также печалят в коде вызовы функций htons(), ntohs(), htonl(), nthol() в коде на C++. На C это ещё допустимо, но не на С++. С этим я никогда не смирюсь! Внимание всё нижесказанное относится к C++!

Ну тут я буду краток. Я в одной из своих предыдущих статей уже писал что нужно делать с порядками байтов. Есть возможность описать структуры, которые внешне работают как числа, а внутри сами определяют порядок хранения в байтах. Таким образом наша структура IP-заголовка будет выглядеть так:

#pragma pack(push,1)
struct IpHeader
{
    uint8_t header_length:4;
    uint8_t version:4;
    uint8_t type_of_service;
    u16be total_length;
    u16be identificator;

    // Flags
    uint8_t _reserved:1;
    uint8_t dont_fragment:1;
    uint8_t more_fragments:1;

    uint8_t fragment_offset_part1:5;
    uint8_t fragment_offset_part2;
    uint8_t time_to_live;
    uint8_t protocol;
    u16be checksum;

    // ...
};
#pragma pack(pop)

Внимание собственно обращать на типы 2-байтовых полей — u16be. Теперь поля структуры не нуждаются ни в каких преобразованиях порядка байт. Остаются проблемы с fragment_offset, ну а у кого их нет — проблем-то. Тем не менее тоже можно придумать шаблон, прячущий это безобразие, один раз его оттестировать и смело использовать во всём своём коде.

«Язык С++ достаточно сложен, чтобы позволить нам писать на нём просто» © Как ни странно — Я

З.Ы. Планирую в одной из следующих статей выложить идеальные, с моей точки зрения, структуры для работы с заголовками протоколов стека TCP/IP. Отговорите — пока не поздно!

habr.com

struct. Справочник по C# | Microsoft Docs

  • Время чтения: 2 мин

В этой статье

Тип struct представляет собой тип значения, который обычно используется для инкапсуляции небольших групп связанных переменных, например координат прямоугольника или характеристик элемента в инвентаризации.A struct type is a value type that is typically used to encapsulate small groups of related variables, such as the coordinates of a rectangle or the characteristics of an item in an inventory. В следующем примере показано простое объявление структуры:The following example shows a simple struct declaration:

public struct Book
{
    public decimal price;
    public string title;
    public string author;
}

ПримечанияRemarks

Структуры также могут содержать конструкторы, константы, поля, методы, свойства, индексаторы, операторы, события и вложенные типы, хотя, если требуется несколько таких членов, тип необходимо заменить классом.Structs can also contain constructors, constants, fields, methods, properties, indexers, operators, events, and nested types, although if several such members are required, you should consider making your type a class instead.

Примеры см. в разделе Использование структур.For examples, see Using Structs.

Структуры могут реализовать интерфейс, но не могут наследоваться из другой структуры.Structs can implement an interface but they cannot inherit from another struct. По этой причине члены структуры не могут объявляться как protected.For that reason, struct members cannot be declared as protected.

Дополнительные сведения см. в разделе Структуры.For more information, see Structs.

ПримерыExamples

Примеры и дополнительные сведения см. в разделе Использование структур.For examples and more information, see Using Structs.

Спецификация языка C#C# language specification

Примеры см. в разделе Использование структур.For examples, see Using Structs.

См. такжеSee also

docs.microsoft.com

C++ Структуры. Пользовательские типы в С++

В сегодняшнем уроке мы рассмотрим структуры в C++ (Structs). Структуры позволяют определить свой собственный тип данных. В предыдущих урокам мы сталкивались только со стандартными типами данных языка С++: char, float, int… Когда мы начнём рассматривать WinAPI, то увидим, что Microsoft использовала огромное количество структур при написании Windows.

В структурах можно определить несколько переменных для моделирования какого-либо сложного понятия. Например, в нашей программе есть множество юнитов. При этом каждый должен обладать свойствами: размер, сила и скорость. Вот так мы можем описать это в C++ без структур:

int unit_size[10]; int unit_strengths[10]; int unit_speed[10];

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

Структуры C++

struct Unit { int size; int speed; int strength; };

Структуры С++ позволяют построить более адекватную модель сложной (сложнее чем простое число или строка) сущности. Когда мы создаём модель, мы стараемся описать самые важные свойства объекта из реального мира (или абстрактного понятия). Так как в наших примерах мы рассматриваем создание игр, то и примеры будем приводить из этой области. В данном коде мы пытаемся описать единицу (юнит) из какой-то абстрактной игры. В данном случае юнит обладает тремя свойствами — вы сами определяете, что должно быть в структуре.

Структура определяется с помощью ключевого слово struct, после которого идёт имя структуры. Имя может быть любым (стандартный идентификатор C++). В дальнейших уроках все имена структур будут начинаться с заглавной буквы. В WinAPI имена всех структур пишутся в верхнем регистре: RECT, POINT, SIZE и т.д. В фигурных скобках мы описываем разные свойства структуры с помощью объявления переменных. В данном случае, структура Unit будет иметь три свойства: size, speed, strength. Эти переменные называются членами, полями или свойствами структуры. Обратите внимание на точку с запятой после закрывающей фигурной скобки — она обязательна.

Структуры в C++ пришли из языка C. При этом С++ структуры имеют некоторые отличия от оригинала. В следующем уроке мы начнём рассматривать классы и увидим чем структуры в С отличаются от структур в C++.

С++ структуры в памяти

В памяти поля структур располагаются последовательно. Поля size, speed, strength будут расположены рядом. Между ними не будет никаких других данных. Вот как может выглядеть переменная типа Unit в памяти:

0000007F3D3AFAE8 // size 0000007F3D3AFAEC // speed 0000007F3D3AFAF0 // strength

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

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

Пользовательский тип данных

В C++ структуры являются такими же типами данных как и int, char, double… После определения структуры, её можно использовать как и любой другой тип — создавать структурные переменные.

struct Unit { int size; int speed; int strength; }; Unit u1; Unit u2; Unit units[10];

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

Unit u1 = {1,1,1}; Unit units[3] = { {4,4,5}, {2,10,1}, {10,1,25}, };

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

Ещё раз повторю, так как это важно: для компьютера это всего лишь числа не имеющие никакого значения. Значение этим числам придаём мы. Мы строим модель какого-то понятия (юнита в данном случае). И мы сами интерпретируем эти значения. Глядя на последний массив мы можем сказать про наши юниты следующее: первый — середнячок, у него все параметры средние; второй — маленький, быстрый, но слабый; последний — большой, медленный, но сильный. Это всего-лишь данные. Когда мы познакомимся с графическим выводом, мы сможем наглядно преподнести эти данные пользователю.

Доступ к полям структурных переменных

Мы можем прочитать и изменить отдельные поля с помощью оператора доступа к члену (member selection operator или member access operator) — точки:

Unit u1; Unit u2; Unit units[10]; u1.strength = 5; units[4].speed = 10;

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

Для структурных переменных, созданных динамически, используется оператор непрямого доступа ->.

Unit* u1 = new Unit; u1->strength = 5;

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

Заключение

В данном уроке мы рассмотрели структуры в C++. В следующем мы сделаем логичный шаг и объединим в одной сущности не только переменные, но и функции: наши модели понятий будут обладать не только свойствами, но и поведением — мы начнём рассматривать классы.

about-prog.com

Структуры в Си и их передача

Структура — это удобное хранилище для разнородных данных, которые хочется объединить. К примеру, вы можете создать структуру, описывающую параметры вашего устройства — сетевые настройки, таймаут спящего режима, его идентификатор и прочее подобное, типа какой-нибудь строки приветствия и состояния светодиода. Раз все параметры будут храниться в одном месте — они всегда будут на виду, да и нормальные IDE будут вам подсказывать поля структуры при обращении к ним. Ещё мы рассмотрим хранение и восстановление структур из архива, а также их передачу по сети.

Объявление такой структуры:

struct {
uint32_t ID;
char IP[4];
uint16_t timeout;
bool led;
char text[12];
} params;

Как это работает?

В си довольно удобный синтаксис, в том плане что многие вещи записываются как «тип_данных переменная», начиная с «int i» заканчивая «void main() {}». Так и здесь, кодовое слово struct начинает объявление структуры, и весь кусок кода «struct { … }» просто задаёт новый тип. Соответственно, params — это уже готовая переменная (экземпляр типа), которую можно использовать. Внутри фигурных скобок перечислены все поля структуры, которые потом будут доступны так: params.ID или params.IP[2]. Длина полей должна быть фиксированной, поэтому нельзя использовать строки вида *text, только массивы вида text[12].

Можно было сделать немного иначе: объявить только тип, а переменную завести позже. Для этого мы использовали бы ключевое слово typedef и написали так:

typedef struct {
uint32_t ID;
char IP[4];
uint16_t timeout;
bool led;
char text[12];
} params_struct;
params_struct params;

Так появляется возможность оставить все объявления структурных типов в отдельном файле (header), а в главном файле просто использовать уже готовые структурные типы для объявления структур прямо по месту.

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

struct {
uint32_t ID;
char IP[4];
uint16_t timeout;
bool led;
char text[12];
} params1, params2, params[10];

Вариант с массивом особенно удобен для сервера в клиент-серверной топологии сети — на каждом клиенте хранятся в структуре его собственные параметры, а на мастер-устройстве располагается таблица параметров всех клиентов в виде массива структур.

В принципе, ничего сложного в структурах нет, а с темой серверов и клиентов мы плавно подошли к более интересной теме:

Хранение, передача и синхронизация структур

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

Начальное смещение получим так:

char *Bytes = &params;

мы объявили указатель char и поместили в него адрес params. Теперь Bytes указывает на первый байт структуры, и при последовательном чтении мы побайтно прочитаем всю структуру. Но сколько байт нужно прочитать? Для этого рассмотрим две интересных функции.

sizeof и offsetof

Это даже не функции, а встроенные макросы языка Си. Начнём с более простой, sizeof.

Компилятор заменяет все записи вида sizeof X на значение длины Х. В качестве X может выступать как тип, так и экзмепляр типа, т.е. в нашем случае можно подставить в sizeof и тип структуры (если мы его заводили с помощью typedef), и саму переменную структуры так: sizeof params_struct или sizeof params. Она пройдёт по всем полям структуры, сложит их длины и отдаст сумму, которая и будет длиной структуры.

offsetof — настоящий макрос, который принимает два параметра (структуру _s_ и поле _m_ в ней) и отдаёт положение этого поля в структуре, его смещение относительно начала структуры. Выглядит этот макрос очень просто:

offsetof(s, m) (size_t)&(((s *)0)-›m).

Как он работает?

  1. Берём число 0
  2. Преобразуем его к типу «указатель на структуру s»: (s*)0
  3. Обращаемся к полю m из этой структуры: ((s*)0)->m
  4. Вычисляем его адрес: &(((s*)0)->m)
  5. Преобразуем адрес к целому числу: (size_t)&(((s*)0)->m)

Магия именно в первом шаге, в котором мы берём 0. Благодаря этому на четвёртом шаге абсолютный адрес поля, вычисленный компилятором, оказывается отсчитан относительно начала структуры — структуру-то мы положили в адрес 0. Таким образом, после выполнения этого макроса мы реально имеем смещение поля относительно начала структуры. Понятно, что этот макрос правильно определит смещения даже в сложных и вложенных структурах.

Здесь нужно сделать небольшое отступление. Дело в том, что я рассматривал самый простой случай, когда поля упакованы точно вслед друг за другом. Есть и другие методы упаковки, которые называются «выравнивание». К примеру, можно выдавать каждому полю «слот», кратный 4 байтам, или 8 байтам. Тогда даже char будет занимать 8 байт, и общий размер структуры вырастет, а все смещения сдвинутся и станут кратны выравниванию. Эта штука полезна при программировании для компьютера, поскольку из-за грануляции ОЗУ процессор гораздо быстрее умеет извлекать из памяти выровненные данные, ему требуется на это меньше операций.

Работа с массивом из структуры

Окей, теперь мы умеем представлять любую структуру в виде массива байт, и обратно. Вы поняли фишку? У нас теперь одна и та же область памяти имеет роли «структура» и «массив». Изменяем что-то в структуре — меняется массив, меняем массив — меняется структура.

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

Теперь осталось лишь научиться удобно с этим всем работать.

Хранение и передача структуры

Чтобы создать архивную копию структуры, для передачи по сети или для складывания её в надёжное место — отдайте в вашу функцию передачи данных адрес этого массива. К примеру, моя функция записи массива данных в EEPROM выглядит так: I2C_burst_write (I2Cx, HW_address, addr, n_data, *data). Вам просто нужно вместо n_data передать sizeof params, а вместо *data — &params:

I2C_burst_write (I2Cx, HW_address, addr, sizeof params, &params)

Функции передачи данных по сети обычно выглядят примерно так же. В качестве данных передавайте &params, а в качестве длины данных — sizeof params.

Приём и восстановление структуры

Всё точно так же. Моя функция чтения массива из EEPROM: I2C_burst_read (I2Cx, HW_address, addr, n_data, *data). n_data = sizeof params, *data = &params:

I2C_burst_read (I2Cx, HW_address, addr, sizeof params, &params)

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

memcpy(&params, &temp_buffer, sizeof params).

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

Хранение/восстановление отдельных полей

И зачем же мы так долго рассматривали макрос offsetof? Его очень удобно использовать для чтения и записи отдельных полей структуры, например так:

I2C_burst_write (I2Cx, HW_address, addr + offsetof(params, IP), sizeof params.IP, &params.IP)
I2C_burst_read  (I2Cx, HW_address, addr + offsetof(params, IP), sizeof params.IP, &params.IP)

Ну и вообще, было бы неплохо сделать удобные макросы-обёртки для этой цели.

#define store(structure, field) I2C_burst_write (I2Cx, HW_address, addr + offsetof(structure, field), sizeof(structure.field), &(structure.field))
#define load(structure, field)  I2C_burst_read  (I2Cx, HW_address, addr + offsetof(structure, field), sizeof(structure.field), &(structure.field))

Post Views: 861

catethysis.ru

C определение структуры. Инициализация структуры в си (c struct)

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

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

Struct addr {
char name;
char street ; char city;
char state;
unsigned long int zip;
};

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

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

Struct addr addr_info;

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

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

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

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

Например:

Struct addr {
char name;
char street;
char city;
char state;
unsigned long int zip;
} addr_info; binfo, cinfo;

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

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

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

Struct {
char name;
char street;
char city;
char state;
unsigned long int zip;
} addr_info;

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

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

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

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

Массивы

Прежде чем говорить о структуре в Си, нужно описать массив.

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

Обычный массив в Си записывается так: int a = {1, 2, 3, 4}.

Видим, что a — имя, int — тип данных, внутри фигурных скобок { } находятся значения, между квадратными скобками указывается длина, то есть количество элементов. Количество элементов является статическим, равняется 4. Это означает, что если в этом примере пользователь добавит пятое значение, компилятор выдаст ошибку. Если изначально не известно количество, они могут быть добавлены позже, но в квадратных скобках не ставится значение.

Двумерный объявляется похожим образом. Например, массив, который содержит 5 элементов-массивов, при этом каждый содержит по 3 элемента объявляется так: int a.По аналогии с одномерным добавлять ничего нельзя, чтобы не получить ошибку компилирования.

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

Классы

Класс и структура похожи по между собой, но отличаются некоторыми нюансами. Что это такое? Это абстракция, описывающая мето

offlink.ru

Руководство по программированию на C#. Структуры

  • Время чтения: 2 мин

В этой статье

Структуры определяются с помощью ключевого слова struct, например:Structs are defined by using the struct keyword, for example:

public struct PostalAddress
{
    // Fields, properties, methods and events go here...
}

Структуры используют большую часть того же синтаксиса, что и классы.Structs share most of the same syntax as classes. Имя структуры должно быть допустимым именем идентификатора C#.The name of the struct must be a valid C# identifier name. Структуры более ограничены по сравнению с классами по следующим причинам.Structs are more limited than classes in the following ways:

  • В объявлении структуры поля не могут быть инициализированы до тех пор, пока они будут объявлены как const или static.Within a struct declaration, fields cannot be initialized unless they are declared as const or static.
  • Структура не может объявлять конструктор без параметров или метод завершения.A struct cannot declare a parameterless constructor (a constructor without parameters) or a finalizer.
  • Структуры копируются при присваивании.Structs are copied on assignment. При присваивании структуры к новой переменной выполняется копирование всех данных, а любое изменение новой копии не влияет на данные в исходной копии.When a struct is assigned to a new variable, all the data is copied, and any modification to the new copy does not change the data for the original copy. Это важно помнить при работе с коллекциями типов значений, такими как Dictionary<string, myStruct>.This is important to remember when working with collections of value types such as Dictionary<string, myStruct>.
  • Структуры являются типами значений, а классы — ссылочными типами.Structs are value types, unlike classes, which are reference types.
  • В отличие от классов структуры можно создавать без использования оператора new.Unlike classes, structs can be instantiated without using a new operator.
  • Структуры могут объявлять конструкторы, имеющие параметры.Structs can declare constructors that have parameters.
  • Структура не может наследовать от другой структуры или класса и не может быть базовой для класса.A struct cannot inherit from another struct or class, and it cannot be the base of a class. Все структуры наследуют непосредственно от ValueType, который наследует от Object.All structs inherit directly from ValueType, which inherits from Object.
  • Структуры могут реализовывать интерфейсы.A struct can implement interfaces.
  • Структура не может быть null, а переменная структуры не может быть назначена null, если переменная не объявлена как тип, допускающий значение NULL.A struct cannot be null, and a struct variable cannot be assigned null unless the variable is declared as a nullable value type.

См. такжеSee also

docs.microsoft.com

typedef — КРАТКИЙ ОБЗОР. Язык Си

typedef — КРАТКИЙ ОБЗОР

     Функция typedef позволяет нам создать свое собственное имя типа. Это напоминает директиву #define, но со следующими тремя изменениями:

1. В отличие от #define функция typedef дает символические имена, но ограничивается только типами данных.

2. Функция typedef выполняется компилятором, а не препроцессором.

3. В своих пределах функция typedef более гибка, чем #define.

     Посмотрим, как она работает. Предположим, вы хотите использовать термин real для чисел типа float. Тогда вы определяете термин real, как если бы он был переменной типа float, и перед его определением ставите ключевое слово typedef:

typedef float real;

С этого момента вы можете использовать real для определения переменных:

real х, у[25], *рr;

Область действия такого определения зависит от расположения оператора typedef. Если определение находится внутри функции, то область действия локальна и ограничена этой функцией. Если определение расположено вне функции, то область действия глобальна.

Часто в этих определениях используются прописные буквы, чтобы напомнить пользователю, что имя типа является на самом деле символической аббревиатурой:

typedef float REAL;

В последнем примере можно было бы применить директиву #define. А здесь это делать нельзя:

typedef char *STRING;

Без ключевого слова typedef оператор определял бы STRING как указатель на тип char. С ключевым словом оператор делает STRING идентификатором указателей на тип char. Так,

STRING name, sign;

означает

char *name, *sign;

Мы можем использовать typedef и для структур. Вот пример:

typedef struct COMPLEX {

float real;

float imag; };

Кроме того, можно использовать тип COMPLEX для представления комплексных чисел.

     Одна из причин использования typedef заключается в создании удобных, распознаваемых имен для часто встречающихся типов. Например, многие пользователи предпочитают применять STRING или его эквивалент, как это мы делали выше. Вторая причина: имена typedef часто используются для сложных типов. Например, описание

typedef char *FRPTC ( ) [5];

приводит к тому, что FRPTC объявляет тип, являющийся функцией, которая возвращает указатель на пятиэлементный массив типа char. (См. «Причудливые описания».)

Третья причина использования typedef заключается в том, чтобы сделать программы более мобильными. Предположим, например, что вашей программе нужно использовать 16-разрядные числа. В некоторых системах это был бы тип short, в других же он может быть типом int. Если вы использовали в ваших описаниях short или int, то должны изменить все описания, когда перейдете от одной системы к другой. Вместо этого сделайте следующее, В файле директивы #include есть такое определение:

typedef short TWOBYTE;

Используйте TWOBYTE в ваших программах для переменных типа short, которые должны быть 16-разрядными. Тогда если вы перемешаете программу туда, где необходимо использовать тип int, то следует только изменить одно определение в вашем файле директивы #include:

typedef int TWOBYTE;

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

Поделитесь на страничке

Следующая глава >

it.wikireading.ru

Оставить комментарий

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

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