Два способа использования одноэлементных структур в Cи с пользой
6К открытий6К показов
В структурах языка Си очень много странностей, но, по большей части, они предсказуемы, полезны и понятны.
Для тех, кто не знаком с Cи: структуры представляют собой наборы данных. Примером их использования является точка на декартовой плоскости:
Обычно такие конструкции используют для связывания двух или более элементов данных. Как и предполагает название статьи, далее будет представлено, почему вы, возможно, захотите использовать структуру с единственным элементом.
Массив с защищенным типом информации
По сравнению со структурами, с массивами языка Cи трудно работать, потому что существует много неожиданных случаев: например, начинающим трудно отличить их от указателей (которые уже достаточно трудно понять самостоятельно). Наиболее проблематичным в массивах языка Cи является то, что они отбрасывают информацию о своем размере, как только на них ссылаются в функции или модуле (оформленном специальным образом функционально самостоятельном, в том числе по отношению к компиляции или загрузке, блоке кода, взаимодействие с которым осуществляется через его внешний интерфейс).
Рассмотрим несколько примеров использования оператора sizeof в языке Си. Во-первых, познакомимся с обычным поведением массивов. В этом примере мы выделим память под массив и будем ссылаться на него по имени, под которым он был объявлен.
Этот пример при запуске выводит на консоль, как и ожидалось, значение «10». Если же заменить функцию direct_reference новой функцией под названием indirect_reference, то результат компиляции будет совершенно другим.
На 64-разрядной машине этот пример выводит значение «8», что является, вероятно, размером указателя в системе. Обратите внимание, что sizeof рассматривает referenced_array как указатель, хотя мы явно определили его тип.
К счастью, мы можем использовать структуру с одним элементом, чтобы сохранить информацию о размере массива, несмотря на ограничения при ссылке! Например:
В этом примере, мы определяем структуру с одним полем — массивом. Интересно, что если мы будем в любой части кода использовать ссылку на структуру и применим оператор sizeof к массиву внутри конструкции, его размер не изменится. Структура сохраняет всю информацию о размере своих элементов. Выполнение этого кода, как и ожидалось, выводит значение «10».
То же самое будет происходить и при использовании внешних ссылок (при использовании extern).
Примечание: Тот же самый эффект сохранения информации о размере может быть достигнут при использовании оператора typedef вместо структуры.
Предотвращение нежелательного приведения типов
Язык Cи обеспечивает механизм, с помощью которого можно переименовать типы. Ключевое слово typedef дает нам возможность создавать такие типы, как count_t, который на самом деле является типом int32_t. Это хорошо для семантики, но не дает дополнительной надежности во время компиляции.
И ничто не помешает вам, скажем, определить тип с именем seconds и другой тип с именем milliseconds, а затем случайно сложить их! В обоих случаях Си воспримет оба типа как целочисленные и выполнит операцию сложения! Это, конечно же, приведет к бессмысленному результату. Если добавить 5 миллисекунд к 10 секундам, вы, в конечном счете, получите 15 секунд или миллисекунд, что, естественно, не является правильным и желаемым выполнением кода.
Как вы уже догадались, мы можем использовать одноэлементную структуру, чтобы добиться дополнительной надежности при компиляции, чего typedef сам по себе не обеспечивает! В следующем примере мы видим ситуацию, когда миллисекунды и секунды ошибочно суммируются.
Мы можем добиться немного более надежного выполнения, сделав следующее. Во-первых, вместо того чтобы использовать typedef, мы можем указать значения в структуре! Во-вторых, мы можем определить функцию, которая выполняет сложение секунд. Далее представлен более полный пример.
В этом примере мы передаем целочисленные значения, тип которых определен в структурах, функции, осуществляющей суммирование чисел. Вы заметите, что этот пример не компилируется! Компилятор может выдать следующую ошибку:
error: passing ‘struct milliseconds’ to parameter of incompatible type ‘struct seconds’
struct seconds result = add_seconds(x, y);
Целью наших действий было создание условий, при которых компилятор может более активно искать ошибки. Используя дополнительные структурные типы, мы сокращаем количество ошибок, которые совершаем при смешивании разных типов, которые компилятор рассматривает как операционно-совместимые.
6К открытий6К показов