#c #pointers #struct #datadesign
#c #указатели #структура #datadesign
Вопрос:
При проектировании структур, содержащих текстовые данные, я использовал два основных подхода, проиллюстрированных ниже:
typedef struct {
STRING address1;
STRING address2;
STRING city;
STRING state;
STRING zip;
} ADDRESS;
typedef struct {
STRING* address1;
STRING* address2;
STRING* city;
STRING* state;
STRING* zip;
} ADDRESS;
где STRING — это некоторый тип хранения строк переменной длины. Преимущество версии с указателем заключается в том, что я могу хранить значение NULL, указывающее на отсутствие данных. Например, address2 может быть не предоставлен для некоторых адресов. В типе со встроенными строками я должен использовать «пустую» строку, то есть строку, имеющую длину 0.
С указателями (возможно) больше нагрузки на код, потому что я должен проверять каждый элемент на наличие NULL перед использованием. Однако преимущество не так велико, потому что обычно встроенная версия тоже должна быть проверена. Например, если я печатаю адрес, я должен проверить наличие строки нулевой длины и пропустить эту строку. С помощью указателей пользователь может фактически указать, что ему нужно «пустое» вместо отсутствующего значения, хотя трудно увидеть применение для этого.
При создании или освобождении структуры указатели добавляют множество дополнительных шагов. Мой инстинкт состоит в том, чтобы стандартизировать встроенный стиль, чтобы сохранить эти шаги, но я обеспокоен тем, что может быть скрытая ошибка. Это необоснованный страх, или я должен использовать указатели по какой-то веской причине?
Обратите внимание, что использование памяти является проблемой, но она довольно незначительна. Версия указателя занимает немного больше памяти, потому что я храню указатели на структуры в дополнение к структурам. Но каждая строковая структура занимает в среднем, может быть, 40 байт, поэтому, если я храню 4-байтовые указатели, то версия указателя стоит, возможно, на 10% больше памяти, что не является значительным. Наличие возможных нулевых указателей не экономит значительную память, поскольку большинство полей заполнены.
Вопрос об АДРЕСЕ, а не О СТРОКЕ
Некоторые респонденты, похоже, сбиты с толку и думают, что я спрашиваю о глобальных компромиссах, например, о том, как свести к минимуму мой общий объем работы. Это не тот случай. Я спрашиваю о том, как создать АДРЕС, а не СТРОКУ. Члены address могут иметь фиксированные массивы, а в других случаях нет. Для целей моего вопроса меня не беспокоят последствия для контейнера.
Я уже заявлял, что единственная проблема, которую я вижу, заключается в том, что использование указателей требует больше времени, но я получаю преимущество от возможности хранить значение NULL. Однако, как я уже сказал, это преимущество не кажется значительным, но, возможно, это по какой-то причине. В этом суть моего вопроса: есть ли какая-то скрытая выгода от такой гибкости, которую я не вижу и хотел бы иметь позже.
Если вы не понимаете вопроса, пожалуйста, прочтите предварительный ответ, который я написал сам ниже (после некоторых дополнительных размышлений), чтобы увидеть, какой ответ я ищу.
Комментарии:
1. Я предполагаю, что
STRING
этоstruct
элемент с элементами для отслеживания того, сколько памяти было выделено / использовано, и указатель на указанную память. Но оба ответа предполагают, чтоSTRING
это atypedef
для массива фиксированной длины. Возможно, вы захотите прояснить это.2. Вам нужно показать нам определение
STRING
структуры.3. Практический ответ на этот вопрос зависит от того, как
STRING
определена структура.4. Иногда я использую структуры, которые имеют массивы фиксированной длины, в других случаях я использую встроенные структуры, которые имеют указатели на массивы, связанные с malloced. Предположим, что СТРОКА может иметь либо. Я спрашиваю о дизайне контейнера здесь, а не о содержимом.
5. @MarkBenningfield Почему?
Ответ №1:
Компромиссы по использованию памяти и сокращению mallocs
Похоже, что компромиссы сосредоточены вокруг двух вопросов: 1) Насколько ценна память? и 2) Имеет ли значение, что для строк выделяется фиксированный объем памяти, ограничивающий длины, которые будут храниться в каждом поле?
Если память важнее всего остального, то версия указателя, вероятно, выигрывает. Если предпочтительна предсказуемость использования хранилища и предотвращение mallocs, а ограничение длины имен некоторой фиксированной величиной приемлемо, тогда версия с фиксированной длиной может быть победителем.
Комментарии:
1. Проблемы с памятью, я думаю, довольно незначительны. Я добавлю комментарий по этому поводу к своему вопросу.
2. Обратите внимание, что версия указателя не обязательно экономит память, поскольку экономия от случайного нулевого указателя компенсируется стоимостью хранения самих указателей. Так, например, в моих данных случаях есть 5 полей, поэтому я должен хранить 5 указателей, которые могут стоить 5 x 8 = 40 дополнительных байт по сравнению со встроенной версией.
3. Пока вы не расскажете, как реализована STRING, мы похожи на компанию друзей, играющих в Dungeons and Dragons, ожидающих, что Мастер Подземелий просветит нас относительно истинной природы мира, в котором мы бродим. 🙂
4. Хорошая аналогия, но я рекомендую прочитать вопрос более внимательно. Если вы считаете, что содержимое STRING повлияет на ответ, значит, вы не поняли вопрос.
5. Я склонен не соглашаться. Два других участника с репутацией намного выше моей сказали то же самое. На самом деле, оба они сказали, что я сделал «предположения», которые были неверны в моем ответе.
Ответ №2:
Одна из проблем со встроенным стилем заключается в том, что СТРОКА должна быть определена как что-то вроде char[MAX_CHAR 1]
where MAX_CHAR
— пессимистическая максимальная длина для заданных полей. Стиль указателя позволяет выделить правильный объем памяти. Недостатком, как вы упомянули, является гораздо более высокая когнитивная нагрузка на управление вашей структурой.
Комментарии:
1. «… СТРОКА должна быть определена как
char[MAX_CHAR 1]
…» : Не обязательно, пока мы абсолютно не знаем, как реализован строковый тип.2. Я хочу сказать, что каждый строковый элемент должен иметь пессимистическую максимальную длину.
3. Почему? Мы понятия не имеем, как
STRING
это реализовано.4. В большинстве случаев строковая структура содержит указатели внутри нее, поэтому она имеет гибкую структуру памяти, но это не имеет отношения к вопросу. Мой вопрос вращается вокруг того, как спроектировать контейнер.
5. Хорошо, я неправильно понял вопрос ОП
Ответ №3:
Я рассматривал это более глубоко, и я думаю, что в большинстве случаев указатели необходимы, потому что важно различать пустое и отсутствующее. Причина этого заключается в том, что недостающие данные необходимы, когда входные данные неверны, повреждены или пропущены. Например, давайте представим, что при чтении из файла файл поврежден, поэтому поле, такое как почтовый индекс, нечитаемо. В этом случае данные «отсутствуют», а указатель должен быть нулевым. С другой стороны, давайте представим, что у места нет почтового индекса, тогда оно «пустое». Таким образом, NULL означает, что пользователь еще не предоставил информацию, но blank означает, что пользователь предоставил информацию, и нет ни одного типа, о котором идет речь.
Итак, чтобы еще раз проиллюстрировать важность использования указателя, представьте, что сложная структура заполняется с течением времени разными асинхронными шагами. Здесь нам нужно знать, какие поля были прочитаны, а какие нет. Если мы не используем указатели (или не добавляем дополнительные метаданные), у нас нет способа определить разницу между полем, на которое был дан ответ, и полем, для которого ответ «нет». Представьте себе, что система запрашивает пользователя: «Какой почтовый индекс?». Пользователь говорит: «У этого места нет почтового индекса». Затем через 5 минут система снова спрашивает: «Какой почтовый индекс?». Этот вариант использования ясно показывает, что в большинстве случаев нам нужны указатели.
В свете этого единственная ситуация, когда я должен использовать встроенные структуры, — это когда структура контейнера гарантированно будет иметь полный набор данных всякий раз, когда она создается.
Комментарии:
1. Различие между «пустым» и «отсутствующим» должно быть в самой СТРОКЕ. Таким образом, СТРОКА (сама по себе) может быть передана функции без потери информации о том, присутствует ли строка, пуста или отсутствует. Другими словами, a
STRING
должен быть автономным. Использует ли контейнер указатели на строки или встроенные строки, совершенно не связано с проблемой отслеживания отсутствующей строки.2. @user3386109 Я не хочу добавлять такого рода метаданные в свой строковый контейнер.