将结构传递给C中的回调时的SIGSEGV(分段错误)

时间:2018-03-13 21:22:24

标签: c segmentation-fault

我的代码有一些奇怪的行为。 我不能将结构传递给回调函数。

#include <malloc.h>
#include "stdint.h"

typedef struct {
    int test_rssi;
    int setup_rssi;
} test_setup_scan_result;

typedef void (*WifiCallback)(uint8_t);

WifiCallback TestSetupCb;

void resume_configuration(test_setup_scan_result *result) {
    if (result == NULL) {
        printf("result is NULL\n");
        return;
    }

    printf("result is not NULL\n");
    printf("setup_rssi %d\n", result->setup_rssi);
    printf("test_rssi %d\n", result->test_rssi);
    printf("done");
}

void scan_test_setup_done_cb() {
    printf("scan_done\n");
    test_setup_scan_result *scan_result = malloc(sizeof(test_setup_scan_result));
    printf("created structure\n");
    scan_result->test_rssi = 1;
    scan_result->setup_rssi = 1;
    printf("filled structure\n");
    if (TestSetupCb) {
        printf("executing cb\n");
        printf("setup_rssi %d\n", scan_result->setup_rssi);
        printf("test_rssi %d\n", scan_result->test_rssi);
        TestSetupCb((uint8_t) scan_result);
    }
}

void scan_for_test_setup(WifiCallback cb) {
    TestSetupCb = cb;
    scan_test_setup_done_cb();
}

int main(void) {
    printf("Hello World\n");
    scan_for_test_setup((WifiCallback)resume_configuration);
    return 0;
}

我试图获取resume_configuration函数中的结构值后的代码不起作用。我是c语言的新手,所以任何有用文章的链接都被认为是。

3 个答案:

答案 0 :(得分:1)

将函数名称转换为具有不兼容参数类型的函数指针类型未定义行为

scan_for_test_setup((WifiCallback)resume_configuration);
//                  ^^^^^^^^^^^^^^
//               Undefined behavior

这本身足以让人崩溃。但是,您的程序会非法执行另一项操作 - 下面的调用

TestSetupCb((uint8_t) scan_result);
//                    ^^^^^^^^^^^
//               Does not fit in uint8_t
无论函数指针是否转换,

都会失败,因为指针不适合8位。

答案 1 :(得分:1)

变量“scan_result”是指向具有堆区域中已分配内存空间的struct的指针。 当使用参数“(uint8_t)scan_result”调用TestSetupCb()时,实际上将“scan_result”的地址转换为uint8_t,这意味着获取地址的第一个字节并按值传递。在这种情况下,您将丢失要在resume_configuration()函数中处理的数据。 例如:当你将此地址转换为uint8_t时,假设“scan_result”结构地址为“0x004EE388”,结果为“0x00000088”,此地址无效并在程序中产生问题。此外,这种操作会对您的程序造成危险。 要解决您的问题,只需按以下方式重新定义函数指针: typedef void(* WifiCallback)(test_setup_scan_result *); 并更新你的代码:而不是TestSetupCb((uint8_t)scan_result);更改为TestSetupCb(scan_result);

答案 2 :(得分:0)

我在Valgrind中运行它并且我发现了一个明显的错误(在几个地方出现):

resume_configuration不遵守WifiCallback指针类型;它需要一个指针类型的参数,而不是uint8_t类型的参数,它很可能具有不同的大小(它确实有)

您调用TestSetupCb((uint8_t) scan_result); - 将scan_result指针显式转换为小于指针的数据类型。您已截断指针,因此现在结果无法转换回有效指针。英特尔系统上的截断非常糟糕,任何结果指针都将始终位于NULL页面中。