首页 文章

无效指针假装是无效的双指针

提问于
浏览
0

我一直在思考 . 我没有找到任何直接回答这个问题的东西,但我想我知道答案;我只想要一些经验丰富的人提供一些意见 .

的已知,:

void指针只指向一个内存地址 . 它不包含类型信息 .

int指针指向包含int的内存地址 . 它将读取指向整数的内存地址中的任何内容,无论最初填充到地址中的是什么 .

题:

如果void双指针 void ** foo 指向动态分配的void指针数组

void ** foo = malloc(sizeof(void *) * NUM_ELEMENTS);

是否正确,正如我所假设的那样,由于void指针的独特性质实际上缺少任何类型的信息,而不是 void ** foo 而是一个等价的声明

void * bar = malloc(sizeof(void *) * NUM_ELEMENTS);

当我通过分配特定类型(例如with)使用间接访问时

(有人指出我无法取消引用无效指针 . 为了清楚问题的目的,下一行被更改为适合该信息)

int ** fubar = bar;

我会从单个void指针得到一个适当的指针,它只是 acting 像一个双指针?

或者这一切都在我脑海里?

2 回答

  • 0

    允许将 malloc 的结果分配给 void * 对象,然后将其分配给 int ** 对象 . 这是因为 malloc 的返回值无论如何都具有 void * 类型,并且它保证适合于为具有基本对齐要求的任何类型的对象赋值 .

    但是,这段代码:

    #define NUM_ELEMENTS 1000
    void *bar = malloc(sizeof(void *) * NUM_ELEMENTS);
    int **fubar = bar;
    *fubar = 0;
    

    不符合C标准的保证;它可能有不确定的行为 . 原因并不明显 . C标准不要求不同类型的指针具有相同的大小 . C实现可以将 int * 的大小设置为一百万字节,将 void * 的大小设置为四个字节 . 在这种情况下,为1000 void * 分配的空间不足以容纳一个 int * ,因此对 *fubar 的赋值具有未定义的行为 . 通常,人们会以这种方式实现C只是为了证明一个观点 . 但是,类似的错误可能在较小的范围内:有C实现,其中不同类型的指针具有不同的大小 .

    如果指针具有适合于目标类型的对齐,则指向对象类型的指针可以被转换为指向另一对象类型的指针 . 如果是,则将其转换回来产生具有原始值的指针 . 因此,您可以将指针转换为 void * 指针指向 void 并返回,并且您可以将指针转换为 void * 指针指向 int * 并返回,前提是对齐是合适的(如果指针由 malloc 返回,它们将是使用具有扩展对齐的自定义对象) .

    通常,您不能使用指向对象类型的指针进行编写,然后使用指向不同对象类型的指针读取相同的字节 . 这违反了别名规则 . 一个例外是,如果其中一个指针是字符类型 . 此外,许多C实现确实支持这种别名,但可能需要设置命令行选项来启用此类支持 .

    这种对别名的禁止包括重新解释指针 . 考虑以下代码:

    int a;
    int *b = &a;
    void **c = (void **) &b;
    void *d = *c;
    int *e = (int *) d;
    

    在第四行中, c 指向 b 占用的字节,但 *c 尝试将这些字节解释为 void * . 这不能保证工作,因此 d 获取的值不一定是指向 a 的指针,即使它在最后一行转换为 int * 也是如此 .

  • 2

    在C标准下,您给出的代码的行为是未定义的,因为您分配了一个 void 指针数组,然后尝试将其用作 int 指针的数组 . 标准中没有任何内容要求这两种指针具有相同的大小或对齐方式 . 现在,如果你说过

    void * bar = malloc(sizeof(int*) * NUM_ELEMENTS);
    int ** fubar = bar;
    

    那一切都没问题 .

    现在在绝大多数机器上, int*void* 实际上具有相同的大小和对齐方式 . 所以你的代码应该在实践中运作良好 .

    另外,这两个不等同:

    void ** foo = malloc(sizeof(void *) * NUM_ELEMENTS);
    void * bar = malloc(sizeof(void *) * NUM_ELEMENTS);
    

    这是因为 foo 可以在任何元素处取消引用以获得void指针,而 bar 则不能 . 例如,此程序是正确的,并在我的32位计算机上打印 00000000

    #include <stdio.h>
    #include <stdlib.h>
    
    int main(void) 
    {
      void **a = calloc(10, sizeof(void*));
      printf("%p\n", a[0]);
      return 0;
    }
    

    另一点是你似乎认为类型信息在机器级别的指针中是显式的 . 这不是真的(至少对于绝大多数实现而言) . C指针的类型通常仅在编译程序时表示 . 在编译完成时,显式类型信息通常会丢失,除非调试符号表是不可运行的代码 . (对此有一些小的例外 . 而对于C,情况则非常不同 . )

相关问题