Rezaeimh7
Rezaeimh7

Reputation: 1545

How to use macro in C to convert loop counter variable to char?

I have some char array in my program. the number of those arrays is fixed and the name of each array started by a 'S' character and a number as follows:

char *s0="aaasa";
char *s1="bssbaabb";
char *s2="bbbssbaaaa";
.
.
char *sX="bcccbaabaab";
int num_arrays=X;

I decided to use macros to print content of arrays but no success is achieved( i am new to macros). So i have no idea how should i use Macros to print contents of the arrays in a for loop as follows:

#X(s,i) //what should be here?

for(int i=0;i<X;i++){
   printf("s%d: %s \n",i, X(s,i) );
}

Thanks.

Upvotes: 0

Views: 258

Answers (4)

Lundin
Lundin

Reputation: 214300

Sounds like what you are fishing for is so-called "X macros". Please note that these should be avoided as far as possible, since they make the code very hard to read.

The most correct solution here is to simply use an array. If you find yourself needing something else, the root cause is likely bad program design. Other posted answers show proper solutions with plain arrays. X macros should be the last resort for very special cases.

That being said, this is how you achieve this with X macros:

#include<stdio.h>

#define STRING_LIST \
X(0, "aaasa")       \
X(1, "bssbaabb")    \
X(2, "bbbssbaaaa")  \
X(3, "bcccbaabaab")

int main(void)
{
  // declare pointers s0 to s3:
  #define X(i, str) const char* s##i = str;
    STRING_LIST
  #undef X

  // print data by using pointers s0 to s3:
  #define X(i, str) printf("s%d: %s \n", i, s##i);
    STRING_LIST;
  #undef X

}

If you want to combine this with a loop/array, it is also possible, if we go "full C retard" and do something like this...

// Definitely NOT recommended practice but can be studied for learning purposes
#include<stdio.h>

#define STRING_LIST \
X(0, "aaasa")       \
X(1, "bssbaabb")    \
X(2, "bbbssbaaaa")  \
X(3, "bcccbaabaab")

// determine the size of the X macro list by using an enum:
typedef enum
{
  #define X(i, str) DUMMY_##i,
    STRING_LIST
  #undef X
  STRINGS_N
} strlist_size_t;


// declare union with both pointers s0 to s3 and an array:
typedef union
{
  struct // C11 anonymous struct
  {
    #define X(i, str) const char* s##i;
      STRING_LIST
    #undef X
  };

  const char* array [STRINGS_N];
} strlist_t;


int main(void)
{
  // ensure that the type punning is safe on the given system:
  _Static_assert(sizeof(strlist_t) == sizeof(const char* [STRINGS_N]), 
                 "Struct padding detected! Type punning failed.");

  // initialize list:
  strlist_t strlist = 
  {
    #define X(i, str) .s##i = (str),
      STRING_LIST
    #undef X
  };

  // print data by using pointers s0 to s3:
  #define X(i, str) printf("s%d: %s \n", i, strlist.s##i);
    STRING_LIST;
  #undef X

  printf("\n");

  // print data using a loop:
  for(int i=0; i<STRINGS_N; i++)
  {
    printf("s%d: %s \n", i, strlist.array[i]);
  }
}

Upvotes: 1

dragosht
dragosht

Reputation: 3275

Something like this should work (at least in gcc it would - it uses the block expression compiler extension):

#define X(s, i) ({          \
    char *ss[num_arrays];   \
    ss[0] = s##0;           \
    ss[1] = s##1;           \
    ss[2] = s##2;           \
    ss[3] = s##3;           \
    ...
    ss[X] = s##X;           \
    ss[i]; })

But it's really creepy and inefficient, as it creates a new array of char pointers and initializes it every time it is used ...

Upvotes: 1

Ed Heal
Ed Heal

Reputation: 60017

Assuming all your strings are constants you can do the following without macros (They tend to be a bad idea IMHO):

const char *S[] = {
   "string1",
   "string2",
   "strin3",
   NULL
};


for(int i=0; s[i];i++){
   printf("s%d: %s \n",i, s[i] );
}

https://ideone.com/fNUMJ4

PS: Using NULL as a marker for the end of the list - Could use sizeof s/sizeof (s[0]) instead

Upvotes: 1

unalignedmemoryaccess
unalignedmemoryaccess

Reputation: 7441

Answer is you can't do that.

Simply, because macros are compiled and can't be used with for loops in your way.

Use array of pointers to your strings, something like:

char* pointers[] = {
    "String1",
    "String2",
    "...."
};

Usage for printf (using sizeof in for loop to identify number of elements in array to print):

for (int i = 0; i < sizeof(pointers) / sizeof(pointers[0]); i++) {
    printf("s%d: %s \n", i, pointers[i]);
}

Upvotes: 2

Related Questions