The reason for this is the implementation of mpn . In particular, if you are mathematically inclined, you will understand that N is the set of natural numbers (1,2,3,4 ...), while Z is the set of integers (..., - 2, -1,0 1 , 2, ...).
The implementation of the bignum library for Z is equivalent to doing this for N and taking into account some special rules for operations with signs, i.e. tracking whether addition or subtraction is needed and what the result is.
Now about how the bignum library is implemented ... here is a line that will give you the key:
typedef unsigned int mp_limb_t; typedef mp_limb_t * mp_ptr;
And now let's look at the function signature acting on this:
__GMP_DECLSPEC mp_limb_t mpn_add __GMP_PROTO ((mp_ptr, mp_srcptr, mp_size_t, mp_srcptr,mp_size_t));
Basically, it is that the "limb" is an integer field representing the bits of a number, and the integer is represented as a huge array. The smart part is that gmp does it all in a very efficient, well-optimized way.
In any case, back to the discussion. In principle, the only way to pass arrays to C, as you know, is to pass pointers to those arrays that effectively allow passing by reference. Now, to track what’s happening, two types are mp_ptr : mp_ptr , which is an array of mp_limb_t large enough to hold your number, and mp_srcptr , which is a version of const, so you can’t accidentally change the bits of the original bonuses to you are working. The basic idea is that most functions follow this pattern:
func(ptr output, src in1, src in2)
etc .. Thus, I suspect that the mpz_* functions follow this convention simply in order to be consistent, and that is because the authors think so.
Short version: due to how you need to implement bignum lib, this is necessary.