0

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.

Rezaeimh7
  • 1,467
  • 2
  • 23
  • 40

4 Answers4

2

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]);
}
unalignedmemoryaccess
  • 7,246
  • 2
  • 25
  • 40
  • Can I just say that `sizeof` is not a function, see [Ed Heal's answer](http://stackoverflow.com/a/42410004/28169) for how it can be written with fewer tokens. :) – unwind Feb 23 '17 at 10:14
  • No, it's not wrong, but I consider it confusing and a bad thing to be teaching people. That's why I pointed it out. In a majority of cases, `sizeof` is resolved at compile-time, i.e. it's not a function that is being called. Making it look like a function is therefore, in my opinion, a bad idea since it can be confusing. – unwind Feb 23 '17 at 10:18
  • Everything in C is possible if you throw enough evil macros on it. As proven by my answer. – Lundin Feb 23 '17 at 10:23
  • @unwind macros are also compile time but you need `(` and `)` if you pass a parameter. Is it also confusing? – unalignedmemoryaccess Feb 23 '17 at 10:53
  • @tilz0R No, that's generally handled by making the macro name all upper case, to make them distinct from regular functions. – unwind Feb 23 '17 at 13:35
  • @unwind this is true that is `generally` handled, but this is not the always. The same as sizeof is not always. I like put sizeof with brackets when I have size of pointer or similar like `sizeof(char *)` instead of `sizeof char *`. In first case it may be confusing because result of `sizeof char * 5` is what? – unalignedmemoryaccess Feb 23 '17 at 13:37
  • Visual studio won't compile this: `printf("%d\n", sizeof char *);` Here is the good answer. http://stackoverflow.com/a/28075117/3716664 – unalignedmemoryaccess Feb 23 '17 at 13:44
  • @tilz0R Fine. :) I disagree with that, but nevermind I was off-topic to begin with. :) – unwind Feb 23 '17 at 14:11
1

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

Ed Heal
  • 59,252
  • 17
  • 87
  • 127
1

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 ...

dragosht
  • 3,237
  • 2
  • 23
  • 32
1

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]);
  }
}
Lundin
  • 195,001
  • 40
  • 254
  • 396