【问题标题】:Convert const char** to void*? [duplicate]将 const char** 转换为 void*? [复制]
【发布时间】:2015-03-16 11:05:39
【问题描述】:

考虑这段代码:

#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*,从而产生警告。这是对正在发生的事情的正确解释吗?这是非标准行为,对吧?

【问题讨论】:

  • 我会说可能是一个 MS 编译器错误。虽然这样的代码让我毛骨悚然,但据我所知,它在技术上是完全合法的......
  • FWIW,迂腐模式下的 clang (3.4.1) 也认为你的代码没问题。
  • @mafso 不错,就是这个

标签: c pointers visual-c++ gcc


【解决方案1】:

编译器过于热心(读作:编译器错误)。

const char** caps 表示caps 是指向不同指针(也不是常数)的指针(不是常数),指向常数char。也就是说,您承诺不会通过超过 caps 的间接访问来修改 char
这意味着您正式与编译器签订以下合同:

  1. 您可以更改caps
  2. 您可以更改*capscaps 指向的char*)。
  3. 不允许更改**caps*caps 指向的charcaps 指向的))通过这个链指针 .
  4. 没有提及任何其他人(例如别名指针)更改该字符的值。

    const char **caps = malloc( sizeof( char * ) );

用一个合法的值初始化caps。如果malloc 失败,这个值是一个空指针,但是从语言的角度来看,这通常也是完全合法的(尽管它会导致下面的memset 崩溃)。在 C++ 中,您需要显式转换 malloc 返回的 void*,但 C 允许这种事情就好了。

memset(caps, 0, sizeof(char*));

让我毛骨悚然(我是一名 C++ 程序员),但从 C 语言的角度来看,这仍然是完全合法的事情。
它所做的是覆盖到目前为止已分配但未初始化(并由caps 指向)的内存块,该内存块包含第二个指针,其零字节数等于指针的大小(char 指针,如它发生了)。

库函数memset,它接受一个非常量void*,只是用你提供的值(这里:零)填充你要求的字节数(这里是sizeof(char*))。它不需要也不需要关心您与编译器签订的合同。但即便如此,它也没有违反任何规则。它会覆盖指针,而不是指向的常量值。
是的,它会将几个char 值写入一个不是chars 数组的东西(这就是我头发竖起来的原因),但是……那是…… 合法。毕竟,这正是memset 应该做的事情,而且它很可能会“按预期工作”。它将指针设置为零位模式,除了一些非常罕见的外来架构外,它对应于空指针。

内存位置**caps 从未更改(甚至被访问),因此所有这些都是完全合法的,您没有违反任何承诺。

因此警告是错误的。

【讨论】:

【解决方案2】:

您不能将指向常量 (const X *) 的指针强制转换为 void *:这将丢弃其 const 限定符。

memset 需要修改这些数据,这就是为什么它需要void * 参数而不是const void * 参数。

【讨论】:

  • 我认为你没有抓住重点。不是const X*,而是const X**memset 不会修改要成为 const 的部分。
  • 但是,这不是指向常量的指针。它是一个指向常量的指针。这应该编译得很好。使用const char**,您承诺不会修改您指向的指针通过此指针指向的charmemset 修改指针时不会发生这种情况。
  • @nyarlathotep108 实际上,使用多少层间接确实重要。
  • @nyarlathotep108 有很大的不同。给定const char* p = "foo"; const char** pp = &amp;p;,那么memset(p, ...) 是非法的,因为它试图修改p 指向的const char。相当于*p = '\0'。另一方面,memset(pp, ...) 应该完全没问题,因为它正在修改pp 指向的pointer-to-const(注意指针本身不是const);它相当于*pp = NULL。它只是让pp 指向别的东西,而不是修改const 数据。
  • memset 根本看不到任何const。它看到一个非常量的void*,它用(char) 0x00 填充sizeof(pointer) 字节,尽管从您的角度来看,指向的内存甚至不是char 类型!唉,memset 不(也不需要)关心。但是关于类型的判决仍然存在。这是一个非常量指针,指向一个不同的非常量指针(它正在被覆盖,并且合法地如此)指向一个常量char
【解决方案3】:

按照你写的方式,const 指的是 char,而不是指针。显然,分配内存只是为了声明它永远不会被修改(这意味着它永远不会被初始化)是没有意义的。

如果你希望指针本身是 const,你应该写:

char ** const caps

您也可以将第二个指针设为 const,但这并不比将 char 设为 const 更有意义。

【讨论】:

  • stjin 正在为指向const char 的指针分配内存;该指针本身是not const 并且可以被赋予本应为const 的地址。这种用法对我来说很有意义。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-07-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多