tags:

views:

3149

answers:

9

If I have an array of a fixed size depending on how it is defined and used, I typically use one of two ways to reference it.

Array type 1: Since it is a fixed size based on a define, I just use that define in all my loops referencing it.

#define MAXPLAYERS 4

int playerscores[MAXPLAYERS];

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

Array type 2: Since this array can grow as items are added to it, I use the sizeof to count the number of entries in it. The size would be converted to a constant by the compiler so there shouldn't be any runtime penalty to doing it this way.

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
 }

Is there a more elegant solution to handling processing of arrays without going past the end or stopping too early. Thoughts? Comments?

+7  A: 

This will work for both of your cases, regardless of array element type:

#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] */
}
Alex B
And, this will work even in the first case where it's just a fixed size array of int. By using an ARRAY_COUNT macro, you avoid the problem where you change the size of the array (by using a different #define) but forget to change all the loops.
Greg Hewgill
Be very careful not to pass to ARRAY_COUNT a pointer, because then you will get a wrong answer.
freespace
+4  A: 

In C++ just use the vector class.

If you can't for some reason then there are macro implementations of what you want. See this answer for a set of macros from winnt.h that work in C and even more safely in C++:

http://stackoverflow.com/questions/95500/can-this-macro-be-converted-to-a-function#95714

Michael Burr
+4  A: 

Use the _countof macro of stdlib.h

From this MSDN article:

// 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
}
Brian R. Bondy
I like that name better than the name(s) I've used in the past...
Michael Burr
of course, the leading underscore tells you exactly how portable it is. If you only need to target a single compiler, that's fine, but it can bite you later if you need to port to other compilers (even if not other platforms).
Tanktalus
Actually - I was thinking of simply stealing it to put in my existing macro. I like the symmetry with sizeof().
Michael Burr
That's a nice one - not really really portable, but still kind of
xtofl
+1  A: 

For C, I would suggest realloc to bring in new variables dynamically. If you are doing something statically, I would suggest keeping with the #define. I'm not sure if I'd call that best practice, but, today, that's how I would practice it.

The C++ best practice is to use stl::vector. A reference here

Paul Nathan
+1  A: 

I almost always use a wrapper class (MFC CArray, stl vector, etc.) unless there's a specific reason not too. There's not much overhead, you get a lot of debug checking, you can dynamically size, getting the size is easy, etc.

Nick
+2  A: 

It's fairly common to see C code like

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

Often you can find at least one field which is never 0/NULL for normal elements, and if not, you can use some other special END value.

In code that I write, anything that involves compiletime-sized arrays is done with a macro like ARRAY_COUNT from Checkers's answer, and runtime-sized arrays always come with a size counter, in a struct with the array.

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

The length field allows for easy batched resizing.

ephemient
+1  A: 

For C++, using std::vector

There's no real point in using a C-array. The std::vector has (almost) the same performance as a C array, and it will:

  • grow as needed
  • know its size
  • verify you are really accessing the right memory (i.e. it could throw an exception if you go beyond its bounds)

And this is not even considering the generic algorithm associated with the std::vector.

Now, using C

You can write it somewhat better at least in two ways. First, replacing a define with a true constant variable:

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

int playerscores[MAXPLAYERS];

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

Using a true variable will offer your somewhat more type safety, and won't pollute the global scope. To minimize dependancies, you can even declare the variables in the header, and define them in a source:

/* 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
}

This way, you'll be able to change the size of the array in one source, without needing recompilation of all the sources using it. The downside is that MAXPLAYERS is not anymore known at compile time (but, is this really a downside?)

Note that your second type of array cannot grow dynamically. The sizeof is (at least in C++) evaluated at compile time. For growing arrays, malloc/realloc/free is the way to go in C, and std::vector (or any other generic STL container) is the way to go in C++.

paercebal
+1  A: 

Make sure to also read this question's answers - many solutions to the array size problem that are portable.

I especially like the _countof (cfr. Brian R. Bondy's answer) - Pulitzer for the inventor of that name!

xtofl
A: 

Addition to the answers so far, if you are using T[] arrays in C++: Use template argument deduction to deduce the array size. It's much safer:

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

Your sizeof(mystruct)/sizeof(MYSTRUCT_DEF) expression fails quite silently if you change mystruct to a malloc'ed/new'ed MYSTRUCT_DEF*. sizeof(mystruct) then becomes sizeof(MYSTRUCT_DEF*), which often is smaller than sizeof(MYSTRUCT_DEF), and you'll have a loopcount of 0. It will seem like the code is simply not executed, which can be quite perplexing. The template declaration above will give you a clear compiler error instead ("mystruct is not an array")

MSalters