问题描述
在下面的示例中,非const 限定指针b
被传递给func
,它需要一个指向const 限定类型的指针。因此,我希望编译器发出警告。但是,如果我用 clang 和 -Wall -Wextra -pedantic
编译这个程序,则没有警告。这是为什么?
#include <stdlib.h>
#include <string.h>
#define N 5
struct Bear {
int n;
int v;
int *data;
};
void func (const struct Bear *bear,int *data,const size_t n)
{
memcpy (bear->data,data,n);
}
int main (void)
{
int arr[N];
int *mem = malloc (N*sizeof (int));
if (mem == NULL) return -1;
struct Bear *b = &(struct Bear){1,2,mem};
func (b,arr,N);
free (mem);
return 0;
}
此外,如果我将结构初始化的行更改为
const struct Bear *b = &(const struct Bear){1,mem};
仍然没有警告。我知道结构声明中的 const
应用于其字段,因此,bear->v = 11;
中的 func
显然是一个错误(并产生警告)。然而,对于指针来说,这似乎并非如此。
这是未定义的行为吗?我在这里错过了什么?
解决方法
转换指针
当调用带有原型的函数时,参数(b
,类型为struct Bear *
)被转换为对应参数的类型(bear
,类型为{{1} }),根据 C 2018 6.5.2.2 7:
如果表示被调用函数的表达式的类型确实包含原型,则参数会被隐式转换,就像通过赋值一样,转换为相应参数的类型......
6.5.16.1 1 中的赋值约束包括允许转换为指向相同类型的指针,但添加了诸如 const struct Bear *
之类的限定符:
……左操作数具有原子、限定或非限定指针类型,并且……两个操作数都是指向兼容类型的限定或非限定版本的指针,并且左侧指向的类型具有指向的类型的所有限定符正确的……
此外,在 C 2018 6.3.2.3 中讨论了指针的转换,其中第 2 段说:
对于任何限定符q,指向非q限定类型的指针可以转换为指向q限定类型的指针类型的版本...
很简单,限定词是对事物使用的限制;他们说它旨在用于该事物的某些潜在用途的子集。 const
限定符表示该对象将仅用于读取其值,而不是修改其值。1 因此,将指针传递给可能 被修改为一个函数,表明它不会修改它。所以编写 C 标准的规则是为了允许这样做。
const
应用于结构
我知道结构声明中的 const 应用于其字段......
如果结构是const
,则结构成员const
是data
,但该成员是一个指针。所以这只是意味着指针是const
。这并不意味着它指向的是const
。
脚注
1const
限定符并不是修改对象的完全障碍。如果一个对象是在没有 const
的情况下定义的,则可以将添加了 const
的指向它的指针(通过强制转换)转换回没有 const
的指针并用于修改该对象.