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

Ahm*_*war 2 c pointers void

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

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;

}
Run Code Online (Sandbox Code Playgroud)

Eri*_*hil 6

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

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

在那段经文中,我们看到了两个限制:

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

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

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

因此,您可以使用转换为 from 的指针int *来访问代表 an 的各个字节int,并且您可以使用相同的方法访问任何其他对象类型的字节。但该保证仅适用于访问具有字符类型的单个字节,而不适用于short类型。

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

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

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

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

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

— 一种类型,它是与对象有效类型的限定版本相对应的有符号或无符号类型,

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

— 一种字符类型。

所以,*c = 0;在你的例子不是由下两个原因支持:c并不一定指向的任何部分x或任何有效的地址,而且,即使是这样,修改的部分的行为int x使用short类型不是由C定义标准。它可能看起来适用于您的 C 实现,甚至可能受您的 C 实现支持,但它并不严格符合 C 代码。

C 标准提供了void *在特定类型不合适时使用的类型。6.3.2.3 1void为指向对象的指针提供了类似的保证:

指向的指针void可以与指向任何对象类型的指针相互转换。指向任何对象类型的指针都可以转换为指向void和返回的指针;结果应与原始指针相等。

void *与必须使用任意对象类型的例程一起使用,例如qsort. char *可以用于此目的,但最好有一个单独的类型,明确表示没有特定类型与之关联。例如,如果函数的参数是char *p,则该函数可能会无意中使用*p并获取它不想要的字符。如果参数是void *p,则函数必须在使用指针访问对象之前将指针转换为特定类型。因此,为“通用指针”使用特殊类型可以帮助避免错误以及向阅读代码的人表明意图。