【问题标题】:Dereferencing pointer to array of void取消引用指向 void 数组的指针
【发布时间】:2011-10-20 07:28:09
【问题描述】:

我正在尝试更多地了解 C 及其神秘的隐藏功能,并且我尝试制作一个包含指向 void 的指针的示例结构,旨在用作数组。 编辑:重要提示:这是针对原始 C 代码的。

假设我有这个结构。

    typedef struct mystruct {
        unsigned char foo;
        unsigned int max;
        enum data_t type;
        void* data;

    } mystruct;

我希望数据保存 max 无符号字符、无符号短整数和无符号长整数,data_t 枚举包含 这 3 种情况的值。

    enum Grid_t {gi8, gi16, gi32}; //For 8, 16 and 32 bit uints.

然后我有这个函数来初始化和分配其中一个结构,并且应该返回一个指向新结构的指针。

    mystruct* new(unsigned char foo, unsigned int bar, long value) {
        mystruct* new;
        new = malloc(sizeof(mystruct)); //Allocate space for the struct.
        assert(new != NULL);
        new->foo = foo;
        new->max = bar;
        int i;
        switch(type){
            case gi8: default:
                new->data = (unsigned char *)calloc(new->max, sizeof(unsigned char));
                assert(new->data != NULL);
                for(i = 0; i < new->max; i++){
                    *((unsigned char*)new->data + i) = (unsigned char)value;
                    //Can I do anything with the format new->data[n]? I can't seem
                    //to use the [] shortcut to point to members in this case!
                }
            break;
        }
        return new;
    }

编译器不返回任何警告,但我不太确定这种方法。使用指针是否合法?

有没有更好的方法©?

我错过了呼叫它。像 mystruct* P; P = 新的(0,50,1024);

工会很有趣,但不是我想要的。由于无论如何我都必须单独处理每个特定案例,因此铸造似乎与工会一样好。我特别希望 8 位数组比 32 位数组大得多,所以联合似乎没有帮助。为此,我将其设为 long 数组:P

【问题讨论】:

  • 要回答 cmets 中的问题,您不能使用 [] 因为取消引用 void 指针是非法的。如果您使用 void 指针,那么您需要在每个取消引用周围都使用该 switch 语句,以便您可以在运行时将指针转换为正确的类型。
  • 这实际上是我的意图。
  • 您的代码只是 C99,而不是“原始 C”。 “原始C”是C89。您的代码不起作用,在“switch(type)”中“type”未定义。
  • @user4 这是在帖子中做模型时的拼写错误,而不是在实际代码中。而且之前就被发现了。

标签: c arrays pointers void dereference


【解决方案1】:

不,您不能取消引用 void* 指针,这是 C 语言标准所禁止的。在这样做之前,您必须将其转换为具体的指针类型。

作为替代方案,根据您的需要,您还可以在结构中使用union 而不是void*

typedef struct mystruct {
    unsigned char foo;
    unsigned int max;
    enum data_t type;
    union {
        unsigned char *uc;
        unsigned short *us;
        unsigned int *ui;
    } data;
} mystruct;

在任何给定时间,data.ucdata.usdata.ui 中只有一个是有效的,因为它们都占用相同的内存空间。然后,您可以使用适当的成员来获取您的数据数组,而无需从 void* 进行转换。

【讨论】:

    【解决方案2】:

    怎么样

    typedef struct mystruct 
    {
        unsigned char foo;
        unsigned int max;
        enum data_t type;
        union
        {
            unsigned char *chars;
            unsigned short *shortints;
            unsigned long *longints; 
        };
    } mystruct;
    

    这样一来,就根本不需要施法了。只需使用data_t 来确定您要访问的指针。

    【讨论】:

    • 是的,这也可以。危险,但 C 假设您知道自己在做什么。
    • 我读过一些关于联合的文章...它使用的内存与较大的元素(长)或分配的最大元素一样多吗? (所以如果我分配 255,它在内存存储中是否算长?)。 (我假设是前者,但以防万一)
    • @roger 在这种情况下它们的大小都相同,因为它们是指针,但通常是的,联合占用与其最大成员一样多的空间。
    • @roger_rales:它使用最大指针的大小。通常,指针的大小都相同。对齐当然会起作用,但它们的对齐方式可能相同。
    • @mkb:可能不比铸造更危险。在那里,编译器还假设您知道自己在做什么。 ;-)
    【解决方案3】:

    type 应该是函数的参数吗? (不要命名这个函数或任何变量new 或任何试图使用它的 C++ 程序员会追捕你)

    如果你想使用数组索引,你可以使用这样的临时指针:

    unsigned char *cdata = (unsigned char *)new->data;
    cdata[i] = value;
    

    我真的不认为你的方法有问题。如果你期望一个特定的大小(我认为你确实给出了名称 gi8 等),我建议包括 stdint.h 并使用 typedefs uint8_tuint16_tuint32_t

    【讨论】:

    • 抱歉,我在编辑发帖功能时忽略了这一点。类型实际上是完整的sn-p中的一个参数,我的错!我看到了临时指针,我可能会喜欢使用这种方法(使转换更具可读性 IMO)。仅作记录,有没有办法使用 foo->bar[0] 语法?
    • 正如其他海报所写,union 可以做到这一点。你可以有ptr_to_mystruct-&gt;chars[max-1]
    【解决方案4】:

    指针只是内存空间中的一个地址。您可以根据自己的意愿选择解释它。查看union,了解有关如何以多种方式解释同一内存位置的更多信息。

    指针类型之间的转换在 C 和 C++ 中很常见,使用 void* 意味着您不希望用户意外取消引用(取消引用 void* 会导致错误,但在转换为 int* 时取消引用相同的指针会不是)

    【讨论】:

      猜你喜欢
      • 2012-03-25
      • 1970-01-01
      • 1970-01-01
      • 2021-07-19
      • 2015-01-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多