【问题标题】:Storing small data in a pointer in C?将小数据存储在C中的指针中?
【发布时间】:2013-12-06 19:16:46
【问题描述】:

为了简单起见,我有一个结构类似于

typedef struct foo 
{
 void *data;
 size_t sz;
 /* various other crap */
} foo_t;

但我发现大约 90% 的时间它真的只需要存储一个字节或最多一个短字节(但有时,它需要是一大块内存)。我是否要求使用执行某种大小检查的函数来包装 get 和 set 并直接在 data 中存储和读取小数据而不是将其用作指针?逻辑看起来很简单,例如

if (f->sz <= sizeof(void*) && f->sz == sizeof(char)) 
{
 return (char) f->data; 
}

从维护的角度来看这样做的可取之处(一方面,它使释放 90% 的结构更容易,并且不会分配大量的单个字符,上帝知道堆上的位置;另一方面,它意味着现在每个读取和写入都有可能采用的多个代码路径,这意味着我将来会讨厌这个),是否有任何特殊原因我不能只存储一个字符(或指针中还有什么不大于 sizeof(void*)) 并稍后读回吗?我会在奇怪的平台上遇到字节序问题吗?我记得很久以前一位 CS 教授说“说‘指针是整数’是错误的;整数是实现指针的一种方式”,但我不知道他是否只是在提出概念性观点,或者存储是否存在真正的问题编译器被告知的位置中的非地址数据是指针。

(我的意思是,很明显,如果我随后错误地使用其中之一作为指针,那会造成痛苦,但我太聪明了,不会这样做......对吧?.. .)

【问题讨论】:

  • union { void *ptr; char byte; short sh; } data;
  • 很公平。但是在写入时,我不知道在编译时指针会有多大(但这解决了我 90% 问题中的 90%……我会试一试)。跨度>
  • @userXXX 嗯...什么?
  • @user3075658,你为什么不知道?那仍然只是sizeof(data)
  • 答案是“你必须自己决定”。不过,我可以解决一点:“我会遇到字节序问题吗?”不,你不会。指针的存储方式无关紧要,只要您在存储和检索时进行正确的转换,它的样子并不重要。

标签: c pointers casting struct void


【解决方案1】:

如果您希望能够作为数据指针透明地访问它,请使用

union overlay {
  void* point;
  unsigned char array[sizeof(void*)];
};

并有一些类似的代码

inline
void* get_data_pointer(struct foo* f) {
  return (f->sz > sizeof(void*)) ? f->data.point : f->data.array;
};

编辑:使用这种方法不会处理您的小类型的数据布局。您的其他代码在任何情况下都会看到void*,并根据需要将数据存储到相应的对象(然后以相同的方式检索它)。正如 nos 指出的那样,唯一可能的问题可能是您要存储的数据的对齐要求。通常,小于void* 的数据类型的对齐要求比void* 的限制性小。我不知道有哪个平台会出现问题。

顺便说一句,以_t 结尾的类型名称由 POSIX 保留,您不应在代码中定义它们。类型标识符使用与struct 标签相同的名称不会有任何问题。

typedef struct foo foo;

【讨论】:

  • 我喜欢这样,虽然它改变了结构。我也会试试看;虽然这意味着我必须在那里处理我自己的字节序。谢谢。
  • @user3075658 为什么字节序有问题?你打算以某种方式在外部存储这个结构吗?
  • @nos 我在 80 年代学习 C(从那时起就没有考虑太多),当时字节序是每个人都头疼的问题,而且旧习惯很难改掉。我希望我的程序不依赖于它正在执行的平台;我可以想象一个平台,其中指针以与等效大小的 int 不同的字节顺序存储,但显然使用强制转换编译器会为我处理...
  • @user3075658 无论如何这里没有问题。您将所需的任何内容存储在 unsigned char 数组中并从该数组中检索它,或者将指针存储在 void* 中。如果这里有任何问题,那就是您存储在数组中的任何内容的对齐要求是否大于 void* 的对齐要求。假设你想在那里存储一个 char、short、int 或 float,我还没有见过这样的平台。
  • 无论如何,结构都在堆上;如果我要向所有这些数组添加一个 4 字符数组,我不妨只 malloc() 单个字节并将数据指向它(因为它最终会占用整个内存字的大部分时间)。如果可能的话,我宁愿少调用 malloc() 和 free() ...
【解决方案2】:

你应该使用union,例如:

union foo {
  void *data;
  size_t sz;
  /* various other crap */
}

这是一个很好的 tutorial 在 C 中使用联合。

【讨论】:

  • 我知道联合是什么,声子;我也知道,在编译之前,我不知道在任何正在编译的假设平台上指针有多大,所以这可能只能解决我的部分问题。
  • @H2CO3,当然这很重要,size_t 可能太大了,会炸毁结构。但我承认这不太可能。
  • @JensGustedt 在什么意义上它可以“炸毁”任何东西? union 分配的空间至少与其最大成员的大小一样多。不会有任何问题(无论如何,在符合标准的实现上)。
  • @H2CO3 确切地说,我不想让我的结构更大。我想保留我拥有的结构并滥用指针被实现为某个特定大小的整数这一事实,但是在编译之前我不知道该指针实际上可以有多大的数字抓住。我的意思是,我可以说“如果它是一个字符存储在那里”(因为任何类型都必须至少是一个字符大)并完成它......
  • @user3075658 是的,您可以转换为/转换为sizeof(T) &lt;= sizeof(void *) 的任何整数类型T。
【解决方案3】:

size_t 是无符号整数。隐式声明,指针 void 的大小也等于特定编译器上 int 的大小。有一个称为打包的概念,在 gcc 中默认为 4。例如 结构体 { 字符 c; 诠释一个; } 所以 s 的大小是 8 个字节而不是 5 个字节,因为这三个字节不会使用这个概念,这与操作系统概念有关。 if (f->sz sz == sizeof(char)) { 返回(字符)f->数据; } 在这里你尝试做什么。 f->sz 将返回您存储在 sz 变量中的值,并且 sizeof(void*) 将返回 4。现在 return (char) f->data 这将返回地址以及如何将地址转换为 char 它始终是整数。在这里,您只返回最低有效字节。假设 f->data=0xaabbccdd;因此,存储在最后一个最低有效字节的数据是 0xdd,因此通过使用 char 进行类型转换,您只会返回这个值。 如果您感到困惑,请打印 printf("%p",f->data) 并在返回它之后您将在哪里收到此值再次打印该值。这里 %p 将以十六进制格式打印值。可能在这里我很困惑,只是检查一下如何以十六进制格式打印。我认为这可能对你有帮助 谢谢 asif aftab

【讨论】:

  • 如果您有任何与大小端相关的问题,请询问我会为您解答。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-03
  • 1970-01-01
  • 1970-01-01
  • 2011-02-02
  • 1970-01-01
相关资源
最近更新 更多