我怎么能';防撞';如果传递了错误的参数类型,则使用C编写的回调函数

我怎么能';防撞';如果传递了错误的参数类型,则使用C编写的回调函数,c,pointers,types,signals,C,Pointers,Types,Signals,我有一个用C编写的回调函数,它在服务器上运行,必须是防崩溃的。也就是说,如果需要一个整数并传递一个字符串指针,我必须在函数内部确定这一点,并防止在尝试对不正确的参数类型执行某些不允许的操作时出现分段错误 函数原型为: void callback_function(parameter_type a, const b); “a”应该通过enum告诉我“b”是整数还是字符串指针。 如果调用函数的程序员犯了一个错误,并在“a”中告诉我“b”是一个整数,“b”实际上是一个字符串指针,那么我如何在不破坏回

我有一个用C编写的回调函数,它在服务器上运行,必须是防崩溃的。也就是说,如果需要一个整数并传递一个字符串指针,我必须在函数内部确定这一点,并防止在尝试对不正确的参数类型执行某些不允许的操作时出现分段错误

函数原型为:

void callback_function(parameter_type a, const b);
“a”应该通过enum告诉我“b”是整数还是字符串指针。 如果调用函数的程序员犯了一个错误,并在“a”中告诉我“b”是一个整数,“b”实际上是一个字符串指针,那么我如何在不破坏回调函数代码的情况下确定这一点呢。这在服务器上运行,如果调用方函数出错,则必须继续运行

代码必须是C语言,并且是可移植的,所以C库扩展是不可能的。编译器是:gcc v4.8.2 平台上整数的大小是4,字符指针的长度也是4。 整数可以具有与字符指针相同的数值,反之亦然

如果我认为我得到了一个字符指针而它没有,当我试图找到它的内容时,我当然会得到一个分割错误

如果我编写了一个信号处理程序来处理故障,我现在如何“清除”信号,并在一个正常的位置恢复执行

我有没有提到“b”是一个定义为:

union param_2 {
  char * c;
  int i;
} param_to_be_passed;
我想就是这样

谢谢您的回答。

这是不可能的

除了
NULL
之外,并没有办法“查看”指针并确定取消引用是否有效——当然要检查它


除此之外,没有神奇的方法可以知道指针是否指向字符数据、整数、函数或其他任何东西。

您正在寻找黑客

无论提出什么建议,都不要在生产中使用这些东西


如果需要后期绑定,则采用一种不同的、故障安全的方法。

如果您正在为嵌入式设备编写代码,则所有变量都将驻留在RAM中。例如,从地址
0x20000000
0x20020000
,您可能有128 kB的RAM。对于
c
,如果向您传递了一个指向没有此范围的内存地址的指针,那么除了检查空地址之外,这将是另一种确定错误的方法

if((a == STRING) && ((b.c == NULL) || (b.c < 0x20000000) || (b.c > 0x20020000)))
    return ERROR;
if((a==STRING)和((b.c==NULL)| |(b.c<0x20000000)| |(b.c>0x20020000)))
返回误差;

如果您在多线程环境中工作,您可以更进一步,要求传递给回调函数的所有地址都来自某个线程的内存空间(堆栈)。

在C中进行运行时类型检查实际上是不可能的

调用者有责任正确地传递数据;没有(好的、标准的、可移植的)方法来确定
b
是否包含正确类型的数据(即,调用方没有将指针值作为整数传递给您,反之亦然)


我唯一能提出的建议是创建两个单独的回调,其中一个采用
int
,另一个采用
char*
,并将编译时进行类型检查的负担强加给编译器

注意:这实际上并没有试图使函数“防崩溃”,因为我怀疑这是不可能的

如果允许您更改API,一个选项可能是合并union,仅使用API访问该类型

typedef enum Type { STRING, INT } Type;
typedef struct StringOrInt {
    Type type;
    union { int i; char* s } value;
} StringOrInt;

void soi_set_int(StringOrInt* v, int i) {
     v->type = INT;
     v->value.i = i;
}

void soi_set_string(StringOrInt* v, char* s) {
     v->type = STRING;
     v->value.s = s;
}

Type soi_get_type(StringOrInt cosnt* v) {
     return v->type;
}

int soi_get_int(StringOrInt const* v) {
     assert(v->type == INT);
     return v->value.i;
}

char* soi_get_string(StringOrInt const* v) {
     assert(v->type == STRING);
     return v->value.s;
}

虽然这实际上并不能使其防崩溃,但API用户会发现使用API比手动更改成员更方便,从而显著减少错误。

如果调用方在
a
中说结果是
int
,则不会有很大的崩溃风险,因为:

  • 在您的情况下,这两种类型具有相同的长度(请注意,这并不保证可移植!)

  • C标准说(ISO-第6.3.2.3节):“任何指针类型都可以转换为整数类型。除之前指定的以外,结果是实现定义的。如果结果不能在整数类型中表示,则行为是未定义的

  • 但幸运的是,大多数32位值都是有效的整数

请记住,在最坏的情况下,该值可能毫无意义。因此,您可以通过系统地验证该值的一致性来避免崩溃(例如,如果使用整数处理某些数组元素,则执行绑定控件)

如果调用方在“a”中说结果是指针,但提供了int,那么以可移植的方式避免崩溃就困难得多

  • 标准ISO规定:整数可以转换为任何指针类型。除非之前指定,否则结果是实现定义的,可能没有正确对齐,可能没有指向引用类型的实体,可能是陷阱表示

  • 在实践中,这些错误中的大多数都是在非常低的系统级别上被内存访问异常捕获的。行为是由实现定义的,没有可移植的方法来实现


整数输入有什么实际界限吗?我会对输入的“整数”或“指针”进行合理化测试。此外,字符串的长度有什么实际界限吗?我也会研究一下。我认为你不能100%防止崩溃,但你应该能够接近。有一些POSIX破解了它。我想我见过使用指针和大小为0,或者大小为1调用
write
。内核将检查内存位置的读取访问并返回
EFAULT