我用单个字段和一个初始化函数定义一个结构,如下所示:
typedef struct{
int* field;
}myStruct;
myStruct init(int x){
myStruct s;
s.field = &x;
return s;
}
然后我声明其中两个结构,然后打印第一个结构的字段。
int main(){
myStruct s1 = init(1);
myStruct s2 = init(2);
printf("s1.field=%d\n",*s1.field);
return 0;
}
其结果是“ s1.field = 2”。为什么第二个的初始化会影响第一个?
答案 0 :(得分:2)
您两次调用此函数:
myStruct init(int x){
myStruct s;
s.field = &x;
return s;
}
在两个调用中,都分配了变量(或参数)x
,但在函数返回时不再有效。您已经存储了本地变量的地址,并且由于使用的是相同的函数,因此本地变量地址第二次相同,这说明了“魔术”(仍然是未定义的行为,但大多数使用堆栈作为自动变量的编译器将产生相同的结果)
不幸的是,编译器不够聪明,无法检测到您正在存储局部变量的地址。当您返回本地地址(通常更容易检测)时,它们通常会触发警告。
例如,如果您调用printf
,将不会得到1或2,而是会产生完全的垃圾,因为参数存储器的组织方式与您的函数不同。
一种干净的方法是分配动态内存:
s.field = malloc(sizeof(*s.field)); // this memory persists even after the end of "init"
*s.field = x; // copying the value, not the address
当然,当不使用结构时,它需要释放。
答案 1 :(得分:1)
任何变量在内存中都有一些空间。指针引用该空间。当函数调用返回时,局部变量所占用的空间将被释放,这意味着它可以并且将被其他东西重用。因此,对该空间的引用将指向完全不相关的事物。
首选方法是使用malloc()保留非本地内存。这里的危险是您必须释放(free())使用malloc()分配的所有内容,并且如果忘记了,则会造成内存泄漏。
答案 2 :(得分:1)
在功能中
myStruct init(int x){
myStruct s;
s.field = &x;
return s;
}
您正在将x
的地址分配给s.field
。问题是x
对于函数来说是本地的,一旦函数退出,就会停止存在(从逻辑上讲-显然,它所占据的内存位置仍然存在,但是现在可用供其他使用,可能会被覆盖)。该指针值现在为无效,尝试取消引用它会导致未定义的行为。
在这种特殊情况下,最有可能发生的事情是 ,x
调用中用于init(1)
的空间正在{{1}中被重用和覆盖}调用,并且奇迹般地,该位置要么未被init(2)
调用覆盖,要么printf
正在向该位置写入printf
作为其操作的一部分。