7

Some example code to start the question:

 #define FOO_COUNT 5

 static const char *foo[] = {
       "123",
       "456",
       "789",
       "987",
       "654"
 };

The way this would normally be iterated over, as for one example, is the following:

int i = FOO_COUNT;
while (--i >= 0) {
 printf("%s\n", foo[i]);

Is there anyway to do the above without explicitly having the human count the number 5? In the future I might add/remove elements and forget to update the size of the array, thus breaking my app.

James McNellis
  • 348,265
  • 75
  • 913
  • 977
randombits
  • 47,058
  • 76
  • 251
  • 433

8 Answers8

17
int i = sizeof(foo)/sizeof(foo[0]);
qrdl
  • 34,062
  • 14
  • 56
  • 86
aJ.
  • 34,624
  • 22
  • 86
  • 128
  • would this still work if the character pointers in foo pointed to character strings of variable size? – randombits Dec 28 '09 at 14:17
  • so if any element exceeds 15 characters, this check will fail? probably better to use the NULL sentinel then? thanks again. – randombits Dec 28 '09 at 14:21
  • It will not fail. sizeof[foo[0]) ==> sizeof(char*); Number of character does not matter. – aJ. Dec 28 '09 at 14:23
  • ok, wasn't sure what you're referring to by 15 char limit in your comment above – randombits Dec 28 '09 at 14:24
  • 1
    The 15 char limit part of the comment means that he was just adding extra characters to meet the 15 character minimum for comments in Stack Overflow. His answer as just "yes", but Stack Overflow doesn't like comments that short. – Brian Campbell Dec 28 '09 at 14:32
  • Note that `i` above is a compile-time constant, so you can do: `#define N sizeof foo / sizeof foo[0]` followed by `int data[N];`. That is valid even for C89. Also, the parentheses are not required if `sizeof` operator is used with a value instead of a type. – Alok Singhal Dec 28 '09 at 15:39
  • The first sentence above should read, "...the RHS in the definition of `i` is a compile-time constant". – Alok Singhal Dec 28 '09 at 15:41
  • Sorry for the confusion. Thanks @Brian Campbell for clarifying what I meant. – aJ. Dec 28 '09 at 16:55
12

Use a sentinel at the end, such as NULL:

static const char *foo[] = {
       "123",
       "456",
       "789",
       "987",
       "654",
       NULL
};

for (char *it = foo[0]; it != NULL; it++)
{
        ...
}
Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
2

The usual way to do this is to end the array with NULL and iterate until you hit that.

diciu
  • 29,133
  • 4
  • 51
  • 68
2

Yes.

int i = sizeof(foo) / sizeof(char*);

Note: This only applies to statically allocated arrays. It will not work for malloced or newed arrays.

rui
  • 11,015
  • 7
  • 46
  • 64
2
size_t i = sizeof foo / sizeof *foo; // or sizeof foo / sizeof foo[0]

This divides the total number of bytes in the foo array (sizeof foo) by the number of bytes in a single element (sizeof *foo), giving the number of elements in the array.

John Bode
  • 119,563
  • 19
  • 122
  • 198
2

There's also another method in C99, especially if you want named indexes, allowing for instance localization and such.

enum STRINGS {
  STR_THING1,
  STR_THING2,
  STR_THING3,
  STR_THING4,
  STR_WHATEVER,

  STR_MAX         /* Always put this one at the end, as the counting starts at 0 */
                  /* this one will be defined as the number of elements */
}     

static const char *foo[STR_MAX] = {
  [STR_THING1]   = "123", 
  [STR_THING2]   = "456",
  [STR_THING3]   = "789",
  [STR_THING4]   = "987",
  [STR_WHATEVER] = "OR Something else",
};

By using named initializer the program still is correct even if an enum value changes.

for (i = STR_THING1; i<STR_MAX; i++)
  puts(foo[i]);

or anywhere in the program with the named index

  printf("thing2 is %s\n", foo[STR_THING3]);

This technique can be used to simulate ressource bundles. Declare one enum and several string arrays with language variants and use a pointer in the rest of the program. Simple and fast (especially on 64bit machines where getting the address of a constant (string) can be relatively costly. Edit: the sizeof foo/sizeof *foo technique still works with this.

Patrick Schlüter
  • 11,394
  • 1
  • 43
  • 48
0

Yes.

sizeof(foo)/sizeof(char*)

is 5.

Pavel Radzivilovsky
  • 18,794
  • 5
  • 57
  • 67
0

For some application, try and Catch would work.

iampat
  • 1,072
  • 1
  • 12
  • 23