Вопрос

Есть ли способ переносимо определить верхнюю и нижнюю границы значений указателя void в ANSI C89 / ISO C90? (В настоящее время у меня нет с собой экземпляра стандарта (у меня есть один дома).Конечно, если значения void-указателя гарантированно не будут подписаны, эта задача тривиальна (через sizeof(void *));однако я не могу вспомнить, гарантировано ли это или нет.Я могу придумать несколько очень неэффективных алгоритмов (увеличение до переполнения и т.д.), Но я хотел бы знать, есть ли у кого-нибудь относительно дешевый (с точки зрения временной сложности) и переносимый способ вычисления этих границ.)

--РЕДАКТИРОВАТЬ--

Также: Существует ли переносимый способ определения достоверности значений указателя?

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

Это было полезно?

Решение

Не существует переносимого способа определить, является ли данный указатель допустимым или нет.Вы должны знать, с какой системой памяти вы имеете дело.В зависимости от операционной системы и процессора может существовать, а может и не существовать способ запросить таблицы страниц диспетчера виртуальной памяти для определения допустимых диапазонов указателей.

Например, в Linux вы можете изучить специальные mmap файл под /proc чтобы получить карту виртуальной памяти процесса.Вот пример того, как cat считывание собственной карты памяти:

$ cat /proc/self/mmap
08048000-0804c000 r-xp 00000000 09:00 5128276                            /bin/cat
0804c000-0804d000 rw-p 00003000 09:00 5128276                            /bin/cat
0804d000-0806e000 rw-p 0804d000 00:00 0                                  [heap]
f7ca7000-f7e40000 r--p 00000000 09:00 3409654                            /usr/lib/locale/locale-archive
f7e40000-f7e41000 rw-p f7e40000 00:00 0 
f7e41000-f7f68000 r-xp 00000000 09:00 2654292                            /lib/tls/i686/cmov/libc-2.3.6.so
f7f68000-f7f6d000 r--p 00127000 09:00 2654292                            /lib/tls/i686/cmov/libc-2.3.6.so
f7f6d000-f7f6f000 rw-p 0012c000 09:00 2654292                            /lib/tls/i686/cmov/libc-2.3.6.so
f7f6f000-f7f72000 rw-p f7f6f000 00:00 0 
f7f83000-f7f85000 rw-p f7f83000 00:00 0 
f7f85000-f7f9a000 r-xp 00000000 09:00 2637871                            /lib/ld-2.3.6.so
f7f9a000-f7f9c000 rw-p 00014000 09:00 2637871                            /lib/ld-2.3.6.so
ff821000-ff836000 rw-p 7ffffffea000 00:00 0                              [stack]
ffffe000-fffff000 r-xp ffffe000 00:00 0                                  [vdso]

Вы можете видеть диапазоны допустимых указателей вместе с битами, указывающими, доступна ли память для чтения (r), (w) ritable, e (x) ecutable или (p) resent (т.е.не выгружается на диск).

Другие советы

Спецификация гарантирует, что указатели не будут подписаны.Но с какой стати вы хотите найти границы?"Все между 0x00000001 и 0xffffffff" на самом деле не является полезным тестом, поскольку количество действительный указатели будут каким-то крошечным подмножеством этого.

void * всегда достаточно велик, чтобы вместить указатель на адресуемую память.Любое другое использование строго запрещено ассоциацией бейсбола высшей лиги.

Пример:Dec-10 представлял собой 36-битную архитектуру с 36-битными словами.Тем не менее, адреса были 18 битными, и вы могли содержать 2 указателя в любом регистре / слове.

Да , это крайний пример.Если вы должны выполнять математику с помощью указателей, допустим sizeof;но выполнять математику указателей на чем-либо, кроме непрерывного массива, сложнее, чем изворотливо.

Наконец, никогда не используйте 'void *' для хранения указателя на объект или указателя на элемент в C ++.Многие реализации компилятора фактически используют несколько "физических" указателей для реализации множественного наследования конкретных (или частично конкретных) классов.На самом деле это почти никогда не возникает, потому что очень немногие люди используют множественное наследование таким образом, а когда они это делают, то очень редко вырезают и открепляют указатели.Когда это действительно всплывает, действительно трудно понять, что произошло.

Вы должны различить целочисленное значение, к которому void * может быть отлит из фактического битового шаблона в памяти - приведение void * преобразование в целочисленный тип может включать преобразования!

Предполагая sizeof(void *) == sizeof(long), для a void * p следующее вполне может быть ложным:

((long)p) == *((long *)&p)

Кроме того, в стандарте не указано, существует ли даже является целочисленный тип, достаточно большой, чтобы содержать значения всех допустимых указателей!

Таким образом, просто не существует переносимого способа делать то, что вы хотите сделать...

Помимо области, соответствующей NULL, вообще не существует ограничений (переносимости) на адреса памяти.Достаточно защищенная операционная система могла бы использовать различные механизмы CPU / OS для предоставления каждому процессу случайных и хорошо распределенных адресов при каждом вызове malloc(), а позиционно-независимый исполняемый файл плюс ASLR также могут позволить запускать код с любого адреса.

Я знаю, что в Win32 64-разрядные указатели расширены по знаку.Интересно проверять 32-разрядный мини-дамп с 64-разрядной машины, если вы не подписываете указатели расширения.

Видишь здесь для того, как 64-битный указатель (POINTER_64) работает на Win32.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top