使用带有指针的指针将具有空指针的结构转换为结构

时间:2015-05-03 23:11:51

标签: c pointers struct void-pointers

简短版本:

假设我有两个结构:

struct charPtrWithLen
{
 size_t len;
 char * charPtr;
}

struct voidPtrWithLen
{
 size_t len;
 void * voidPtr;
}

有没有办法将voidPtrWithLen转换为charPtrWithLen,反之亦然,甚至更好,将一个隐式转换为另一个,就像char *和void *可以很容易地在彼此之间转换和隐式转换一样?

换句话说:

我正在尝试编写所有的C,以便所有指向数组的指针都带有它们的大小信息。我还尝试使用void指针编写泛型函数,以适用于保持基本相同,完全相同的操作。我正在寻找一种方法来传递包含类型指针的'大小的数组'采用包含void-pointer的' size-array'结构的泛型函数。参数。

长版本,参与示例:

因此,void指针非常灵活,所以我可以这样做:

int foo(void * ptr, size_t dataLen);
/* ... */
char * c;
size_t c_n;
/* ... */
foo(c, c_n);
/* ... */
int * i;
size_t i_n;
/* ... */
foo(i, i_n);

但是因为"指向任意长度数组的模式,加上大小那么"是如此常见,假设在某些时候我厌倦了根据参数对,指针和长度来指定我的各种函数,而是开始使用封装在结构中的这些对来编码:

typedef struct
{
    size_t v_n;
    void * v;
}
pointerWithSize;
/* ... */
int foo(pointerWithSize);

到目前为止一切顺利。我总是可以分配我的" char * c"或" int * i"进入指针和#S;" void * v"最小的困难。但是当你这么长时间使用相同的模式时,会遇到以下问题:很快就会有一堆通用函数,这些函数可以非常地使用数据,因此很乐意使用void指针,例如:

pointerWithSize combinePointersWithSize(pointerWithSize p1, pointerWithSize p2);
int readFromStream(FILE * readFromHere, pointerWithSize * readIntoHere);

但是你最终还得到了固有的特定数据类型的函数:

size_t countOccurancesOfChar(pointerWithSize str, char c);
int summate(pointerWithSize integers);

然后你最终会厌倦必须在后一类函数中进行强制转换。例如。你最终得到这样的东西:     / *这里面的countOccurancesOfChar * /     if(((char *)str.m)[i] == c){     / * ..或此内部摘要:* /     sum + =((int *)integers.m)[i];

所以你要达到这样的程度,即你有很多功能,专门用于#34;字符串大小",在所有这些情况下,你不想要有很多功能带有void指针。所以相反,在这些情况下,你开始做这样的事情:

typedef struct
{
    size_t v_n;
    char * v;
}
stringWithSize;
/* ... */
size_t countOccurancesOfChar(stringWithSize str, char c);
int parseFormatting(stringWithSize str, struct someFormat_t foo);

这很棒,因为现在所有与字符串相关的代码都不需要使用强制转换混乱。但是,现在我不能使用我精彩的泛型函数combinePointersWithSize来连接stringWithSize中包含的字符串,这种语法在语法上是干净的,如果我仍然按照两个方式编写函数的话。每个指针和大小对的单独参数。

完成插图:

pointerWithSize combinePointersWithSize(pointerWithSize p1, pointerWithSize p2);
void * combineAlternative(void * p1, size_t p_n1, void * p2);
/* ... */
stringWithSize a, b, c;
/* ... */
/* This doesn't work, incompatible types: */
c = combinePointersWithSize(a, b);
/* But this works, because char * can be passed into void * parameter. */
c.v_n = a.v_n + b.v_n;
c.v = combineAlternative(a.v, a.v_n, b.v, b.v_n); /* Works fine. */

我认为可能的解决方案:

1:不要将这些结构作为参数编写我的函数,而是用单独的对参数编写它们。但这首先是我想要避免的 - 我喜欢清洁'并且在一个结构中包含size_t和指针的意图清晰度表示。

2:做这样的事情:

stringWithSize a, b, c;
/* ... */
pointerWithSize d;
d = combinePointersWithSize((pointerWithSize){.v=a.v, .v_n=a.v_n}, (pointerWithSize){.v=b.v, .v_n=b.v_n})
/* and then do either this: */
c.v = d.v;
c.v_n = d.v_n;
foo(c);
/* ..or this: */
foo((stringWithSize){.v=d.v, .v_n=d.v_n});

..但我认为大多数人会同意,这也与库函数中最初的转换问题一样糟或坏。表面上看起来更糟糕,因为它将铸造负担卸载到客户端代码而不是库代码,在实现/完成(包括测试/等)之后,库代码可能相当稳定。另一方面,如果你确实按照包含pointerWithSize的void *来定义每个函数,你最终可能会强迫类似的强制类型转换为你在自己的函数中执行的类型,代码中的其他地方,更糟糕的是,你失去了编译器对你大喊大叫的优势,因为现在代码在同一个pointerWithSize结构中携带了所有东西。

我还担心有多少编译器能够优化此解决方案的两个变体中的第一个(其中服务器只是一个临时结果持有者。<\ n / p>

3:指针联盟。而不是我之前的pointerWithSize示例,我会这样做:

typedef union
{
    void * void;
    char * char;
    int * int;
    /* ...and so on... */
}
rainbowPointer;

typedef struct
{
    size_t v_n;
    rainbowPointer v;
}
pointerWithSize;
乍一看,这几乎已经足够了。但是,我经常最终想要存储某些结构的数组,这些结构特定于我在这个&#34;指针中使用大小&#34;构造,在这些情况下,指针类型的预定义联合对我来说是无用的,我仍然会回到这个问题。

4:我可以为每个置换指针类型编写包装函数。我甚至可以编写类似函数的宏来定义这些指针大小结构类型中的每一个,这将在同一个swoop中生成包装器函数。例如:

#define pointerWithSizeDef(T, name) \
typedef struct \
{ \
    size_t v_n; \
    T * v;
} \
name; \
foo_ ## name (name p1) \
{ \
    /* generic function code defined in macro */ \
    /* Or something like this: */ \
    foo((pointerWithSize){.v=p1.v, .v_n=p1.v_n});
};
/* Then, stuff like this: */
pointerWithSizeDef(char, stringWithSize)

我的直觉是这种方法迟早会变得笨拙。

5:如果有一种机制没有性能影响,但是否则没有吸引力,我可以将我的泛型函数编写为类似函数的宏,然后调用底层的实际函数:

int foo_actual(void * v, size_t v_n);
#define foo(p) \
foo_actual(p.v, p.v_n);

..甚至是类似的东西,以取代转换语法:

#define castToPointerWithSize(p) \
((pointerWithSize){.v=p.v, .v_n=p.v_n})
/* ... */
stringWithSize a;
foo(castToPointerWithSize(a));

但正如可能解决方案#5的这些示例所示,我实际上无法想到这样做的方法很快就会成为可能的问题(例如,如果有人想要放置一个功能在上面的示例中,调用返回了一个pointerWithSize来代替&#39; p&#39;您将运行该函数两次,并且从代码中看起来并不明显。

所以我不认为我所考虑的任何解决方案对我的用例来说已经足够了,所以我希望你们中的一些人知道我可以利用的一些C语法或机制这里是为了方便演员/&#34;演员&#34;除了其成员之一的指针类型之外,两个结构之间是相同的。

2 个答案:

答案 0 :(得分:1)

首先,任何类型的&#34;实际&#34;根据标准的字母不允许进行转换,因为C makes no guarantee at all所有指针都具有相同的格式。从某种任意指针类型到void指针的转换允许涉及表示的转换(当你将其转换回来以便访问数据时会反转),包括可能转换为不同大小的指针或存在于指针中的指针。单独的地址空间。因此,对位模式进行简单的重新解释以改变指针类型是不安全的; void*的位模式并不能保证特定含义,并且其他类型的位模式不能保证以任何特定方式相关。 (有多少系统实际上利用了这一点,我不知道。)

由于void*和其他类型之间的显式转换必须存在于某处,因此使用整值转换可能是最安全的想法。你可以做的是定义一个宏来快速,轻松地生成&#34;演员函数&#34;对你而言,例如:

#define GEN_CAST(NAME, FROM_TYPE, TO_TYPE)     \
  static inline TO_TYPE NAME(FROM_TYPE from) { \
    return (TO_TYPE){ .v=p.v, .v_n=p.v_n };    \
  }

GEN_CAST(s_to_v, stringWithSize, pointerWithSize)
GEN_CAST(v_to_s, pointerWithSize, stringWithSize)

...然后你可以用它来代替表达式中的强制转换运算符:

stringWithSize a, b, c;
pointerWithSize d;
d = combinePointersWithSize(s_to_v(a), s_to_v(b));
foo(v_to_s(d));

一个好的编译器应该认识到,在通用平台上,转换函数是一个身份操作,并完全删除它。

答案 1 :(得分:-2)

您应该能够通过将一个转换为指针,将其转换为另一个类型的指针并将其解除引用来将一个转换为另一个。这也将反过来。

struct charPtrWithLen
{
 size_t len;
 char * charPtr;
};

struct voidPtrWithLen
{
 size_t len;
 void * voidPtr;
};

int main() {
    struct charPtrWithLen cpwl = {.len = 6, .charPtr = "Hello"};

    struct voidPtrWithLen vpwl = *(struct voidPtrWithLen *)&cpwl;

    return 0;
}

注意只有结构布局对两个结构都相同时才会起作用。