ANSI C: Declare a pointer of type "array of string"

Viewed 133

I have a structure like this

struct {
  int    id;
  char   str00[10];
  char   str01[10];
  char   str03[10];
  char   str04[10];
  ...
}   myStructure;

All strXX have the same size. I would like to access them via an array (strArray), like this :

strcpy(strArray[i], strValue);

How to declare strArray ?

I used this:

char   (*strArray)[][10] = (void *)&myStructure.str00;

It's working, but I have to code the strcpy like this

strcpy((*strArray)[i], strValue);

... and I don't like this :-)

Is there another way to declare strArray ?

Thanks for your ideas and help.

2 Answers

You almost had it, the correct pointer type is simply char (*ptr)[10].

Naively, you could use this pointer type to iterate over the struct members, but doing so would invoke undefined behavior. Because strictly speaking, a pointer can only point at single item or an array, and if we use pointer arithmetic to go beyond that single item/array, we invoke undefined behavior.

I'll include an example still, for the sake of demonstrating array pointer arithmetic:

// BAD CODE, it relies on undefined behavior!

#include <stdio.h>
#include <string.h>

typedef struct {
  int    id;
  char   str00[10];
  char   str01[10];
  char   str02[10];
  char   str03[10];
} myStructure;


int main(void)
{
  myStructure ms = { 0 };
  char (*ptr)[10] = &ms.str00;

  for(size_t i=0; i<4; i++)
  {
    strcpy(ptr[i], "hello ");
    strcat(ptr[i], (char[]){i+'0', '\0'});
  }

  puts(ms.str00);
  puts(ms.str01);
  puts(ms.str02);
  puts(ms.str03);

  return 0;
}

The proper solution is to instead use a union, so that you can access the members either individually, or as an array:

typedef union {
  struct              // anonymous struct, requires a standard C compiler
  {
    char str00[10];
    char str01[10];
    char str02[10];
    char str03[10];
  };
  char array[4][10];
} str_t;

typedef struct {
  int    id;
  str_t  str;
} myStructure;


strcpy(ms.str.array[i], ...); // access as array
puts(ms.str.str00); // access as individual item

The cleanest way to define strArray as requested would be to make it an array of pointers to the (first elements of the) arrays in myStructure:

char *strArray[] = { myStructure.str00, myStructure.str01, myStructure.str03, myStructure.Str04, … };

With this definition, strArray[i] is initialized to the corresponding member of the structure, such as myStructure.str01. Note that myStructure.str01 will be automatically converted to a pointer to its first element, so strArray[i] is a pointer to the first char in one of the arrays.

Then strArray[i][j] is char j of array i.

(Incidentally, you skip str02 in your sample code. I do not know why but have retained that in the code above.)

An alternative method would be to use a union, which can be done in various ways, one of which is:

struct
{
    int    id;
    union
    {
        struct
        {
            char   str00[10];
            char   str01[10];
            char   str03[10];
            char   str04[10];
            ...
        };
        char strArray[number of arrays][10];
    };
} myStructure;

That is generally a poor design, as it is needlessly confusing. (While this has a technical possibility of failure due to padding between the individually defined arrays, assertions can be used to ensure this does not occur, or rather to detect when it does.)

Most often, we would simply define the strings as an array of arrays:

struct
{
    int id;
    char str[number of arrays][10];
} my Structure;

Then the members would always be referred to by index, such as myStructure.str[1], and not by individual names, such as myStructure.str01.

Related