Search code examples
cstringstatic

Is returning a static string from a function in C a bad practice?


I'm working on a C program where I've implemented a function to generate ANSI color codes for printing colored text.

const char* getAnsiColorCode(int colorId) {
    static char colorCode[15];
    snprintf(colorCode, sizeof(colorCode), "\e[38;5;%dm", colorId);
    return colorCode;
}

All I need to do for my task is to print some colored hello worlds:

int main(int argc, char *argv[]) {
    for (int i = 0; i < 256; i++) {
        printf("%sHello, World!\n", getAnsiColorCode(i));
    }
}

This is the cleanest solution I could find but I am wondering if declaring colorCode as static there is not a bad practice. Will every initialization of colorCode override the same variable or will the previous colorCodes stay in the memory? I don't really like the idea of all of them staying there. But if colorCode gets overwritten it doesn't seem bad.

I also tried some other solutions, but I don't like them as much. I don't want to allocate colorCode on the heap and then free it. I also don't want to pass it as an argument, because it looks less clean that way. Is there a better way of doing it than static and is static here not a bad practice?


Solution

  • The problem with this approach is you have a hidden static state that makes your function neither thread safe not multi callable in the same expression, including indirectly.

    Consider for example this code:

    int main(int argc, char *argv[]) {
        for (int i = 0; i < 256; i++) {
            printf("%sHello, World!%s\n", getAnsiColorCode(i), getAnsiColorCode(0));
        }
    }
    

    The output will be indeterminate as the order of calls for the getAnsiColorCode is unspecified, but in all cases the espace sequence output by printf will be the same for both %s replacements.

    It is OK to return a static string as long as its value is constant, ie: does not change across calls.

    There are 2 ways to achieve your escape sequence code:

    • using an argument array of sufficient length where the sequence is constructed

      char *getAnsiColorCode(char colorCode[static 15], int colorId) {
          snprintf(colorCode, 15), "\e[38;5;%dm", colorId);
          return colorCode;
      }
      
      int main(int argc, char *argv[]) {
          for (int i = 0; i < 256; i++) {
              char colorCode[15];
              printf("%sHello, World!\n", getAnsiColorCode(colorCode, i));
          }
      }
      
    • Using a static array of answers (not fully thread safe, but callable multiple times without restrictions):

      const char *getAnsiColorCode(int colorId) {
          static char colorCode[256][12];
          colorId %= 255;
          if (!*colorCode[colorId]) {
              snprintf(colorCode[colorId], sizeof colorCode[colorId],
                       "\e[38;5;%dm", colorId);
          }
          return colorCode[colorId];
      }
      
      int main(int argc, char *argv[]) {
          for (int i = 0; i < 256; i++) {
              printf("%sHello, World!\n", getAnsiColorCode(i));
          }
      }