我有一些代码可以添加分数。
#include <stdio.h>
#include <stdlib.h>
struct frac
{
int enumerator;
int denominator;
};
typedef struct frac frac_t;
frac_t *Add(frac_t *b1, frac_t *b2)
{
frac_t rfrac;
frac_t *p;
p = &rfrac;
(*p).enumerator= ((*b1).enumerator* (*b2).denominator) + ((*b2).enumerator* (*b1).denominator);
(*p).denominator= ((*b1).denominator* (*b2).denominator);
return p;
}
int main(void)
{
frac_t b1 = {2,4};
frac_t b2 = {1,7};
frac_t *add = Add(&b1, &b2);
printf("%i %i\n", add->enumerator, add->denominator);
system("pause");
return 0;
}
这完全没问题。结果是:3 5,因为它应该是。
如果我添加“printf”,它会完全弄乱我的结果:
int main(void)
{
frac_t b1 = {2,4};
frac_t b2 = {1,7};
frac_t *add = Add(&b1, &b2);
printf("addition:\n");
printf("%i %i\n", add->enumerator, add->denominator);
system("pause");
return 0;
}
结果是:
此外:
2008958704 -1
出了什么问题?
答案 0 :(得分:11)
您的函数Add
正在返回指向该函数内创建的临时变量的指针。一旦该函数返回,程序就可以以任何方式使用该内存;你不应该再访问它了。你第一次幸运 - 程序碰巧只留下了那段记忆,你的结果得以保留。添加第二个printf
会导致对堆栈内存的额外修改,这会覆盖原始值并暴露出错误。
您应该将指向frac_t
的指针传递给函数Add
,而不是获得结果。 E.g:
void Add(frac_t *result, frac_t *b1, frac_t *b2) {
// modify result here
}
答案 1 :(得分:3)
在
frac_t *Add(frac_t *b1, frac_t *b2)
{
frac_t rfrac;
frac_t *p;
p = &rfrac;
(*p).enumerator= ((*b1).enumerator* (*b2).denominator) + ((*b2).enumerator* (*b1).denominator);
(*p).denominator= ((*b1).denominator* (*b2).denominator);
return p;
}
你返回一个局部变量rfrac的地址。当您使用它时,这会给您未定义的行为。 printf()调用只是让UB显现出来。
答案 2 :(得分:1)
您将返回函数Add
的本地对象的地址。这意味着一旦你离开函数地址不再有效,对象就会被破坏。
如果您尝试访问该对象,有时它可能会起作用(如您的第一个示例中所示),但很多时候它不会,您不能依赖该程序可能会执行的操作。
您需要更改为按函数返回结构,而不是指向本地结构的指针,或者指向应该向其写入结果的结构的指针,或者为结果动态分配内存并返回指向此内存的指针。在最后一种情况下,调用者必须负责释放该内存。