从 C 中的函数返回静态字符串是一种不好的做法吗?

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

提问人:kallazz 提问时间:11/11/2023 更新时间:11/12/2023 访问量:112

问:

我正在开发一个 C 程序,在其中我实现了一个函数来生成用于打印彩色文本的 ANSI 颜色代码。

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

我需要做的就是打印一些彩色的hello worlds:

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

这是我能找到的最干净的解决方案,但我想知道是否声明,因为没有不好的做法。 每次初始化都会覆盖相同的变量,还是前一个 s 会保留在内存中?我真的不喜欢他们都住在那里的想法。但如果被覆盖,这似乎还不错。colorCodestaticcolorCodecolorCodecolorCode

我也尝试了一些其他解决方案,但我不太喜欢它们。 我不想在堆上分配然后释放它。 我也不想把它作为一个论点传递,因为它看起来不那么干净。 有没有比这更好的方法,而且这不是一个坏做法?colorCodestaticstatic

C 字符串 静态

评论

0赞 Retired Ninja 11/11/2023
只要您了解每次调用该函数时,以前返回的任何指针都将指向新内容。如果这是程序中的问题,它也不是线程安全的。
4赞 user2357112 11/11/2023
一旦您需要同时使用两个颜色代码,这样做就会引起很多问题。例如,如果要在同一调用中使用两种颜色,或者两个线程想要同时打印彩色文本(可能打印到两个不同的输出流)。staticprintf
0赞 Brian61354270 11/11/2023
这个问题可能更适合软件工程,因为它是一个一般的设计/推荐问题。在本网站上,它可能会因基于意见而被关闭。
1赞 Harith 11/11/2023
使用转义序列是不可移植的。最好使用。\e\x1B

答:

3赞 chqrlie 11/11/2023 #1

这种方法的问题在于你有一个隐藏状态,使你的函数在同一个表达式中既不是线程安全的,也不是多重可调用的,包括间接的。static

例如,请考虑以下代码:

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

输出将是不确定的,因为未指定调用的顺序,但在所有情况下,两个替换输出的 espace 序列都是相同的。getAnsiColorCodeprintf%s

只要静态字符串的值是常量,就可以返回静态字符串,即:在调用之间不会更改。

有 2 种方法可以实现转义序列代码:

  • 在构造序列时使用足够长度的参数数组

    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));
        }
    }
    
  • 使用静态答案数组(不是完全线程安全的,但可以不受限制地多次调用):

    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));
        }
    }
    

评论

1赞 kallazz 11/11/2023
现在一切都清楚了。谢谢!
1赞 Fe2O3 11/12/2023 #2

您不需要“辅助函数”,从而消除了使用一个或多个缓冲区的担忧。static

C 语言是一种强大的语言。始终努力利用其设施。

#include <stdio.h>

#define CLR(i,str) "\033[38;5;%dm" str, i

int main( void ) {

    for( int i = 0; i < 256; i++ )
        printf( CLR( i, "Hello, World! (%d)\n" ), i );

    return 0;
}

输出:

Hello, World! (0) // black
Hello, World! (1) // dark red ... use your imagination or give it a try...
Hello, World! (2) // dark green
Hello, World! (3) // ...
Hello, World! (4)
Hello, World! (5)
Hello, World! (6)
Hello, World! (7)
/* ... */

引用彼得·帕克斯(Peter Parkers)的叔叔的话,“权力越大,责任越大。如果处理不当,宏可能会爆炸。警告 emptor。

编辑
或...如果你想让调用文本颜色的代码“看起来更好”,你可以使用 ...
VARGS

#include <stdio.h>
#include <stdarg.h>

void printfCLR( int clr, char *fmt, ... ) {
    fprintf( stdout, "\033[38;5;%dm", clr ); // output the colour prefix

    va_list ap;
    va_start( ap, fmt );
    vfprintf( stdout, fmt, ap ); // output the string (with arguments)
    va_end( ap );
}

int main( void ) {

    for( int clr = 0; clr < 256; clr++ )
        printfCLR( clr, "Hello, World!\n" );  // 'clean' invocation...

    return 0;
}

如果你想看到迷幻的 60 年代的东西......

    for( int i = 0; i < 256; i++ ) {
        printfCLR( 255-i, "Hello, " );
        printfCLR(     i, "World!\n" );
    }