Разница между двумя выражениями в C - PullRequest
0 голосов
/ 09 декабря 2018

В настоящее время я работаю над проблемой в унаследованном коде, полностью разработанном на C. Он использует концепцию разделяемой памяти.Я хочу понять некоторые выражения, используемые в этом.Предположим, что struct

> 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 голосов
/ 09 декабря 2018

Я не могу понять, почему они вычитают два указателя.Не вернет ли оно то же значение (ноль)?

Да, но только если член 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;

Этот подход хрупок в том смысле, что для правильного написания всех этих выражений требуется много усилий, в то же время обеспечивая правильность базовой логики.(Я думаю, что это похоже на попытку постукивания одной рукой и одновременного рисования круга другой. Большинству людей требуется много практики, чтобы сделать это правильно.)

Если это вообще возможно, этоЛучше использовать массив и индексирование массива, а не смещать начало общей памяти.

Я видел, как этот метод смещения использовался разумно, когда размер каждого элемента менялся.Даже тогда обычно существуют лучшие алгоритмические подходы.

...