Лучшие практики для обработки массивов переменного размера в C / C ++? - PullRequest
4 голосов
/ 10 октября 2008

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

Тип массива 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
 }

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

Ответы [ 9 ]

7 голосов
/ 10 октября 2008

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

#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] */
}
5 голосов
/ 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
}
5 голосов
/ 10 октября 2008

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

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

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

2 голосов
/ 10 октября 2008

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

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

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

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

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

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

1 голос
/ 10 октября 2008

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

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

1 голос
/ 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
}

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

/* 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 ++.

1 голос
/ 10 октября 2008

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

1 голос
/ 10 октября 2008

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

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

0 голосов
/ 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 - это не массив»)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...