You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何无法使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 10:42:08