C语言中如何获取作为参数传入的mpz_t类型的地址?
我有一段C代码想要实现如下功能:
#include <gmp.h> extern void g(mpz_t in); extern mpz_t temp; /* initialized elsewhere */ void f(mpz_t in, int flag) { mpz_t *arg; if (flag) { mpz_mul_ui(temp, in, 2); arg = &temp; } else { /* this bit is the problem */ arg = ∈ } /* placeholder for more complex code using *arg (readonly) */ g(*arg); }
这段代码的目的是避免大量重复代码(此处用g(*arg)代表),让程序既能处理原始输入,也能处理其修改后的版本。
但arg = &temp可以正常编译,arg = &in却不行。我知道这是因为mpz_t被typedef为数组(typedef __mpz_struct mpz_t[1]),且从相关资料得知“数组作为函数参数时会退化为指针”,但我不清楚该指针的实际类型,因此无法正确处理。我尝试了arg = &in、arg = in、arg = &in[0],每种写法都会被GCC提示“warning: initialization from incompatible pointer type”。
我的系统GCC版本为7.5.0,未提示具体不兼容类型,但使用11.2.0版本时得到提示:warning: assignment to '__mpz_struct (*)[1]' from incompatible pointer type '__mpz_struct **'。我想知道是否存在无需强制转换的安全赋值方式,若必须转换,正确的转换写法是什么(比如arg = (mpz_t *)&in是否可行)?
核心原因分析
- 全局变量
temp是mpz_t类型,即__mpz_struct[1]数组,因此&temp的类型是__mpz_struct (*)[1](指向长度为1的__mpz_struct数组的指针),和你定义的mpz_t *arg类型完全匹配(mpz_t*等价于__mpz_struct (*)[1])。 - 函数参数
in虽然声明为mpz_t,但根据C语言规则,数组作为函数参数会退化为指向数组首元素的指针,实际类型是__mpz_struct *,而非数组类型。因此&in的类型是__mpz_struct **,与mpz_t *arg类型不兼容,这就是编译警告的根源。
无需强制转换的安全写法
方法一:调整arg的类型为__mpz_struct *
这是最简洁高效的方案,完全避免类型不匹配问题:
#include <gmp.h> extern void g(mpz_t in); extern mpz_t temp; /* initialized elsewhere */ void f(mpz_t in, int flag) { __mpz_struct *arg; if (flag) { mpz_mul_ui(temp, in, 2); arg = temp; // temp是数组,自动退化为指向首元素的__mpz_struct*指针 } else { arg = in; // in已经是退化后的__mpz_struct*指针 } // 将__mpz_struct*转换为mpz_t(即__mpz_struct[1]),符合g函数的参数要求 g((mpz_t)arg); }
这种写法利用了mpz_t是长度为1的数组这一特性,将指针“包装”为数组类型传递给g函数,类型完全匹配,无任何安全风险。
方法二:用临时mpz_t变量中转
如果必须保留mpz_t *arg的类型,可以在else分支创建临时变量复制输入值:
void f(mpz_t in, int flag) { mpz_t *arg; if (flag) { mpz_mul_ui(temp, in, 2); arg = &temp; g(*arg); } else { mpz_t temp_in; mpz_init_set(temp_in, in); // 复制in的值到临时变量 arg = &temp_in; g(*arg); mpz_clear(temp_in); // 清理临时变量 } }
注意:这种方式会产生额外的内存分配和数据复制,仅适合无法修改arg类型的场景,且临时变量仅在当前分支有效。
必须强制转换时的正确写法
如果一定要保留mpz_t *arg且不想复制数据,正确的强制转换写法如下:
else { // 将__mpz_struct*类型的in,强制转换为mpz_t*(即__mpz_struct (*)[1]) arg = (mpz_t *)∈ }
这种转换是安全的:因为mpz_t本质是包含单个__mpz_struct的数组,in指向的__mpz_struct实例与mpz_t的内存布局完全一致,解引用*arg后得到的类型符合g函数的参数要求。
不过需要注意:该写法依赖GMP的mpz_t是长度为1的数组这一实现细节(GMP官方长期保持该定义的可能性极高),因此优先推荐方法一的无转换方案。
内容的提问来源于stack exchange,提问作者Hugo van der Sanden

