Лучше ли оборачивать массивы и их переменные длины в структуру в C? - PullRequest
5 голосов
/ 19 марта 2009

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

Это всегда был мой первый вопрос относительно массивов, так как их легко испортить.

Является ли обычной практикой объединять массив и связанную с ним переменную, содержащую его длину в структуре?

Я никогда не видел этого в книгах, и обычно они всегда держат их отдельно или используют что-то вроде sizeof (array [] / array [1]).

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

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

Cheers, Кай.

Ответы [ 11 ]

5 голосов
/ 19 марта 2009

Да, это отличная практика в C. Это вполне логично заключить связанные значения в содержащую структуру.

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

3 голосов
/ 19 марта 2009

Есть три способа.

  1. Для статического массива (не выделенного динамически и не переданного в качестве указателя) размер известен во время компиляции, поэтому вы можете использовать оператор sizeof, например: sizeof(array)/sizeof(array[0])
  2. Использовать терминатор (специальное значение для последнего элемента массива, который нельзя использовать в качестве обычного значения массива), например строки с нулевым символом в конце
  3. Использовать отдельное значение, либо как член структуры, либо как независимую переменную. Это на самом деле не имеет значения, потому что все стандартные функции, которые работают с массивами, принимают отдельную переменную размера, однако объединение указателя массива и размера в одну структуру повысит читабельность кода. Я предлагаю использовать более чистый интерфейс для ваших собственных функций. Обратите внимание, что если вы передадите свою структуру по значению, вызываемая функция сможет изменить массив, но не переменную размера, поэтому лучше будет передать указатель структуры.
3 голосов
/ 19 марта 2009

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

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

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

int array[4711];
int i;

for(i = 0; i < sizeof array / sizeof *array; i++)
{
  /* Do stuff with each element. */
}

Помните, что sizeof не является функцией, скобки не всегда нужны.

РЕДАКТИРОВАТЬ : Один из реальных примеров обертки, точно такой, который вы описываете, - это тип GArray , предоставляемый glib . Видимая пользователем часть объявления - это именно то, что вы описываете:

typedef struct {
  gchar *data;
  guint len;
} GArray;

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

2 голосов
/ 19 марта 2009

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

2 голосов
/ 19 марта 2009

Для публичного API я бы пошел с разделенным массивом и значением размера. Вот как это делается в большинстве (если не во всех) библиотеках, которые я знаю. Как вы справляетесь с этим внутри, это полностью зависит от вас. Так что использование структуры плюс несколько вспомогательных функций / макросов, которые выполняют сложные задачи для вас, является хорошей идеей. Меня всегда мучает головная боль, когда я переосмысливаю, как вставить элемент или удалить его, так что это хороший источник проблем. Решение этого разового и общего поможет вам получить ошибки с самого начала. Хорошая реализация для динамических и универсальных массивов: kvec .

1 голос
/ 19 марта 2009

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

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

struct Foo {
  int *myarray;
  int size;
};

Таким образом, вы можете передавать эту структуру по значению, но вы действительно передаете указатель на int (указатель на массив) и int (размер массива).

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

1 голос
/ 19 марта 2009

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

1 голос
/ 19 марта 2009

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

0 голосов
/ 14 декабря 2014

Я думаю, что эта часть вашего вопроса задом наперед:

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

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

Возможно ли передать массив по значению в рекурсивную функцию?

А вот еще об особом поведении массивов:

http://denniskubes.com/2012/08/20/is-c-pass-by-value-or-reference/

0 голосов
/ 19 марта 2009

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

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

...