1

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"

Steven
  • 811
  • 4
  • 23
  • 2
    Literal strings are immutable. Whenever you want to *change* them, you *must* return a modified copy. – Jongware Apr 04 '20 at 12:10
  • What should `Multi` do expect as arguments an return as a result? Please be more specific – Ackdari Apr 04 '20 at 12:19
  • @Ackdari I've edit the question – Steven Apr 04 '20 at 12:24
  • Does this answer your question? [How to write a while loop with the C preprocessor?](https://stackoverflow.com/questions/319328/how-to-write-a-while-loop-with-the-c-preprocessor) – Šimon Tóth Apr 04 '20 at 12:28
  • Specifially, https://stackoverflow.com/a/10542793/140750 Don't look at the accepted answer to the question referenced by @ŠimonTóth – William Pursell Apr 04 '20 at 13:06

4 Answers4

2

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
rici
  • 234,347
  • 28
  • 237
  • 341
  • Is it possible through other feature to complete function I want? – Steven Apr 04 '20 at 12:26
  • 1
    That depends on your needs. You can obviously construct the strings at runtime, but you'll need to use dynamic memory. It will depend on how long you need the created strings to last. Alternatively, if you know that you will never need more than a certain number of repetitions, you could do it with one string literal of the maximum size. – rici Apr 04 '20 at 12:53
  • @steven: So I don't really know what complete function you want but I added one which could possibly be what you're looking for. – rici Apr 04 '20 at 15:16
1

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.

Ackdari
  • 3,222
  • 1
  • 16
  • 33
  • That will leak memory unless the returned allocation is eventually free'd. – rici Apr 04 '20 at 12:55
  • Yes of course, but the only other option I see is to demand a char-array with enough space as argument, and this would be imho bad design. But I think I see another problem, each arm of the if-else statement should return a pointer to a new string. – Ackdari Apr 04 '20 at 13:02
  • You need to at least mention the need for the caller to free. And yes, if you return a malloc from one execution path, you need to do so from all of them. – rici Apr 04 '20 at 13:04
  • `strcpy(&new_str[orig_length * i], orig);` will accomplish the same. `if (times == 0) return calloc(1, sizeof(char));` not needed. – chux - Reinstate Monica Apr 04 '20 at 14:06
  • It is not good to use `strcpy` because it also copies the `\0` byte which is not problematic but unnecessary. – Ackdari Apr 04 '20 at 14:31
  • 1
    @ackdari: But calloc clears all the bytes, which is even more unnecessary. To say nothing of the issues with `strncpy`. (Use memcpy if that's what you mean. And then zap the trailing NUL.) – rici Apr 04 '20 at 15:09
1

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
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
0

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;
}
Saadi Toumi Fouad
  • 2,779
  • 2
  • 5
  • 18
  • 2
    You need to also set the null-byte of the new string or else it is undefined if the string is null-terminated or not. – Ackdari Apr 04 '20 at 14:38
  • I'm taking care of that in my code, I'm allocating one byte more than the length of the new string and that byte stores the null terminator – Saadi Toumi Fouad Apr 04 '20 at 16:21
  • But you don't set it to be null and so it is up to chance that the string is properly null-terminated – Ackdari Apr 04 '20 at 16:23
  • Aside: Since the goal could make very long strings, using `size_t` instead of `int` would help prevent overflow. – chux - Reinstate Monica Apr 05 '20 at 15:47
  • @chux-ReinstateMonica I don't think the user will ever overflow 2billions, in only one string!!! – Saadi Toumi Fouad Apr 05 '20 at 15:51
  • I use to think that way about strings a couple decades ago - then learned that good code is used in every widening applications. Your call. – chux - Reinstate Monica Apr 05 '20 at 16:02
  • 1
    It's up to the user if he needs more he will change the way he likes, for me I use linked lists of chars as strings, and linked lists of linked lists of chars as strings lists, actually I have written a small library for that has lot of functions those exist on strings in another programming languages, I haven't finished it's documentation yet, maybe few days and it will be on GitHub :) – Saadi Toumi Fouad Apr 05 '20 at 16:08