为何无法使用POSIX线程保留函数栈帧?
为什么借助POSIX线程保留栈帧的尝试会段错误?
提前说明:我知道POSIX有标准的参数传递方法;我清楚自己的操作不规范,绝不会在实际场景中用;我只是想搞懂编译器对内存和栈帧的处理逻辑,避免以后踩内存错误的坑。这不是“怎么实现”的问题,而是“为什么行不通”的问题。
正确的结构体值传递示例
向调用函数返回结构体时,标准的做法是用值传递:
#include<stdio.h> typedef struct some_struct { int val1; int val2; } some_struct; some_struct remote() { some_struct s; s.val1 = 1; s.val2 = 2; return s; } int main() { some_struct by_value = remote(); printf("%d %d\n", by_value.val1, by_value.val2); // 输出 '1 2' return 0; }
返回局部变量指针的风险
如果改成返回被调用函数中局部变量的指针,就会出现未定义行为:
#include<stdio.h> typedef struct some_struct { int val1; int val2; } some_struct; some_struct* remote() { some_struct s; s.val1 = 1; s.val2 = 2; return &s; } int main() { some_struct *by_reference = remote(); printf("%d %d\n", by_reference->val1, by_reference->val2); // 有时会输出 '1 2',但本质非法 return 0; }
我的理解是:当函数返回时,编译器会将被调用函数的栈帧标记为可释放,后续可能被任意覆盖。所以调用函数引用该栈帧内的值时,内存大概率已被覆盖,只有小型程序可能侥幸输出正确值。
尝试用POSIX线程保留栈帧的失败代码
我曾想过,能不能用POSIX线程保留被调用函数的栈帧?于是写了这段代码:
#include<stdio.h> #include<unistd.h> #include<pthread.h> typedef struct some_struct { int val1; int val2; } some_struct; int semaphore1 = 1; int semaphore2 = 1; void* remote(void* arg) { some_struct s; s.val1 = 1; s.val2 = 2; arg = &s; semaphore1 = 0; while(semaphore2) sleep(1); return NULL; } int main() { pthread_t thread; some_struct *by_reference; pthread_create(&thread, NULL, remote, (void*)by_reference); while(semaphore1) sleep(1); printf("%d %d\n", by_reference->val1, by_reference->val2); // 触发段错误 semaphore2 = 0; return 0; }
运行后直接触发段错误,这是为什么?难道线程无法保留栈帧?还是有其他原因?
问题根源解析
线程的栈帧确实是独立的,只要线程还在运行,它的栈帧不会被系统回收,但你的代码的问题根本不在栈帧释放上,而是参数传递的逻辑错误:
- 在
main函数中,by_reference是一个未初始化的指针,它的值是随机的垃圾地址。你把它强转为void*传给pthread_create,但这只是传递了指针的当前值,而非指针本身的引用——换句话说,线程函数拿到的是这个垃圾地址的副本,不是by_reference变量的地址。 - 在
remote线程函数里,你修改的是函数参数arg的局部副本,这个修改完全不会同步到main里的by_reference变量。main中的by_reference依然是未初始化的垃圾指针,当你用它访问结构体成员时,本质是在访问一个随机的非法内存地址,直接触发段错误。
补充说明
就算你修正了参数传递的问题(比如传递&by_reference,也就是指针的指针,在线程函数里解引用修改*((some_struct**)arg) = &s;),让主线程拿到了线程栈中some_struct s的地址,也要保证线程在主线程访问该变量期间不退出——否则线程退出后栈帧被回收,这个地址就变成了悬空指针,后续访问依然是未定义行为。但你的代码连第一步参数传递都没做对,所以直接触发了段错误。
内容的提问来源于stack exchange,提问作者hstone32
相关产品推荐
相关产品推荐

