【问题标题】:Why void pointer if pointers can be casted into any type(in c)?如果指针可以转换为任何类型(在 c 中),为什么要使用 void 指针?
【发布时间】:2019-09-17 18:45:47
【问题描述】:

我想了解拥有一个 void 指针的真正需要,例如在下面的代码中,我使用强制转换能够以不同的方式使用相同的 ptr,那么如果可以的话,为什么真的有一个 void 指针铸造?

int main()
{
    int x = 0xAABBCCDD;
    int * y = &x;
    short * c = (short *)y;
    char * d = (char*)y;
    *c = 0;
    printf("x is %x\n",x);//aabb0000
    d +=2;
    *d = 0;
    printf("x is %x\n",x);//aa000000

    return 0;

}

【问题讨论】:

  • Void 指针不必显式强制转换,也不能被取消引用。此外,意图内置于语法中。你可以正确地假设,给定void * raw;,raw 可能会被转换为不同的类型。而如果您有 char * raw;,则不清楚这是字节数组还是可变类型。
  • @AhmadAnwar 那么你建议如何定义函数void *memset(void *dest, int c, size_t count);?而且您的代码违反了strict aliasing rule
  • 我真正看到的唯一原因是上面提到的严格的别名规则。

标签: c pointers void


【解决方案1】:

基本 C 不支持将任何指针类型转换为任何其他指针类型(即,没有 C 标准不需要的任何扩展或行为的 C)。 2018 C 标准在第 6.3.2.3 条第 7 段中说:

指向对象类型的指针可以转换为指向不同对象类型的指针。如果结果指针未正确对齐引用的类型,则行为未定义。否则,当再次转换回来时,结果将等于原始指针......

在那篇文章中,我们看到了两个限制:

  • 如果指针未正确对齐,转换可能会以各种方式失败。在您的示例中,将int * 转换为short * 不太可能失败,因为int 通常比short 具有更严格的对齐方式。但是,基本 C 不支持反向转换。假设您使用short x[20];char x[20]; 定义了一个数组。然后数组将根据shortchar 的需要对齐,但不一定根据int 的需要对齐,在这种情况下(int *) x 的行为不会由C 标准定义。
  • 转换产生的值大多未指定。这段话只保证将它转换回来会产生原始指针(或等效的东西)。它不能保证您可以在不将其转换回来的情况下对指针执行任何有用的操作——您不一定要使用从 int * 转换而来的指针来访问 short

该标准确实对某些指针转换做出了一些额外的保证。其中之一是上述段落的延续:

… 当指向对象的指针转换为指向字符类型的指针时,结果指向对象的最低寻址字节。结果的连续递增,直到对象的大小,产生指向对象剩余字节的指针。

因此,您可以使用从int * 转换而来的指针来访问表示int 的各个字节,并且您可以执行相同的操作来访问任何其他对象类型的字节。但这种保证仅适用于访问具有字符类型的单个字节,而不是 short 类型。

从上面我们知道,在你的例子中short * c = (short *)y;之后,y不一定指向它源自的x的任何部分——指针转换产生的值不能保证有效作为short *。但是,即使它确实指向 x 所在的位置,base C 也不支持使用 c 访问这些字节,因为 6.5 7 说:

对象的存储值只能由具有以下类型之一的左值表达式访问:

——与对象的有效类型兼容的类型,

——与对象的有效类型兼容的类型的限定版本,

——对象的有效类型对应的有符号或无符号类型,

— 有符号或无符号类型,对应于对象有效类型的限定版本,

——在其成员中包含上述类型之一的聚合或联合类型(递归地,包括子聚合或包含联合的成员),或

——一种字符类型。

因此,C 不支持您的示例中的 *c = 0;,原因有两个:c 不一定指向 x 的任何部分或任何有效地址,即使是这样, C 标准未定义使用 short 类型修改 int x 的一部分。它可能在您的 C 实现中工作,甚至可能由您的 C 实现支持,但它不是严格符合 C 代码。

C 标准提供了void * 类型,以便在特定类型不适用时使用。 6.3.2.3 1 对指向void 的指针做出与对指向对象的指针类似的保证:

指向void 的指针可以转换为或从指向任何对象类型的指针转​​换。指向任何对象类型的指针可以转换为指向void 的指针并再次返回;结果应与原始指针比较。

void * 用于必须处理任意对象类型的例程,例如qsortchar * 可以达到这个目的,但最好有一个单独的类型,清楚地表明没有特定类型与之关联。例如,如果函数的参数是char *p,则该函数可能会无意中使用*p 并获得它不想要的字符。如果参数为void *p,则函数必须将指针转换为特定类型,然后才能使用它来访问对象。因此,为“通用指针”设置一个特殊类型可以帮助避免错误,并向阅读代码的人表明意图。

【讨论】:

  • 您是否阅读过“指向任何对象类型的指针可以转换为指向void 的指针然后再返回”需要转换为并且来自相同的类型,使得“结果应与原始指针比较。”。我会这样读——这是错误的看待它的方式吗?这将使其与第 6.3.2.3 条第 7 段一致。
  • @DavidC.Rankin:是的,转换必须回到相同的类型。对于指针,除了void * 和空指针常量之外,无论如何只能比较兼容的类型(具有const 之类的条件)。
【解决方案2】:

如果指针可以转换为任何类型(在 c 中),为什么要使用 void 指针?

C 没有指定 void* 可以转换为 any 类型的指针。 void * 可以转换为指向任何 object 类型的指针。 IOWs,void * 可能不足以完全存储 function 指针。

需要一个空指针

void *object 类型的通用指针。抛开指向constvolatile 等关注点的指针,malloc(), memset() 等函数提供了分配和移动/设置数据的通用方法。

在更新颖的架构中,int *void * 等有不同的大小和解释。 void* 是对象的通用指针类型,足以存储信息以重构原始指针,无论指向的对象类型如何。

【讨论】:

    猜你喜欢
    • 2021-09-20
    • 1970-01-01
    • 2011-10-27
    • 1970-01-01
    • 2011-07-31
    • 1970-01-01
    • 2016-06-02
    • 2013-06-05
    相关资源
    最近更新 更多