Steven
Steven

Reputation: 871

Is repeat string literal possible using 'for' loop in C?

I am wondering can pure C do following pseudo code?

for(int i = 0; i < N; i++)
    func( Multi("str",i));

I know the feature char *tmp = "str1" "str1" and tried to combine that and macro. But, the only way I come up with is define several macro with different repeat times. My method is bad for concise, are there better method ?

edit:

expect Multi can return "str" * i times

e.g. char *tmp = Multi("str",3); // now tmp is "strstrstr"

Upvotes: 1

Views: 537

Answers (4)

Saadi Toumi Fouad
Saadi Toumi Fouad

Reputation: 2829

Yes pure C can do a lot my friend, here I have written a function for you

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

char * strRepeat(char *str, int n) {
  int len = strlen(str);
  char *repeatedStr = (char *)malloc(len * n + 1);
  if(repeatedStr == NULL) {
    return NULL;
  }
  for(int i = 0;i < len * n; i++) {
    repeatedStr[i] = str[i % len];
  }
  repeatedStr[len * n] = '\0';
  return repeatedStr;
}

int main(void) {
  char *s = strRepeat("str", 7);
  printf("%s", s);
  //output: strstrstrstrstrstrstr
  free(s);
  return 0;
}

Upvotes: 0

Ackdari
Ackdari

Reputation: 3498

One way to implement this is to use strncpy and calloc to copy the original string several times into a new char array

char* repeat(char* orig, size_t times) {
    if (times == 0) return calloc(1, sizeof(char)); // is the empty string
    else {
        size_t orig_length = strlen(orig);
        size_t new_length = times * orig_length;
        char* new_str = malloc((new_length + 1) * sizeof(char));

        for (size_t i = 0; i < times; i++) {
            strncpy(&new_str[orig_length * i], orig, orig_length);
        }

        new_str[new_length] = 0; // setting the null-byte

        return new_str;
    }
}

This function return always a new string, which needs to be freed with free before the last reference to it is lost or else you will have a memory leak.

This could also be done recursive, but this won't do much for this kind of function. And this can most likly be optimized, feel free to suggest improvements.

Upvotes: 1

rici
rici

Reputation: 241861

Not if you expect to be able to use the run-time value of a variable to control the number of repetitions (unless the range of values of that variable is small and known at compile-time).

Macro expansion and literal string concatenation are done as phases during the compilation, before the executable has been produced. The program doesn't yet exist, and certainly cannot be run. The macro preprocessor only sees a variable as an identifier inside the text of the program.

If you will always use a literal integer, then it is possible to do the expansion with the macro preprocessor, although it does indeed require a lot of macros. There are some macro libraries which can help.

If you know the maximum number of repetitions (and have some runtime mechanism to verify that the limit is not exceeded), you could create a single string literal of the maximum size, perhaps using a macro library as mentioned above. You can then get a string literal containing fewer than this maximum by starting int the middle:

#define Multi(literal, rep) \
    (&(REP(MAXREP, literal))[((sizeof literal)-1)*(MAXREP-rep)])

For that to work, MAXREP must be previously #defined as a (smallish) integer constant (not a constant expression).

Here's a complete example program, using BOOST_PP_REPEAT from the Boost preprocessor library to define REP:

#include <stdio.h>
#include <stdlib.h>
#include <boost/preprocessor/repeat.hpp>

#define MAXREP 80

#define REPEATER(z, n, literal) literal
#define REP(n, literal) BOOST_PP_REPEAT(n, REPEATER, literal)

#define Multi(literal, rep) \
    (&(REP(MAXREP, literal))[((sizeof literal)-1)*(MAXREP-rep)])

int main(int argc, char** argv) {
  int reps = 0;
  if (argc > 1) reps = atoi(argv[1]);
  if (reps <= 0) reps = MAXREP;
  if (reps > MAXREP) {
    fprintf(stderr, "Cannot do %d repetitions; maximum is %d\n", reps, MAXREP);
    exit(1);
  }
  for (int i = 0; i < reps; ++i) printf("%s\n", Multi("foo", i));
  return 0;
}

Sample run:

$ make rep
cc -O0  -Wall -ggdb -std=c11 -D_XOPEN_SOURCE=700 -mtune=native     rep.c   -o rep

$ ./rep 5

foo
foofoo
foofoofoo
foofoofoofoo

Upvotes: 2

chux
chux

Reputation: 154315

Perhaps something employing a compound literal (since C99) to form the space needed?

MULTI(some_string_literal, n) is valid until the end of the block. No need to free.

#include <string.h>

char *Multi(char *dest, const char *s, unsigned n) {
  size_t len = strlen(s);
  char *p = dest;
  while (n-- > 0) {
    memcpy(p, s, len);
    p += len;
  }
  *p = 0;
  return dest;
}

//  compound literal       v-------------------------------v      
#define MULTI(s, n) Multi( (char [(sizeof(s) - 1)*n + 1]){0} , (s), (n))

#include <stdio.h>
int main() {
  char *tmp = MULTI("str", 3);
  printf("<%s>\n", tmp);
  printf("<%s> <%s>\n", MULTI("str", 4), MULTI("str", 5));
  printf("%p\n", MULTI("str", 6));
}

Sample output

<strstrstr>
<strstrstrstr> <strstrstrstrstr>
0xffffcb80

Upvotes: 1

Related Questions