【问题标题】:Is it good to return a "static char[]" string in C?在 C 中返回“static char[]”字符串好吗?
【发布时间】:2013-12-31 02:51:46
【问题描述】:

我有一组函数都在构建这样的日志记录字符串:

...
// Top level snprintf call
snprintf(buffer, BUFSIZ, "%s %s %s", dump_1(obj), dump_2(obj), dump_3(obj));
...

static char *dump_1(my_own_type_t obj)
{
        static char t1[BUFSIZ];
        // Logic printing obj data into t1
        return t1;
}

static char *dump_2(my_own_type_t obj)
{
        static char t2[BUFSIZ];
        // Logic printing obj data into t2
        return t2;
}

static char *dump_3(my_own_type_t obj)
{
        static char t3[BUFSIZ];
        // Logic printing obj data into t3
        return t3;
}

这是一种好方法,还是有更好的方法来实现同样的目标?我可以改用动态分配的字符串,但是我必须添加内存管理,这会使代码更加复杂。我也可以传递缓冲区,然后直接写入缓冲区,但这意味着需要大量额外的 snprintf 调用,因为每次我想向字符串添加内容时都必须调用 snprintf。

编辑: 在我的情况下,线程安全不是问题,并且日志调用不是可重入的。所以理论上我会很好地保持现状。但是,按照 mcleod_ideafix 的建议,以某种方式这样做感觉更干净。这样我就不需要任何静态变量,而且我仍然会将 snprintf 调用保持在最低限度。唯一的缺点是声明了很多本地缓冲区,但我觉得在我的情况下这是可以管理的。非常感谢所有反馈。

【问题讨论】:

  • 这完全不是线程安全的,这对您的情况有影响吗?
  • 如果您曾经运行多个线程,甚至只是进行可重入调用,这将严重失败。
  • @ChrisHayes:不,线程安全不是问题。

标签: c string


【解决方案1】:

只要您意识到它使函数不可重入,返回指向静态的指针就可以了。所以,例如,

snprintf(buffer, BUFSIZ, "%s %s", dump_1(obj1), dump_1(obj2));

会失败,因为对 dump_1() 的两次调用会干扰,并且可能不会按照您期望的顺序调用。此外,您将无法在线程代码中使用这些函数。通用字符串函数的最佳实践是将输出字符串和大小作为参数传递,就像 snprintf 所做的那样。

【讨论】:

  • 对 dump_1 的两次调用会干扰,还是可能会干扰?
  • @EricZ 在本例中它将干扰,因为dump_1 总是返回相同的指针(并且dump_1 函数调用都运行并完成了副作用 -以任何顺序 - 在输入 snprintf 之前)。
  • @user2864740,哦,是的!忽略对dump_1 的相同调用是我的错。感谢那。 +1
【解决方案2】:

我也可以传递缓冲区,然后直接写入 缓冲区,但这意味着很多额外的 snprintf 调用,因为我 每次我想添加一些东西时都必须调用 snprintf 字符串。

不一定:

...
char s1[BUFSIZ],s2[BUFSIZ],s3[BUFSIZ];
...
// Top level snprintf call
snprintf(buffer, BUFSIZ, "%s %s %s", dump_1(obj,s1), dump_2(obj,s2), dump_3(obj,s3));
...
//or like this:
...
dump_1(obj,s1);
dump_2(obj,s2);
dump_3(obj,s3);
snprintf(buffer, BUFSIZ, "%s %s %s", s1, s2, s3);
...

char *dump_1(my_own_type_t obj, char *s)
{
        // Logic printing obj data into s
        return s;
}

char *dump_2(my_own_type_t obj, char *s)
{
        // Logic printing obj data into s
        return s;
}

char *dump_3(my_own_type_t obj, char *s)
{
        // Logic printing obj data into s
        return s;
}

第二种方式还可以让你清楚地确定不同dump_X()函数的执行顺序,如果在snprintf()参数评估中调用它们可能不清楚(实际上,第一种方式可能会调用先是dump_3(),然后是dump_2(),然后是dump_1(),因为C_decl 函数将其参数推入堆栈的方式是:从右到左。您可能关心也可能不关心)

【讨论】:

  • 其实这是一个很好的观点,我忽略了这个选项。非常感谢!
【解决方案3】:

这是返回字符串的一种完全有效的方式。

然而,这并不是在所有情况下都是正确的方法。一方面,如果你的函数是从多个线程调用的,它们都将使用同一个缓冲区,并且会相互覆盖。

【讨论】:

  • 不仅仅是多线程:所有重入(以及一些)都丢失了。
  • 当然,但这本质上是从函数内部控制的,而不是它的调用方式。
  • 因此我添加了“then some”。它与重入没有严格的关系,但无论这种情况被称为:char *a = dump_1(A); char *b = dump_1(B);,可能会令人惊讶。
【解决方案4】:

返回指向函数本地静态数组的指针不是一个好方法。将变量设为静态只是为函数的下一次调用修复它的值。

如果您声明一个指向 char 的指针,则将函数返回的值分配给它,然后定义一个新指针并将同一函数返回的值分配给它。这将使两个指针指向相同的地址,并更改存储在其中一个指针指向的地址中的值,使其在另一个指针指向相同的地址时发生更改,如下例所示:

int i = 1;

static char *f()
{
    static char a[10];
    if (i){
        strcpy(a, "value 1");
        i = 0;
    }
    else {
        strcpy(a, "value 2");
    }
    return a;
}

int main()
{
    char *x1 = f();
    char *x2 = f();

    printf("%s\n", x1);
    printf("%s\n", x2);
    return 0;
}

输出是:

value 2
value 2

最好的方法是在函数内部声明一个指针并为内容分配内存

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-11
    • 2016-05-23
    • 2016-02-05
    • 2020-10-15
    • 1970-01-01
    相关资源
    最近更新 更多