关于C#:使用带有指针的指针将带有空洞指针的结构转换为结构

Casting Structs With Void Pointers into Structs With Typed Pointers

短版:

我想有两个结构:

1
2
3
4
5
6
7
8
9
10
11
struct charPtrWithLen
{
 size_t len;
 char * charPtr;
}

struct voidPtrWithLen
{
 size_t len;
 void * voidPtr;
}

有没有一个方法来铸造voidptrwithlen charptrwithlen和反之亦然,甚至更好,implicitly转换到另一个相同的方式,使用一个char * a void *可以转换之间的铸造和孟加拉implicitly对方吗?

另一个办法:把

我想写我所有的C都是这样分的大小和数组的信息给他们。所以我想写个通用函数void指针,这是在保持业务不适用相同的,好的,相同的。我在寻找一个方式通过指针型含氟纳米阵列的通用函数的结构以函数到指针数组的大小的含氟的论点。

长版,涉及的例子有:

因此,wonderfully void指针是灵活的,所以我可以这么做:

1
2
3
4
5
6
7
8
9
10
11
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);

但自从"指针到任意长度的模式数组的大小加上有"非常普通,在一些其他点i get函数指定我厌倦了各种条件对参数的指针和长度),而不是一开始的代码,和一个搜索对包覆结构为:A

1
2
3
4
5
6
7
8
typedef struct
{
    size_t v_n;
    void * v;
}
pointerWithSize;
/* ... */
int foo(pointerWithSize);

那么远那么好。我总是分配I CAN"char * C"或"int *"到"pointerwithsize void * V"和最小的困难。但当你做足够长,使用相同的模式运行,你到下面的问题:你有一个很好的功能几乎总是与数据agnostically是快乐的,因此把空分,例如这样:

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

但你最终是通过一个函数用于特异性:数据类型

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

然后你和我最终在大学里面做casts后者类函数。例如,你最终有这样的事情:在countoccurancesofchar * / / *本if(((char *)str.m)〔i〕={c)/*本内。或基础:*/点心+ =(int *)integers.m)[我];

所以你得到一点,你有一个焊料的功能,特别是在经营字符串有大小",在所有这些情况下,你不想在一个void指针要多。那么你会在那些情况下,开始做这样的事情:

1
2
3
4
5
6
7
8
9
typedef struct
{
    size_t v_n;
    char * v;
}
stringWithSize;
/* ... */
size_t countOccurancesOfChar(stringWithSize str, char c);
int parseFormatting(stringWithSize str, struct someFormat_t foo);

这是伟大的,因为现在所有的字符串相关的代码不需要cluttered与casts。但是,现在我不能使用我的位置功能的通用串行combinepointerswithsize字符串包含在一个stringwithsize在一路,这是一syntactically干净,我可以,如果我不写我的函数在两个单独的参数为每个术语的指针和大小对。

插图:整理上。

1
2
3
4
5
6
7
8
9
10
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:不要写我的功能与这些结构作为参数,而不是写一个人对他们的论点。但这是一个大的一部分我想避免在第一位一样"清洁和清晰的意向,有一个大小_ T和A代表一个捆在struct指针。

2:我这样做:

1
2
3
4
5
6
7
8
9
10
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});

我认为最..but会同意,这是作为浴室或更糟的是原问题的铸造和库函数。表面上,它看起来更糟,因为它offloads铸造负担到客户端代码库的代码,所以它可以代替的是相当稳定(包括后实施/完成。测试/等)。在其他的手,如果你做的每一个函数中定义的术语保持在含pointerwithsize void *,你可以结束你的相似casts六月的孩子做在你自己的函数,在他们的代码的地方,更糟糕的是,你失去了你的优势在yelllng的编译器,因为现在所有的代码是载在THpointerwithsize E相同的结构。

我是那么担心有多少编译出来的有能力的第一个信息是有用的。在本解决方案(D为临时服务器仅仅结果持有人。

3:联盟之分。而我,我会做pointerwithsize之前,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
typedef union
{
    void * void;
    char * char;
    int * int;
    /* ...and so on... */
}
rainbowPointer;

typedef struct
{
    size_t v_n;
    rainbowPointer v;
}
pointerWithSize;

在第一一瞥这几乎是很难的。然而,我想我最终发现的存储阵列结构的一些具体的程序,这是我的工作在这样的"指针和大小"的构建,和在这些情况下,对于A类型的联盟管理是指针对我,我就在这寂静的反馈问题。

4:我可以写permuted for each函数指针类型的包装。我可以写一个函数,宏定义的每个类论文指针和结构类型的大小,它会在相同的俯冲产生的包装函数。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#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)

我的直觉是,迟早会成为unwieldy本法。

5:如果有一个机制与NO的性能影响,但这是我可以写我的unappealing,否则,一般用作类的宏功能,


首先,任何类型的"实际"铸造都不允许按照标准的字母,因为C根本不能保证所有指针都具有相同的格式。允许从某个任意指针类型转换为空指针的转换涉及表示形式的转换(当您将其转换回以访问数据时,转换会被反转),包括可能转换为不同大小的指针或存在于单独地址空间中的指针。因此,对一个位模式进行简单的重新解释来改变指针类型是不安全的;void*的位模式不能保证有任何特别的意义,并且其他类型的位模式也不能保证以任何特别的方式相关。(我不知道有多少系统真正利用了这一点。)

由于void*和其他类型之间的显式转换必须存在于某个地方,因此使用整值转换可能是最安全的想法。您可以做的是定义一个宏,以便快速、轻松地为您生成"强制转换函数",例如:

1
2
3
4
5
6
7
#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)

…然后可以在表达式中使用它来代替强制转换运算符:

1
2
3
4
stringWithSize a, b, c;
pointerWithSize d;
d = combinePointersWithSize(s_to_v(a), s_to_v(b));
foo(v_to_s(d));

一个好的编译器应该认识到,在公共平台上,转换函数是一个标识操作,并将其完全删除。


您应该能够通过将一个指针转换为另一个指针,将其转换为另一个类型的指针,并取消对它的引用来将其转换为另一个类型的指针。这也将反向工作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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;
}

注意,这只在两个结构的结构布局相同的情况下有效。