sprintf不能在printf调用中更改静态变量

时间:2018-12-19 16:09:40

标签: c

我似乎无法理解这里到底发生了什么

#include <stdio.h>

const char* mes(int a)
{
    static char mess[100];
    sprintf(mess, "%d", a);
    return mess;
}
const int* hes(int a)
{
    static int arr[100];
    arr[0] = a;
    return arr;
}

int main()
{
    printf("%s %s\n", mes(55), mes(25)); //55 55
    printf("%s\n", mes(55)); //55
    printf("%s\n", mes(25)); //25
    printf("%d %d\n", hes(55)[0], hes(25)[0]); //55 25
}

在第一个printf中,似乎忽略了第二个函数,并且先前的输入的输出再次被打印。
起初我以为这是一个静态变量问题,所以我尝试单独打印它们,然后它们似乎工作正常。
然后我以为这是一个printf问题,所以我尝试用整数数组模拟相同的行为,并且在这里也能正常工作。
我已经使用各种输入运行了该程序几次,排除了使用UB的可能性。
那么,我到底在这里想念什么?

编辑: 我在其他地方遇到了这个问题,无法理解发生了什么。因此,我以简短的示例代码重现了该问题。但是我的问题仍然存在(正如许多人所提到的那样),是否在打印之前评估了所有参数?如果是这样,则无论评估顺序如何,这两种情况(int和char数组)均应覆盖。

5 个答案:

答案 0 :(得分:5)

函数参数的求值顺序未指定,这意味着从理论上讲您可以看到25 25。这是第一件事。 其次,当调用printf时,两个函数都已经被求值,并且 same 指针作为第一个和第二个字符串(因为它是一个静态位置)被传递,这就是结果最后一次评估的结果(在您的情况下为55)。因此,将打印相同的文本。

这几乎等同于以下内容:

char* a = mes(25);
char* b = mes(55);

// Note, the above can swap depending on the order of evaluation

printf("%s %s\n", a, b);

但是这里a等于b,因为它们都指向静态数组。

对于第二个示例,这没有发生,因为它等效于以下内容(按评估顺序):

int *ap = hes(55);
int a = ap[0];

int *bp = hes(25);
int b = bp[0];

printf("%d %d\n", a, b);

请注意,此处传递的是指向的值,而不是指针本身。因此,即使ap等于bpa也与b不同。

答案 1 :(得分:1)

其他人很好地解释了为什么代码错误的原因。

static char mess[100]的替代方法是使用复合文字作为参数。然后,mes()hes()的返回值一直有效,直到代码块的末尾-printf()之后。

#include <stdio.h>

const char* mes_helper(char mess[100], int a) {
  sprintf(mess, "%d", a);
  return mess;
}
const int* hes_helper(int arr[100], int a) {
  arr[0] = a;
  return arr;
}

// compound literal        v-------------v
#define mes(a) mes_helper( (char [100]){0}, (a))
#define hes(a) hes_helper( (int  [100]){0}, (a))

// No changes to `main() code
int main(void) {
  printf("%s %s\n", mes(55), mes(25)); //55 55
  printf("%s\n", mes(55)); //55
  printf("%s\n", mes(25)); //25
  printf("%d %d\n", hes(55)[0], hes(25)[0]); //55 25
}

输出

55 25
55
25
55 25

答案 2 :(得分:0)

只有一个变量mess。 致电时:

printf("%s %s\n", mes(55), mes(25));

您要在两个不同的时间填写该变量,一次使用"25",一次使用"55"(覆盖"25")。结果,当printf转换为%s %s的格式时,它两次发现相同的字符串,"55"并再次发现"55",因为"25"已被覆盖

您需要注意,在调用函数之前,必须先评估函数的所有参数。参数评估的顺序未定义,但通常是从右到左。将printf分解为几个小步骤:​​

  1. 评估mes(25);现在静态字符混乱现在为"25"
  2. 评估mes(55),现在静态字符混乱将覆盖到"55"
  3. 评估参数"%s %s\n"(这里没有太多要评估。它只是一个字符串)
  4. 使用参数printf"%s %s\n""55"呼叫"55"

答案 3 :(得分:0)

致电时:

printf("%s %s\n", mes(55), mes(25));

在将

mes(55)mes(25)的结果填充到字符串之前,先对其进行评估。而且由于您指向相同的静态内存,所以在填充字符串时,您将获得相同的值。

答案 4 :(得分:0)

返回静态变量的地址几乎总是一种错误的模式。整个程序只有一个这样的静态分配,因此例如两次调用mes会导致第二个调用覆盖第一个调用的结果。

一个更合理的模式是让调用者提供一个缓冲区。为了线程安全,这也是必需的:

#include <stdio.h>

const char* mes(char *buf, int a)
{
    sprintf(buf, "%d", a);
    return buf;
}

const int* hes(int *arr, char *buf, int a)
{
    arr[0] = a;
    return arr;
}

int main()
{
    char buf1[100], buf2[100];
    int arr[100];
    printf("%s %s\n", mes(buf1, 55), mes(buf2, 25)); //55 55
    printf("%s\n", mes(buf1, 55)); //55
    printf("%s\n", mes(buf1, 25)); //25
    printf("%d %d\n", hes(arr, 55)[0], hes(arr, 25)[0]); //55 25
}