C中两个表达式之间的差异

时间:2018-12-09 15:33:49

标签: c linux shared-memory

我目前正在研究完全用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有关吗?这真是令人困惑。请帮助

1 个答案:

答案 0 :(得分:1)

  

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

是的,但仅当base成员是结构中的第一个成员时。 l_base是结构的地址,而s_base是该结构中base成员的地址,都转换为指向unsigned8的指针。

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

  

与Linux IPC有关吗?

不,我看不到。


但是,存在一种与共享内存进程间通信有关的相似模式。

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

因为每个进程都有自己的虚拟地址空间,尽管shared contents 是共享的,所以每个进程可以将其位于不同的地址,即{{1}的值}本身可以有所不同。

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

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

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

的代码
0

类型intptr_t shared_offset = (intptr_t)shared; 是可移植POSIX兼容类型。在Linux中,您可以使用intptr_t。问题是,示例中存在使用long的旧代码,甚至有使用int的旧书,但是在更新的Intel和AMD计算机等64位体系结构上无法正常工作,例如。

无论如何,要将诸如int的字节偏移量转换为指向shared[5].next(例如footype)的指针,则需要使用其中任何一个

footype *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);

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

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

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