【问题标题】:Is casting to pointers to pointers to void always safe?将指针转换为指向 void 的指针总是安全的吗?
【发布时间】:2013-05-15 03:40:07
【问题描述】:
#include <stdio.h>

void swap(void *v[], int i, int j)
{
    void *tmp;

    tmp = v[i];
    v[i] = v[j];
    v[j] = tmp;
}

int main(void)
{
    char *s[] = {"one", "two"};
    printf("%s, %s\n", s[0], s[1]);
    swap(s, 0, 1);
    printf("%s, %s\n", s[0], s[1]);
    return 0;
}

输出

one, two

two, one

警告: no compatible pointer casting, need void**, but char

我用这个程序模拟了K&R中的swap函数,演示了函数指针的使用,我的问题是void pointer的转换是否总是安全的,或者是否存在有什么办法可以代替它。

【问题讨论】:

  • 我使用 gcc std=c99 编译它。
  • 真正发生的是,指针就像句柄或地址。你不是在交换数据,而是将地址交换到数据。所以,还是挺安全的。
  • @Aniket:不,这不一定安全。想象一个系统,其中char* 的大小与int* 的大小不同。

标签: c pointers void


【解决方案1】:

不,传递 char** 不一定安全,而 void**(实际上是 void*[] 函数参数)是预期的。编译器让您执行显式转换这一事实暗示了这一点。

在实践中,它可能没问题。但是,严格来说,您通常不能保证 sizeof (T*) == sizeof (U*) 用于不同的类型 TU。 (例如,您可以想象一个假设系统,其中sizeof (int*) &lt; sizeof (char*) 因为指向int 的指针是对齐的,因此不需要存储最低有效位。)因此,您的swap 函数可能会索引到@ 987654334@ 数组使用了错误的偏移量。

另请参阅 comp.lang.c 常见问题解答中的 Q4.9:Can I give the formal parameter type void **, and do something like this?

要安全地拨打swap,您应该执行以下操作:

void* temp[] = { &s[0], &s[1] };
swap(temp, 0, 1);

虽然这会交换 temp 的元素,而不是 s 的元素。

如果您正在创作swap,通常您应该使此类函数采用void* 参数(而不是void** 一个)和一个指定每个元素大小的size_t 参数。然后,您的函数可以安全地将 void* 转换为 char* 并交换各个字节:

void swap(void* p, size_t elementSize, size_t i, size_t j)
{
    char* item1 = p;
    char* item2 = p;

    item1 += i * elementSize;
    item2 += j * elementSize;

    while (elementSize-- > 0) {
        char temp = *item1;
        *item1 = *item2;
        *item2 = temp;
        item1++;
        item2++;
    }
}

编辑: 另见this StackOverflow answer to a similar question

【讨论】:

  • 出色的答案!谢谢你,jamesdlin,但在我的电脑中,所有指针似乎都是等价的,我按照你的链接,找到了 [c-faq.com/null/machexamp.html],这就是我想要找到的。跨度>
  • @pupu007 即使相等,如果您使用问题中的代码,您仍然会在程序中调用未定义的行为。
  • 保证sizeof(char*)==sizeof(void*).
  • @n.'pronouns'm。不,没有。不是根据 ISO C(和这个答案)。你有这方面的参考吗?
  • @BenjaminCrawfordCtrl-Alt-Tut port70.net/~nsz/c/c11/n1570.html#6.2.5p28
【解决方案2】:

您需要在交换调用中对指针进行类型转换。改成swap ( ( void * )s, 0, 1 );

【讨论】:

  • @Jens Gustedt:( void * )s 是否从 void * 更改为不同的类型?
  • ( void * )s 本身不是,但由于swap 的第一个参数是void**,因此在显式转换之后和调用函数之前立即进行隐式转换。
  • @unxnut:你实际上是在投射char** => void* => void**char**void** 不是同一类型。
  • 不,代码只是强制转换 char * => void * 并交换 void * 指针,有效地交换 char * 指针。它不会以任何方式取消引用 void * 指针。
  • @unxnut:不,swap 需要 void**。虽然它不取消引用 void* 类型,但它确实取消引用 void**(通过 v[i]v[j])并假定 sizeof (void*) == sizeof (char*)
【解决方案3】:

为避免警告调用函数如下,

swap((void *) s, 0, 1);

将任何指针转换为 void 指针总是安全的。

【讨论】:

  • 您实际上是将char** 转换为void*,然后将其隐式转换为void**。将T** 转换为void** 不一定安全。
  • 这个答案是错误的。将void* 转换为始终是安全的,但从void* 到与原始类型不同的隐式转换则不是。
  • @JensGustedt 为什么?有些计算机对指向不同类型的指针做出不同的表示。所以 void *[] 可能会担心指向 char *[]
  • @pupu007 函数参数void*[] 等价于void**。我不明白你对“担心”的使用。
猜你喜欢
  • 1970-01-01
  • 2021-07-22
  • 1970-01-01
  • 2014-09-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-20
相关资源
最近更新 更多