【问题标题】:How can I declare and unpack an array of pairs of void pointers?如何声明和解压缩成对的 void 指针数组?
【发布时间】:2020-07-25 10:27:00
【问题描述】:

我想写一个函数,它接受一个void*“pairs”数组——每个都是一对指向某物的指针。每对中的第一个元素是字符串,第二个元素是指向其他内容的指针。

我希望传递给函数的数组声明如下:

<what type?> argument = {
        { "some string", <some pointer>},
        { "some other string", <some other pointer>},
        // ... etc
        NULL // To signal ending
}

function(argument);

function 中,我想遍历数组,访问每个“对”,将“对”的成员转换为它们各自的类型(我知道)并利用这些成员。

我遇到了一些麻烦。起初,我尝试将参数声明为void* thing[][]。认为它是void 指针数组的数组。

但是,只是尝试访问此类数组中的索引会给我错误:expression must be a pointer to a complete object type

阅读 SO,我意识到编译器认为我正在尝试取消引用 void*。但我认为在这样的数组上执行a[0] 将访问数组中的第一个“对”,这应该取消引用void** - 这是合法的。我错了吗?

就数组的类型、初始化以及在迭代期间解包数组的方式而言,解决此问题的正确方法是什么?

【问题讨论】:

    标签: c arrays void-pointers


    【解决方案1】:

    你想要void *argument[][2] = { /* ... */ };。您有一个未指定大小的数组,其元素是固定大小为 2 的数组,其元素是指向 void 的指针。您最初的编译器错误是因为当您有多个方括号时,只有第一组可以为空,因此添加2 修复它。

    顺便说一句,它可以作为指针衰减,在这种情况下,它将是void *(*argument)[2]

    请注意,您的哨兵NULL 并没有达到您的预期,因为它位于数组数组中,而数组并不是指针。请注意此警告:

    warning: suggest braces around initialization of subobject [-Wmissing-braces]
            NULL // To signal ending
            ^~~~
            {   }
    

    这是一个使用该类型的示例程序:

    #include <stdio.h>
    
    void *g[][2] = {
            { "some string", (void*)0x1234},
            { "some other string", (void*)0x5678},
            { "some third string", (void*)0x9abc},
            // ... etc
            {NULL} // To signal ending
    };
    
    
    void print_things(void *argument[][2]) {
        for(int i = 0; argument[i][0] /* using just argument[i] instead of argument[i][0] here would not work as intended */; ++i) {
            printf("%s %p\n", (char*)argument[i][0], argument[i][1]);
        }
    }
    
    int main(void) {
        print_things(g);
        return 0;
    }
    

    作为最后一点,我想说的是,使用自定义 struct,您的生活可能会更简单:

    #include <stdio.h>
    
    struct pair {
        char *str;
        void *ptr;
    } g[] = {
            { "some string", (void*)0x1234},
            { "some other string", (void*)0x5678},
            { "some third string", (void*)0x9abc},
            // ... etc
            {NULL, NULL} // To signal ending
    };
    
    
    void print_things(struct pair argument[]) {
        for(int i = 0; argument[i].str; ++i) {
            printf("%s %p\n", argument[i].str, argument[i].ptr);
        }
    }
    
    int main(void) {
        print_things(g);
        return 0;
    }
    

    【讨论】:

    • 感谢您的回答!两个问题:1.您提到void *argument[][2] = { /* ... */ }; 是“可变大小的数组”。 “可变大小数组”是指“可变长度数组”吗?如果是这样,这句话难道不是假的吗?变长数组不是一个大小由运行时值确定的数组吗,而所讨论的数组的大小只是在编译时由右手表达式隐式确定的?
    • 其次:您提到“只有第一个方括号可以为空”。你能详细说明一下吗?这是因为编译器可以通过检查右侧值来隐式理解最外层数组的大小,但仍然需要为内部数组指定大小?或者这是出于其他原因? (假设这确实是不是“可变长度数组”,如果我没记错的话)。
    • 实际上,如果可以的话,还有第三个问题 :) 我现在明白 void *argument[][2] 的意思是“指向 void 的指针数组的数组”。即,我们“从中心向外”阅读它。但是 - 我在读取等效指针类型时遇到问题:void *(*argument[2])。本质上,这是什么,我们应该如何“阅读”?阅读“从中心向外”,这是否读作“指向两个指向 void 的指针的数组的指针”?如果是这样,为什么在这种语法中需要括号?谢谢!
    • 1.我应该说“未指定大小”。可变长度数组是一种创建非固定大小数组的特定方法,它不是这里使用的方法。 2. 如果多于第一个方括号为空,则数组元素的大小将是未知的,因此编译器将不知道argument[1] 的偏移量。 3. 你对那种类型是正确的。您需要括号,因为没有它们,void **argument[2] 将表示“指向 void 的指针的数组 2”,这不是我们想要的类型。
    • 嗨,我借此机会学习了类型表达式的优先规则。我认为void *(*argument[2]) 可能是一个错误?我认为这意味着“参数是两个指向 void 的指针的数组”。 void *(*argument)[2] 不是声明“参数是指向 void 指针的 2 数组的指针”的正确方法吗?
    猜你喜欢
    • 2014-06-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多