C函数const多维数组参数中的奇怪警告

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 *xconst T *x预计,到明确的异常,但T *xconst T *x仍然不同的类型,所以你不能一个指针传递给任何一个的指针到另一个.


Ada*_*eld 6

为了解释约瑟夫所说的:这个函数期望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,volatilerestrict预选赛)的第一指针的水平.你不能将它们添加到更高级别的指针,因为正如DigitalRoss所提到的那样,这样做会让你意外地违反const-correctness.这就是约瑟夫所说的"唯一允许在赋值中添加限定符的情况,参数传递等是直接指针目标上的限定符,而不是那些嵌套得更深的东西."

所以,让我们回到约瑟夫的回答,你不能将a转换成a pointer to array[4] of double,pointer to array[4] of const double因为没有类型X,你正在转换pointer to Xpointer to const X.

如果你尝试使用array[4] of doublefor X,你会看到你可以转换为pointer to const array[4] of double,这是一种不同的类型.但是,C中不存在这样的类型:你可以有一个const类型的数组,但是没有这样的东西const array.

因此,没有办法完美地解决您的问题.您必须为所有函数调用添加强制转换(手动或通过宏或辅助函数),重写函数以不接受const参数(错误,因为它不允许您传入const矩阵),或更改mat4如用户502515所建议的那样,将类型设置为一维数组或结构.