我正在使用GNU Multi-Precision(GMP)库代码使用任意长度的整数来阅读一些代码。 MP整数的类型是mpz_t
,如gmp.h头文件中所定义。
但是,我对这个库定义的mpz_t
类型的低级定义有一些疑问。在标题代码中:
/* THIS IS FROM THE GNU MP LIBRARY gmp.h HEADER FILE */
typedef struct
{
/* SOME OTHER STUFF HERE */
} __mpz_struct;
typedef __mpz_struct mpz_t[1];
第一个问题:[1]
是否与__mpz_struct
相关联?换句话说,typedef
是否将mpz_t
类型定义为__mpz_struct
数组,只有一次出现?
第二个问题:为什么阵列? (为什么只出现一次?)这是我听过的struct hacks之一吗?
第三个问题(可能与第二个问题间接相关):mpz_init_set(mpz_t, unsigned long int)
函数的GMP文档说只使用它作为按值传递,尽管假设这个函数将修改其在被调用函数内的内容(因此需要传递引用)语法。请参阅我的代码:
/* FROM MY CODE */
mpz_t fact_val; /* declaration */
mpz_init_set_ui(fact_val, 1); /* Initialize fact_val */
单次出现数组是否自动启用pass-by-reference(由于C中数组/指针语义的崩溃)?我很自然地承认我有点过分分析这个,但我当然喜欢对此进行任何讨论。谢谢!
答案 0 :(得分:4)
在C2中描述的意义上,这似乎不是结构黑客。看起来他们希望mpz_t
具有指针语义(可能,他们希望人们像使用不透明指针一样使用它)。考虑以下片段之间的语法差异:
struct __mpz_struct data[1];
(&data[0])->access = 1;
gmp_func(data, ...);
和
mpz_t data;
data->access = 1;
gmp_func(data, ...);
因为C数组衰减成指针,所以这也允许mpz_t
类型的引用自动传递。
它还允许您使用类似指针的类型,而无需malloc
或free
。
答案 1 :(得分:3)
* 第一个问题:[1]
是否与__mpz_struct相关联?换句话说,typedef是将mpz_t类型定义为__mpz_struct数组,只出现一次? *
是
第二个问题:为什么数组? (为什么只出现一次?)这是我听说过的结构黑客之一吗?
打败我。不知道,但有一种可能性是作者想要创建一个通过引用自动传递的对象,或者“是”,可能是struct hack。如果你看到一个mpz_t
对象作为结构的最后一个成员,那么“几乎可以肯定”它是结构黑客。分配看起来像
malloc(sizeof(struct whatever) + sizeof(mpz_t) * some_number)`
将是一个死的赠品。
单次出现数组是否允许自动传递引用...?
啊哈哈,你也明白了。 “是的”,一个可能的原因是以更复杂的引用为代价来简化传递引用。我认为另一种可能性是数据模型或算法中发生了某些变化,作者希望找到每个参考并以某种方式对其进行更改。像这样的类型更改会使程序具有相同的基本类型,但每个未转换的引用都会出错。
答案 2 :(得分:2)
原因在于mpn
的实施。具体来说,如果你在数学上倾向于你将意识到N是自然数的集合(1,2,3,4 ...)而Z是整数集(..., - 2,-1,0) ,1,2,...)。
为Z实现bignum库相当于为N执行此操作,并考虑了符号操作的一些特殊规则,即跟踪是否需要执行加法或减法以及结果是什么。
现在,关于如何实施bignum库...这里有一条线可以给你一个线索:
typedef unsigned int mp_limb_t;
typedef mp_limb_t * mp_ptr;
现在让我们看一下在其上运行的函数签名:
__GMP_DECLSPEC mp_limb_t mpn_add __GMP_PROTO ((mp_ptr, mp_srcptr, mp_size_t, mp_srcptr,mp_size_t));
基本上,它归结为“肢体”是表示数字位的整数字段,整数表示为一个巨大的数组。聪明的部分是gmp以非常有效,优化的方式完成所有这些工作。
无论如何,回到讨论。基本上,如你所知,在C中传递数组的唯一方法是将指针传递给那些有效地通过引用传递的数组。现在,为了跟踪发生了什么,定义了两种类型,mp_ptr
是一个mp_limb_t
数组,大到可以存储你的数字,mp_srcptr
是一个const这个版本,这样你就不会意外地改变你正在操作的源bignums的位。基本思想是大多数功能都遵循这种模式:
func(ptr output, src in1, src in2)
等。因此,我怀疑mpz_*
函数遵循这个约定只是为了保持一致,这是因为这就是作者的思考方式。
简短版本:由于你必须如何实现bignum lib,这是必要的。