C 函数 const 多维数组参数中的奇怪警告
我收到一些关于此代码的奇怪警告:
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);
}
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]'
如果我将函数定义为:
void mprod4(mat4 r, mat4 a, mat4 b)
{
}
或在 main 中定义矩阵为:
mat4 mr;
const mat4 ma;
const mat4 mb;
或在 main 中调用函数为:
mprod4(mr, (const double(*)[4])ma, (const double(*)[4])mb);
甚至定义 mat4
as:
typedef double mat4[16];
使警告消失。这里发生了什么?我是不是在做一些无效的事情?
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
Joseph S. Myers 在 gcc bugzilla 上的回答:
不是错误。函数参数 属于“指向 array[4] 的指针”类型 const double”因为 const 位于 数组类型适用于元素 递归地键入,然后 仅最外层数组类型 数组类型的参数衰减为 指针,传递的参数是 类型为“指向数组[4]的指针” double” 数组到指针衰减后, 唯一的情况是限定符是 允许添加到作业中, 参数传递等是限定符 立即指针目标,而不是 那些嵌套得更深。
对我来说听起来很混乱,就像函数所期望的那样:
pointer to array[4] of const doubles
我们正在传递
pointer to const array[4] of doubles
。
或者是相反的情况?警告表明该函数需要 a:
const double (*)[4]
在我看来更像是 a
pointer to const array[4] of doubles
我真的对这个答案感到困惑。有懂的人可以解释一下并举例说明吗?
I'm getting some strange warnings about this code:
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);
}
gcc
output as follows:
$ 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]'
If I define the function as:
void mprod4(mat4 r, mat4 a, mat4 b)
{
}
Or defining matrices in main as:
mat4 mr;
const mat4 ma;
const mat4 mb;
Or call the function in main as:
mprod4(mr, (const double(*)[4])ma, (const double(*)[4])mb);
Or even defining mat4
as:
typedef double mat4[16];
Makes the warning go away. What is happening here? Am I doing something invalid?
The gcc version is 4.4.3, if relevant.
I also posted on gcc bugzilla: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=47143
My current workaround is making ugly macros that cast stuff for me:
#ifndef _NO_UGLY_MATRIX_MACROS
#define mprod4(r, a, b) mprod4(r, (const double(*)[4])a, (const double(*)[4])b)
#endif
Answer from Joseph S. Myers on gcc bugzilla:
Not a bug. The function parameters
are of type "pointer to array[4] of
const double" because const on an
array type applies to the element
type, recursively, and then the
outermost array type, only, of a
parameter of array type decays to a
pointer, and the arguments passed are
of type "pointer to array[4] of
double" after array-to-pointer decay,
and the only case where qualifiers are
permitted to be added in assignment,
argument passing etc. is qualifiers on
the immediate pointer target, not
those nested more deeply.
Sounds pretty confusing to me, like the function expects:
pointer to array[4] of const doubles
and we are passing
pointer to const array[4] of doubles
intead.
Or would it be the inverse? The warnings suggest that the function expects a:
const double (*)[4]
which seems to me more like a
pointer to const array[4] of doubles
I'm really confused with this answer. Could somebody who understands what he said clarify and exemplify?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
我认为问题在于 C99 6.5.16.1(1) 中指定的约束,这些约束似乎禁止在赋值中混合限定,但包含限定符异常的指针除外被定义。问题在于,使用间接指针时,您最终会将指向一个事物的指针传递给指向另一个事物的指针。该赋值无效,因为如果有效,您可以使用以下代码欺骗它修改 const 限定对象:
cpp
看起来似乎很合理,它承诺不修改任何char
可能会被分配一个指向指向非限定char
的对象的指针。毕竟,这是允许单间接指针的,这就是为什么,例如,您可以将可变对象传递给strcpy(3)
的第二个参数,将第一个参数传递给strchr(3 )
,以及使用const
声明的许多其他参数。但是对于间接指针,在下一个级别,允许从合格的指针进行赋值,现在完全不合格的指针赋值将破坏合格的对象。
我没有立即看出二维数组如何导致这种情况,但无论如何它都会遇到标准中的相同约束。
由于在您的情况下,您实际上并没有欺骗它破坏 const,因此您的代码的正确做法似乎是插入强制转换。
更新:好的,伙计们,这个问题发生在 C faq,整个讨论也进行了多次在 gcc 错误列表 和 gcc 邮件列表上。
教训:当需要
const T *x
时,您可以通过显式异常传递T *x
,但是T *x
和const T *x
仍然是不同的类型,因此您不能将指向其中之一的指针传递给指向另一个的指针。I believe the problem is the constraints specified in C99 6.5.16.1(1), which seem to prohibit mixing qualifications in assignments, except for pointers for which an inclusive-qualifier exception is defined. The problem is that with indirect pointers, you end up passing a pointer to one thing to a pointer to another. The assignment isn't valid because, if it was, you could fool it into modifying a const-qualified object with the following code:
It might seem reasonable that
cpp
, which promises not to modify anychar
s, might be assigned a pointer to an object pointing at non-qualifiedchar
s. After all, that's allowed for single-indirect pointers, which is why, e.g., you can pass a mutable object to the second parameter ofstrcpy(3)
, the first parameter tostrchr(3)
, and many other parameters that are declared withconst
.But with the indirect pointer, at the next level, assignment from a qualified pointer is allowed, and now a perfectly unqualified pointer assignment will clobber a qualified object.
I don't immediately see how a 2-D array could lead to this situation, but in any case it hits the same constraint in the standard.
Since in your case, you aren't actually tricking it into clobbering a const, the right thing for your code would seem to be inserting the cast.
Update: OK guys, as it happens this issue is in the C faq, and this entire discussion has also taken place several times on the gcc bug list and on the gcc mailing list.
The lesson: you can pass a
T *x
whenconst T *x
is expected, by explicit exception, butT *x
andconst T *x
are still distinct types, so you can't pass a pointer to either one to a pointer to the other.解释一下 Joseph 所说的:该函数期望传入一个指向 const double 的 array[4] 的指针,但您传入的是一个指向 double 的 array[4] 的指针。这些类型不兼容,因此您会收到错误。它们看起来应该兼容,但事实并非如此。
为了将参数传递给函数(或变量赋值),您始终可以将
X
转换为const X
或指向 X 的指针
code> 指向任何类型X
的const X 指针
。例如:您只能向 添加限定符(即
const
、volatile
和restrict
限定符)第一层指针。您无法将它们添加到更高级别的指针,因为 DigitalRoss 提到,这样做会让你意外地违反常量正确性。这就是约瑟夫所说的“唯一的情况允许在赋值、参数传递等中添加限定符
直接指针目标上的限定符,而不是嵌套得更深的限定符。”
因此,让我们回到 Joseph 的响应,您无法将指向 double 的 array[4] 的指针转换为指针到 const double 的 array[4] ,因为没有类型
X
,因此您可以从指向 X 的指针
转换为指向 const X 的指针
如果您尝试将
array[4] of double
用于X
,您会发现可以转换为>指向 double 的 const array[4] 的指针,这是一种不同的类型,但是 C 中不存在这种类型:您可以拥有一个
const
类型的数组,但确实有。没有 const 数组这样的东西,因此,没有办法完美解决您的问题,您必须向所有函数调用添加强制转换(手动或通过宏或辅助函数)。 ),重写您的函数以不采用
const
参数(不好,因为它不允许您传入const
矩阵),或者更改mat4
类型为一维数组或结构体,如 用户502515建议。To explain what Joseph said: the function is expecting a
pointer to array[4] of const double
to be passed in, but you're passing in apointer to array[4] of double
. These types are not compatible, so you get an error. They look like they should be compatible, but they're not.For the purposes of passing parameters to functions (or for variable assignments), you can always convert an
X
to aconst X
, or apointer to X
to apointer to const X
for any typeX
. For example:You're only allowed to add qualifiers (that is, the
const
,volatile
, andrestrict
qualifiers) to the first level of pointers. You can't add them to higher levels of pointers because, as DigitalRoss mentioned, doing so would allow you to accidentally violate const-correctness. This is what Joseph means by "the only case wherequalifiers are permitted to be added in assignment, argument passing etc. is
qualifiers on the immediate pointer target, not those nested more deeply."
So, bringing us back to Joseph's response, you can't convert a
pointer to array[4] of double
to apointer to array[4] of const double
because there is no typeX
such that you're converting frompointer to X
topointer to const X
.If you try using
array[4] of double
forX
, you'd see that you can convert topointer to const array[4] of double
, which is a different type. However, no such type exists in C: you can have an array of aconst
type, but there is no such thing as aconst array
.Hence, there's no way to perfectly solve your problem. You'll have to either add casts to all of your function calls (either manually or via a macro or helper function), rewrite your functions to not take
const
parameters (bad since it doesn't let you pass inconst
matrices), or change themat4
type to be either a 1-dimensional array or a structure, as user502515 suggested.为了实际解决这个问题,可以使用一个结构体,并将
double[4][4]
更改为有点尴尬的double (*)[4]
避免了这种情况,并且常量也直观地发挥作用——同时使用相同数量的内存:To practically solve this, one could use a struct, and the change of
double[4][4]
into the a-bit-awkwarddouble (*)[4]
is avoided, and constness also works intuitively — while the same amount of memory is used:我认为在 C99 中,你可以这样做,但我不确定它会有帮助:
我手边没有 C99 编译器,但我记得读过 C99 规范中有关
[]
对于数组作为参数。您还可以在其中放置static
(例如ma[static 4]
),但这当然意味着其他含义。编辑
这是第 6.7.3.5 节第 7 段。
I think in C99, you can do this, but I'm not sure it will help:
I haven't got a C99 compiler handy but I remember reading something in the C99 specification regarding qualifiers within the
[]
for arrays as arguments. You can also putstatic
in there (e.g.ma[static 4]
) but of course that means something else.Edit
Here it is, section 6.7.3.5 paragraph 7.
这是一个问题(恕我直言):函数签名中的
double[4][4]
。您知道它是一个
double[4][4]
,但编译器在函数参数列表中看到double(*)[4]
,值得注意的是,它没有数组大小限制。它将 4 x 4 对象的 2D 数组转换为指向 4 个对象的 1D 数组的指针,并且该指针可以有效索引,就像它是 4 个对象的数组一样。我将通过指针传递所有
mat4
对象:这将(我相信)确保 const 正确性和数组大小正确性。它可能会使 mprod4 更难编写,并且仍然涉及一些毛茸茸的演员,但它(恕我直言)是值得的(特别是在上面的宏之后):
当你写它,但我认为它在类型上会更清晰。 (也许我对C++想太多了......)
Here's a problem (IMHO):
double[4][4]
in a function signature.You know it's a
double[4][4]
, but the compiler seesdouble(*)[4]
in the function paramter list, which notably has no array size constraint. It turns your 2D array of 4 by 4 objects into a pointer to a 1D array of 4 objects, and the pointer can be validly indexed as if it were an array of 4 objects.I would pass all
mat4
objects by pointer:This will (I believe) ensure const correctness and array size correctness. It may make
mprod4
a bit harder to write, and still involves some hairy casts, but it'll (IMHO) be worth it (especially after the macro above):It may look a bit bad when you write it, but I think it'll be cleaner type-wise. (Maybe I've been thinking C++ too much...)
编译器只是肛门。
您传递的参数本质上是非常量指针,并且该函数被声明为接受常量指针作为参数。事实上,这两者是不相容的。这不是一个真正的问题,因为只要您可以将第一种类型的值分配给第二种类型的变量,编译器仍然应该工作。因此是警告但不是错误。
编辑:看起来 gcc 不会抱怨其他 con-const 到 const 转换,例如在需要 const char* 的地方传递 char* 。在这种情况下,我倾向于同意 Bugzilla 的 Joseph Myers 是正确的。
Compiler is just being anal.
You're passing an argument that is essentially a non-const pointer, and the function is declared to accept a const pointer as an argument. These two are, in fact, incompatible. It is not a real problem because the compiler is still supposed to work as long as you can assign the value of the first type to the variable of the second type. Hence a warning but not an error.
EDIT: looks like gcc does not complain about other con-const to const conversions, e.g. passing char* where a const char* is expected. In this case, I'm inclined to agree that Joseph Myers from Bugzilla is correct.