首页 > 解决方案 > C中两个表达式之间的区别

问题描述

我目前正在研究用 C 完全开发的遗留代码的问题。它使用共享内存概念。我想了解其中使用的一些表达方式。假设一个结构

> typedef struct
> {
>     void* base;
>     ....
> }shm_test_t;

表情是这样的

> shm_test_t test;
> test.base = (void*)(unsigned8*)&test;
> unsigned8* l_base = (unsigned8*)test.base;
> unsigned8* s_base = (unsigned8*)&(test.base);

然后,他们这样做了

unsigned8 l_diff = l_base - s_base;
unsigend8 s_diff = s_base - l_base;

我不明白他们为什么要减去两个指针。它不会返回相同的值(零)吗?它与Linux IPC有关吗?这真的很令人困惑。请帮忙

标签: clinuxshared-memory

解决方案


我不明白他们为什么要减去两个指针。它不会返回相同的值(零)吗?

是的,但前提是该base成员是结构中的第一个成员。l_base是结构的地址,而s_base是该结构中base成员的地址,两者都转换为指向unsigned8.

C99 及更高版本确实明确表示如果base是结构中的第一个成员,则它与结构具有相同的地址。

它与Linux IPC有关吗?

不,不是我能看到的。


然而,有一个与共享内存进程间通信相关的模糊相似的模式。

假设您有一个struct shared_data *shared, 指向共享内存。

因为每个进程都有自己的虚拟地址空间,虽然 的内容shared共享的,但每个进程可以拥有不同的地址,即shared自身的值可以变化。

这意味着在共享内存中使用指针基本上是没有用的。仅仅因为一个进程中的特定指针值指向共享内存的特定部分,并不意味着它在所有进程中都这样做。

您需要存储相对于 的偏移量shared,而不是指针,以便0引用共享内存区域中的第一个地址,依此类推。(或与共享内存的开头相关的其他类似方案。)

为此,您可能会看到类似于

intptr_t  shared_offset = (intptr_t)shared;

该类型intptr_t是可移植的 POSIX 兼容类型。在 Linux 中,您可以使用long. 问题是,存在使用 的旧代码,int甚至int在其示例中使用的旧书籍,但它无法在 64 位架构上正常工作,例如较新的 Intel 和 AMD 计算机。

无论如何,要将 say 处的字节偏移量转换为指向 say的shared[5].next指针,您需要使用footypefootype *foo

foo = (footype *)((char *)shared + shared[5].next);

或者

foo = (footype *)(shared_offset + shared[5].next);

两者是等价的;前者shared直接使用指针,后者使用shared_offset变量。

foo从到偏移量的逆变换例如是

offset =  (ptrdiff_t)((char *)foo - (char *)shared);

或者

offset = (intptr_t)foo - shared_offset;

这种方法很脆弱,因为它需要非常小心地正确编写所有这些表达式,同时确保底层逻辑也是正确的。(我认为这类似于尝试用一只手敲击,同时用另一只手画一个圆圈。大多数人需要大量练习才能正确完成。)

如果可能的话,最好使用数组和数组索引,而不是共享内存开头的偏移量。

我只看到当每个元素的大小变化时才合理使用这种偏移方法。即使这样,通常也有更好的算法方法。


推荐阅读