首页 文章

C中两个表达式的区别

提问于
浏览
0

我目前正在研究用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 回答

  • 1

    我无法理解为什么他们减去两个指针 . 它不会返回相同的值(零)吗?

    是的,但仅当 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计算机 .

    无论如何,要将 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);
    

    要么

    offset = (intptr_t)foo - shared_offset;
    

    这种方法很脆弱,因为正确地编写所有这些表达式需要花费很多精力,同时确保底层逻辑也是正确的 . (我认为它类似于试图用一只手敲击,同时用另一只手画一个圆圈 . 大多数人需要做很多练习才能做到 . )

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

    我只看到这种偏移方法在每个元素的大小变化时合理使用 . 即便如此,通常还有更好的算法方法 .

相关问题