我需要为GMP库编写自己的内存分配函数,因为默认函数调用abort()并且在发生之后我无法想到恢复程序流(我在整个地方都调用了mpz_init) ,以及如何根据该调用周围发生的事情来处理故障变化。但是,文档要求函数返回的值不为NULL。
是否至少有一个地址范围始终可以保证无效?了解所有这些内容会很有用,因此我可以针对不同的错误代码使用不同的地址,甚至可能针对不同的错误系列使用不同的范围。
答案 0 :(得分:4)
如果默认内存分配函数abort()
,并且GMP的代码无法处理NULL
,则GMP可能根本不准备处理内存分配失败的可能性。如果您返回故意无效的地址,GMP可能会尝试取消引用它,并立即崩溃,这与调用abort()
一样糟糕。更糟糕的是,因为堆栈跟踪不会指出真正导致问题的原因。
因此,如果你要返回,你必须返回一个有效指针,一个没有被其他任何东西使用的指针。
现在,一个稍微恶劣的选择是使用setjmp()
和longjmp()
来退出GMP例程。但是,这会使GMP处于不可预测的状态 - 您应该假设在此之后再也不能再调用GMP例程。它也可能导致内存泄漏......但这可能是你现在最不关心的问题。
另一个选择是在系统malloc中有一个保留池 - 即在应用程序启动时:
emergencyMemory = malloc(bignumber);
现在,如果malloc()
失败,您执行free(emergencyMemory)
,并且希望您有足够的空间来恢复。请记住,这只会为您提供有限的余量 - 您必须希望GMP将返回您的代码(并且该代码将检查并查看已使用紧急池),然后才能真正耗尽内存。
当然,您也可以组合使用这两种方法 - 首先使用保留池并尝试恢复,如果失败,longjmp()
输出,则显示错误消息(如果可以),以及优雅地终止。
答案 1 :(得分:3)
不,没有可移植范围的无效指针值。
您可以使用特定于平台的定义,也可以使用某些全局对象的地址:
const void *const error_out_of_bounds = &error_out_of_bounds;
const void *const error_no_sprockets = &error_no_sprockets;
[编辑:抱歉,错过了您希望将这些值返回到库中。正如bdonlan所说,你不能这样做。即使您发现某些“无效”值,库也不会期望它们。您的函数必须返回有效值,或abort
。]
你可以在全局中做这样的事情:
void (*error_handler)(void*);
void *error_data;
然后在你的代码中:
error_handler = some_handler;
error_data = &some_data;
mpz_init(something);
在你的分配器中:
if (allocated_memory_ok) return the_memory;
error_handler(error_data);
abort();
在调用mzp_init
之前设置错误处理程序和数据可能有点单调乏味,但根据行为在不同情况下的不同程度,您可能可以编写一些函数或宏来处理它。
但是,如果GMP库在分配失败后没有设计应对,那么你不能做的就是恢复并继续运行。在这方面你会受到你的工具的支配 - 如果库调用没有错误返回,那么谁知道它的内部结构将处于什么破坏状态。
但这是一个完全普遍的观点,而GMP是开源的。您可以找到mpz_init
中实际发生的情况,至少对于特定的GMP版本。可能有一些方法可以提前确保你的分配器有足够的内存来满足请求,或者可能有某种方法可以在没有造成太大损害的情况下蠕动(比如bdonlon说,longjmp
)。
答案 2 :(得分:1)
由于没有人提供正确答案,因此您可以安全地用作错误值的非NULL内存地址集与您为此目的创建的地址集相同。如果需要全局可见,只需声明static const char
(或全局const char
)数组N
是您需要的错误代码数,并使用指针{{1此数组的元素为N
错误值。
如果指针类型不是N
而是其他东西,则可能需要使用该类型的对象而不是char *
数组,因为将这些char
指针转换为另一个指针类型不能保证工作。
答案 3 :(得分:0)
仅在当前主流操作系统(启用虚拟内存)和CPU架构上保证:
-1L(表示对于指针足够大的值中的所有位)
许多库使用它来标记释放的指针。有了这个,您可以轻松找到错误来自使用NULL指针或挂起引用。
适用于HP-UX,Windows,Solaris,AIX,Linux,Free-Net-OpenBSD以及i386,amd64,ia64,parisc,sparc和powerpc。
认为这样做足够了。看不出任何超过这两个值(0,-1)的原因
答案 4 :(得分:0)
如果你只是返回,例如16位或32位对齐指针,不均匀的指针地址(LSB等于1)将至少是“神秘的”,并且会创建一个机会来使用我最喜欢的虚假值0xDEADBEEF(对于32位)指针)或0xDEADBEEFBADF00D(对于64位指针)。
答案 5 :(得分:0)
您可以使用多个范围,它们是特定于操作系统和体系结构的。
通常大多数平台都会保留第一页(通常长度为4K字节),以捕获空指针的解除引用(加上轻微偏移的空间)。
您还可以指向保留的操作系统页面,在Linux上,这些页面占用从0xc0000000
到0xffffffff
的区域(在32位系统上)。从用户空间,您将无权访问此区域。
另一个选项(如果要分配多个此类值,则是使用mmap
或equivalent分配没有读取或写入权限的页面,并为每个不同的错误值使用此页面的偏移量。
最简单的解决方案,只是使用对0
,(-1
,-2
等)立即为负的值,或立即为正(1
,{ {1}},...)。您可以非常肯定这些地址位于无法访问的页面上。
答案 6 :(得分:0)
一种可能性是采用保证存在的C库地址,因此永远不会被malloc
或类似地址返回。为了最容易移植,这应该是对象指针而不是函数指针,但在大多数架构上,转换((void*)main)
可能都没问题。我想到的一个数据指针是environ
,但它是POSIX,或stdin
等,不能保证是“真正的”变量。
要使用此功能,您只需使用以下内容:
extern char** environ; /* guaranteed to exist in POSIX */
#define DEADBEAF ((void*)&environ)