gpt4 book ai didi

c - 将带有空指针的结构转换为带类型指针的结构

转载 作者:太空宇宙 更新时间:2023-11-04 04:59:48 25 4
gpt4 key购买 nike

短版本:
假设我有两个结构:

struct charPtrWithLen
{
size_t len;
char * charPtr;
}

struct voidPtrWithLen
{
size_t len;
void * voidPtr;
}

有没有一种方法可以将voidPtrWithLen转换成charPtrWithLen,反之亦然,或者更好的方法是,隐式地将一个字符转换成另一个字符,就像char*和void*可以很容易地被转换并隐式地在彼此之间转换一样?
换一种说法:
我试着写下我所有的C,这样所有指向数组的指针都会带来它们的大小信息。我还尝试使用void指针编写泛型函数(如果适用),以保持基本相同的操作。我正在寻找一种方法,将包含“size array”结构的类型化指针传递到采用包含“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”分配到指针的“void*v”中。但是,当您使用相同的模式执行此操作足够长的时间后,您会遇到以下问题:很快,您就有了一堆不可知地处理数据的通用函数,因此很乐意使用空指针,例如:
pointerWithSize combinePointersWithSize(pointerWithSize p1, pointerWithSize p2);
int readFromStream(FILE * readFromHere, pointerWithSize * readIntoHere);

但最终也会得到一些函数,这些函数本质上是针对特定数据类型的:
size_t countOccurancesOfChar(pointerWithSize str, char c);
int summate(pointerWithSize integers);

最后,你不得不在后一类函数中进行强制转换,这让你很恼火。你最后得到的东西是这样的:
/*这是内务部*/
如果(((char*)str.m)[i]==c){
/*…或者这个里面的总和:*/
sum+=((int*)整数m)[i];
因此,你得到了一个点,你有很多函数,特别是操作“字符串大小”,在所有这些情况下,你不想有很多与空指针。因此,在这些情况下,你开始做这样的事情:
typedef struct
{
size_t v_n;
char * v;
}
stringWithSize;
/* ... */
size_t countOccurancesOfChar(stringWithSize str, char c);
int parseFormatting(stringWithSize str, struct someFormat_t foo);

这很好,因为现在所有与字符串相关的代码都不需要用强制类型转换来填充。但是,现在我不能使用我的出色的泛型函数combinepointerstessize来连接包含在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:不要用这些结构作为参数来编写我的函数,而是用单独的对参数来编写它们。但这是我首先要避免的一大部分——我喜欢大小和指针捆绑在一个结构中所代表的“干净”和意图的清晰。
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});

……但我想大多数人都会同意,这和最初在库函数中进行转换的问题一样糟糕。从表面上看,它看起来更糟,因为它将转换负担转移到客户端代码,而不是库代码,库代码在实现/完成后(包括测试等)有望相当稳定。另一方面,如果你保留了每一个定义为void*包含pointerWithSize的函数,你最终可能会强制执行类似的强制类型转换,就像你在自己的函数内部,在它们的代码中的其他地方,更糟糕的是,你失去了编译器对你大喊大叫的优势,因为现在代码在同一个pointerWithSize结构中携带所有内容。
我还担心有多少编译器能够优化这个解决方案的两个变体中的第一个(其中“d”服务器只是一个临时的结果保持器)。
3:指针的并集。与以前的指针大小示例不同,我将执行以下操作:
typedef union
{
void * void;
char * char;
int * int;
/* ...and so on... */
}
rainbowPointer;

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

乍一看,这已经足够了。然而,我经常想在这个“指针大小”结构中存储一些特定于我正在处理的程序的结构数组,在这种情况下,指针类型的预定义联合对我来说是无用的,我仍然会回到这个问题上来。
4:我可以为每种置换指针类型编写包装函数。我甚至可以编写像宏这样的函数,用大小结构类型来定义每个指针,这样就可以同时生成包装函数。例如:
#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));

但正如这些可能的解决方案的例子所示,我实际上无法想出一种不会很快成为可能的问题的方法(例如,如果有人想放置一个函数调用,该函数调用返回了pointerWithSize,而不是上面的例子中的“p”),那么您将运行该函数两次,从代码上看这一点也不明显。
所以我认为我想到的任何解决方案都不足以满足我的用例,所以我希望你们中的一些人知道一些C语法或机制,我可以利用这里使在两个相同的结构(除了其中一个成员的指针类型之外)之间进行强制转换/强制转换变得容易。

最佳答案

首先,任何类型的“实际”转换都不允许按照标准字母进行,因为Cmakes no guarantee at all所有指针都有相同的格式。允许从任意指针类型到空指针的转换涉及表示的转换(当您将其返回以访问数据时,它会被反转),可能包括不同大小的指针或存在于单独地址空间中的指针。因此,简单地重新解释一个位模式来改变指针类型是不安全的;void*的位模式不能保证有任何特别的含义,其他类型的位模式也不能保证有任何特别的关联。(我不知道到底有多少系统利用了这一点。)
由于在某个地方存在着void*和其他类型之间的显式转换,所以使用整数值转换可能是最安全的想法。您可以定义一个宏,以便快速轻松地为您生成“cast函数”,例如:

#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)

…然后可以在表达式中使用它来代替cast运算符:
stringWithSize a, b, c;
pointerWithSize d;
d = combinePointersWithSize(s_to_v(a), s_to_v(b));
foo(v_to_s(d));

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

关于c - 将带有空指针的结构转换为带类型指针的结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30020435/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com