Лучшие практики для обработки массивов переменного размера в c / c ++?

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

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

#define MAXPLAYERS 4

int playerscores[MAXPLAYERS];

for(i=0;i<MAXPLAYERS;++i)
{
.... do something with each player
}

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

typedef struct
{
    fields....
}MYSTRUCT_DEF;

MYSTRUCT_DEF mystruct[]={
   {entry 1},
   {entry 2},
   {entry 3...n}
   };

 for(i=0;i<(sizeof(mystruct)/sizeof(MYSTRUCT_DEF));++i)
 {
 ..... do something with each entry
 }

Есть ли более элегантное решение для обработки массивов, не доходя до конца и не останавливаясь слишком рано. Мысли? Комментарии?


person KPexEA    schedule 10.10.2008    source источник
comment
Обратите внимание, что это не массив переменного размера в обычном понимании этого термина (где количество элементов меняется во время выполнения).   -  person Michael Burr    schedule 10.10.2008
comment
Если вы используете C ++, вам действительно следует использовать один из контейнеров STL. В противном случае удалите тег C ++. Хотя C ++ и C полупортативны, это не одно и то же.   -  person mdec    schedule 10.10.2008


Ответы (9)


Это будет работать в обоих случаях, независимо от типа элемента массива:

#define ARRAY_COUNT(x) (sizeof(x)/sizeof((x)[0]))

...

struct foo arr[100];
...

for (i = 0; i < ARRAY_COUNT(arr); ++i) {
    /* do stuff to arr[i] */
}
person Alex B    schedule 10.10.2008
comment
И это будет работать даже в первом случае, когда это просто массив фиксированного размера int. Используя макрос ARRAY_COUNT, вы избегаете проблемы, когда вы изменяете размер массива (используя другой #define), но забываете изменить все циклы. - person Greg Hewgill; 10.10.2008
comment
Будьте очень осторожны, чтобы не передать ARRAY_COUNT указатель, потому что тогда вы получите неправильный ответ. - person freespace; 10.10.2008

В C ++ просто используйте векторный класс.

Если по какой-то причине вы не можете этого сделать, то существуют макросы, которые вам нужны. См. Этот ответ, чтобы узнать о наборе макросов из winnt.h, которые работают на C и даже безопаснее на C ++:

Можно ли преобразовать этот макрос в функцию?

person Michael Burr    schedule 10.10.2008

Используйте макрос _countof из stdlib.h

Из этой статьи MSDN:

// crt_countof.cpp
#define _UNICODE
#include <stdio.h>
#include <stdlib.h>
#include <tchar.h>
int main( void )
{
   _TCHAR arr[20], *p;
   printf( "sizeof(arr) = %d bytes\n", sizeof(arr) );
   printf( "_countof(arr) = %d elements\n", _countof(arr) );
   // In C++, the following line would generate a compile-time error:
   // printf( "%d\n", _countof(p) ); // error C2784 (because p is a pointer)

   _tcscpy_s( arr, _countof(arr), _T("a string") );
   // unlike sizeof, _countof works here for both narrow- and wide-character strings
}
person Brian R. Bondy    schedule 10.10.2008
comment
конечно, подчеркивание в начале говорит вам, насколько он портативен. Если вам нужно настроить таргетинг только на один компилятор, это нормально, но он может укусить вас позже, если вам нужно будет выполнить перенос на другие компиляторы (даже если не на другие платформы). - person Tanktalus; 10.10.2008
comment
На самом деле - я думал просто украсть его, чтобы вставить в свой существующий макрос. Мне нравится симметрия с sizeof (). - person Michael Burr; 10.10.2008
comment
Хороший - не совсем портативный, но все же в некотором роде - person xtofl; 10.10.2008

Довольно часто можно увидеть код C вроде

struct foo {
    ...  /* fields */
};
struct foo array[] = {
    { ... }, /* item 1 */
    { ... }, /* item 2 */
    ...,
    { 0 } /* terminator */
};
for (i = 0; array[i].some_field; i++) {
    ...
}

Часто вы можете найти по крайней мере одно поле, которое никогда не _2 _ / _ 3_ для обычных элементов, а если нет, вы можете использовать другое специальное значение END.

В коде, который я пишу, все, что связано с массивами размера времени компиляции, выполняется с помощью макроса, такого как ARRAY_COUNT из ответа Checkers, а массивы размера времени выполнения всегда поставляются со счетчиком размера в структуре с массивом.

struct array_of_stuff {
    struct stuff *array;
    int count;   /* number of used elements */
    int length;  /* number of elements allocated */
};

Поле length позволяет легко изменять размер.

person ephemient    schedule 10.10.2008

Для C я бы предложил realloc, чтобы динамически вводить новые переменные. Если вы делаете что-то статически, я бы посоветовал придерживаться #define. Я не уверен, что назвал бы это лучшей практикой, но сегодня я бы практиковал это именно так.

Лучшая практика C ++ - использовать stl :: vector. Ссылка здесь

person Paul Nathan    schedule 10.10.2008

Я почти всегда использую класс-оболочку (MFC CArray, stl vector и т. Д.), Если нет особой причины. Там не так много накладных расходов, вы получаете много проверок отладки, вы можете динамически изменять размер, легко получить размер и т. Д.

person Nick    schedule 10.10.2008

Для C ++ с использованием std :: vector

Нет смысла использовать C-массив. Std :: vector имеет (почти) ту же производительность, что и массив C, и он будет:

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

И это даже без учета общего алгоритма, связанного с std :: vector.

Теперь, используя C

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

// #define MAXPLAYERS 4
const unsigned int MAXPLAYERS = 4 ;

int playerscores[MAXPLAYERS];

for(i=0;i<MAXPLAYERS;++i)
{
.... do something with each player
}

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

/* header.h */
extern const unsigned int MAXPLAYERS ;
extern int playerscores[] ;

/* source.c */
const unsigned int MAXPLAYERS = 4
int playerscores[MAXPLAYERS];

/* another_source.c */
#include "header.h"

for(i=0;i<MAXPLAYERS;++i)
{
.... do something with each player
}

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

Обратите внимание, что ваш второй тип массива не может расти динамически. Размер sizeof (по крайней мере, в C ++) оценивается во время компиляции. Для растущих массивов malloc / realloc / free - это путь в C, а std :: vector (или любой другой общий контейнер STL) - это способ пойти в C ++.

person paercebal    schedule 10.10.2008

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

Мне особенно нравится _countof (ср. Ответ Брайана Р. Бонди) - Пулитцера для изобретателя с таким именем!

person xtofl    schedule 10.10.2008

Дополнение к ответам на данный момент, если вы используете массивы T [] в C ++: используйте вывод аргументов шаблона, чтобы определить размер массива. Намного безопаснее:

template<int N> void for_all_objects(MYSTRUCT_DEF[N] myobjects)

Ваше sizeof(mystruct)/sizeof(MYSTRUCT_DEF) выражение завершится неудачно, если вы измените mystruct на malloc'ed / new'ed MYSTRUCT_DEF*. Затем sizeof(mystruct) становится sizeof(MYSTRUCT_DEF*), что часто меньше sizeof(MYSTRUCT_DEF), и у вас будет счетчик циклов 0. Может показаться, что код просто не выполняется, что может вызывать недоумение. Объявление шаблона выше выдаст вам явную ошибку компилятора («mystruct не является массивом»).

person MSalters    schedule 10.10.2008