我似乎无法理解这里到底发生了什么
#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数组)均应覆盖。
答案 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
等于bp
,a
也与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
分解为几个小步骤:
mes(25)
;现在静态字符混乱现在为"25"
。mes(55)
,现在静态字符混乱将覆盖到"55"
。"%s %s\n"
(这里没有太多要评估。它只是一个字符串)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
}