考虑以下代码:
#include <memory.h>
#include <stdlib.h>
void Foo()
{
const char ** caps = malloc( sizeof( char * ) );
memset( caps, 0, sizeof( char * ) );
}
使用gcc 4.9.2 -pedantic编译精细但是cl 18(来自VS2013的那个),默认选项在memset行上显示为warning C4090: 'function' : different 'const' qualifiers
。
现在caps
是一个指向const char指针的指针?所以指针本身不是const因此它应该毫无问题地转换为void*
,我认为,但cl似乎会自动从它生成const void*
生成警告。这是对正在发生的事情的正确解释吗?这是非标准行为,对吗?
答案 0 :(得分:3)
编译器过于热心(读作:编译器错误)。
const char** caps
表示caps
是一个指针(不是常数)指向不同的指针(也不是常量)到char
是不变的。也就是说,您承诺不会通过char
的间接来修改caps
这意味着您正式与编译器签订以下合同:
caps
。*caps
(char*
指向的caps
。**caps
(char
指向*caps
指向的caps
)通过这个链指针。没有其他任何人(例如别名指针)更改该字符的值。
const char ** caps = malloc(sizeof(char *));
使用值合法初始化caps
,这是合法的。如果malloc
失败,则此值为空指针,但从语言的角度来看,这通常也是完全合法的(尽管这会导致以下memset
崩溃)。在C ++中,您需要显式转换void*
返回的malloc
,但C允许这样的事情就好了。
memset(caps, 0, sizeof(char*));
让我的头发站起来(我是一名C ++程序员),但从C语言的角度来看它仍然是完全合法的。
它的作用是覆盖到目前为止已分配但尚未初始化(并由caps
指向的)内存块,该内存块包含第二个指针,其中零个字节的数量等于指针的大小({{1}指针,因为它发生)。
库函数char
采用非常量memset
,只需用您提供的值(此处为零)填充您要求的字节数(此处为void*
) 。关于您使用编译器签订的合同,它不需要也不需要关心。但即便如此,它也不违反任何规则。它会覆盖指针,不指向的常量值
是的,它会将一些sizeof(char*)
值写入不是 char
的数组(这就是为什么我的头发会起立),但是很好。那...... 法律。它毕竟是char
应该做的事情,并且它很可能会像预期的那样工作&#34;。它会将指针设置为零位模式,除了一些非常罕见的异构体系结构外,它对应于空指针。
内存位置memset
在任何时候都没有改变(甚至被访问),所以这一切都是完全合法的,你没有违背任何承诺。
因此警告是错误的。
答案 1 :(得分:-3)
您无法将常量(const X *
)指针投射到void *
:这会丢弃其const
限定符。
memset
需要修改这些数据,这就是为什么它需要void *
参数而不是const void *
参数。
答案 2 :(得分:-3)
你写它的方式,const引用char,而不是指针。显然,仅仅分配内存以声明它永远不会被修改(这意味着它永远不会被初始化)是没有意义的。
如果你希望指针本身是const,你应该写:
char ** const caps
你也可以使第二个指针成为常量,但这比使用char const更有意义。