rog*_*ogi 7 c arguments const function multidimensional-array
我对这段代码有一些奇怪的警告:
typedef double mat4[4][4];
void mprod4(mat4 r, const mat4 a, const mat4 b)
{
/* yes, function is empty */
}
int main()
{
mat4 mr, ma, mb;
mprod4(mr, ma, mb);
}
Run Code Online (Sandbox Code Playgroud)
gcc 输出如下:
$ gcc -o test test.c
test.c: In function 'main':
test.c:13: warning: passing argument 2 of 'mprod4' from incompatible pointer
type
test.c:4: note: expected 'const double (*)[4]' but argument is of type 'double
(*)[4]'
test.c:13: warning: passing argument 3 of 'mprod4' from incompatible pointer
type
test.c:4:
note: expected 'const double (*)[4]' but argument is of type 'double
(*)[4]'
Run Code Online (Sandbox Code Playgroud)
如果我将函数定义为:
void mprod4(mat4 r, mat4 a, mat4 b)
{
}
Run Code Online (Sandbox Code Playgroud)
或者在main中定义矩阵:
mat4 mr;
const mat4 ma;
const mat4 mb;
Run Code Online (Sandbox Code Playgroud)
或者在main中调用函数:
mprod4(mr, (const double(*)[4])ma, (const double(*)[4])mb);
Run Code Online (Sandbox Code Playgroud)
甚至定义mat4为:
typedef double mat4[16];
Run Code Online (Sandbox Code Playgroud)
使警告消失.这里发生了什么?我做的事情无效吗?
如果相关,gcc版本是4.4.3.
我还发布了gcc bugzilla:http://gcc.gnu.org/bugzilla/show_bug.cgi?id = 47143
我目前的解决方法是制作丑陋的宏,为我投射东西:
#ifndef _NO_UGLY_MATRIX_MACROS
#define mprod4(r, a, b) mprod4(r, (const double(*)[4])a, (const double(*)[4])b)
#endif
Run Code Online (Sandbox Code Playgroud)
Joseph S. Myers对gcc bugzilla的回答:
不是错误.函数参数的类型是"指向const double的数组[4]",因为数组类型的const递归地应用于元素类型,然后只有数组类型的参数的最外层数组类型衰减到指针,并且在数组到指针衰减之后传递的参数是"指向数组[4]的指针"类型,并且唯一允许在赋值,参数传递等中添加限定符的情况是立即指针上的限定符目标,而不是那些更深层次的嵌套.
对我来说听起来很混乱,就像函数所期望的那样:
pointer to array[4] of const doubles
Run Code Online (Sandbox Code Playgroud)
我们正在过世
pointer to const array[4] of doubles
Run Code Online (Sandbox Code Playgroud)
这一翻译.
或者它会反过来?警告表明该功能需要:
const double (*)[4]
Run Code Online (Sandbox Code Playgroud)
在我看来更像是一个
pointer to const array[4] of doubles
Run Code Online (Sandbox Code Playgroud)
我真的很困惑这个答案.有人明白他说的话会澄清并举例说明吗?
Dig*_*oss 12
我认为问题是C99 6.5.16.1(1)中规定的约束,它似乎禁止在赋值中混合限定,除了定义了包含限定符异常的指针.问题是,使用间接指针,最终会将指向一个事物的指针传递给指向另一个事物的指针.赋值无效,因为如果是,您可以使用以下代码来修改const限定对象:
const char **cpp;
char *p;
const char c = 'A';
cpp = &p; // constraint violation
*cpp = &c; // valid
*p = 0; // valid by itself, but would clobber c
Run Code Online (Sandbox Code Playgroud)
cpp承诺不修改任何chars的似乎是合理的,可能会指向指向非限定chars 的对象的指针.毕竟,允许使用单间接指针,这就是为什么,例如,您可以将可变对象传递给strcpy(3)第一个参数的第二个参数strchr(3),以及许多其他声明的参数const.
但是使用间接指针,在下一级别,允许从限定指针进行赋值,现在一个完全不合格的指针赋值将破坏一个限定对象.
我没有立即看到2-D阵列如何导致这种情况,但无论如何它都会遇到标准中的相同约束.
因为在你的情况下,你实际上并没有欺骗它来破坏const,你的代码的正确之处似乎是插入强制转换.
更新:好的家伙,因为它发生了这个问题在C faq中,整个讨论也在gcc错误列表和gcc邮件列表上多次发生.
教训:您可以传递T *x时const T *x预计,到明确的异常,但T *x和const T *x仍然不同的类型,所以你不能一个指针传递给任何一个的指针到另一个.
为了解释约瑟夫所说的:这个函数期望pointer to array[4] of const double传入,但是你传递了一个pointer to array[4] of double.这些类型不兼容,因此您会收到错误.他们看起来应该兼容,但他们不是.
为了将参数传递给函数(或用于变量赋值),您始终可以将Xa 转换为a const X或a pointer to X转换pointer to const X为任何类型X.例如:
int x1 = 0;
const int x2 = x1; // ok
int *x3 = &x1;
const int *x4 = x3; // ok: convert "pointer to int" to "pointer to const int"
int **x5 = &x3;
const int **x6 = x5; // ERROR: see DigitalRoss's answer
int *const *x7 = x5; // ok: convert "pointer to (pointer to int)" to
// "pointer to const (pointer to int)"
Run Code Online (Sandbox Code Playgroud)
你只允许加限定词(也就是const,volatile和restrict预选赛)的第一指针的水平.你不能将它们添加到更高级别的指针,因为正如DigitalRoss所提到的那样,这样做会让你意外地违反const-correctness.这就是约瑟夫所说的"唯一允许在赋值中添加限定符的情况,参数传递等是直接指针目标上的限定符,而不是那些嵌套得更深的东西."
所以,让我们回到约瑟夫的回答,你不能将a转换成a pointer to array[4] of double,pointer to array[4] of const double因为没有类型X,你正在转换pointer to X为pointer to const X.
如果你尝试使用array[4] of doublefor X,你会看到你可以转换为pointer to const array[4] of double,这是一种不同的类型.但是,C中不存在这样的类型:你可以有一个const类型的数组,但是没有这样的东西const array.
因此,没有办法完美地解决您的问题.您必须为所有函数调用添加强制转换(手动或通过宏或辅助函数),重写函数以不接受const参数(错误,因为它不允许您传入const矩阵),或更改mat4如用户502515所建议的那样,将类型设置为一维数组或结构.