Означает ли равенство указателей целочисленное равенство? - PullRequest
0 голосов
/ 06 июня 2018

Для int *a, int *b означает a == b (intptr_t)a == (intptr_t)b?Я знаю, что это так, например, на современном процессоре X86, но дает ли это стандарт C, POSIX или любой другой стандарт?

Ответы [ 2 ]

0 голосов
/ 06 июня 2018

Вместо того, чтобы пытаться указать все гарантии, которые должны поддерживаться качественными реализациями на обычных платформах, Стандарт вместо этого стремится избегать предоставления каких-либо гарантий, которые могут быть дорогостоящими или проблематичными для любой мыслимой платформы, если только они не настолько ценны, чтобы оправдать какие-либовозможная стоимость.Авторы ожидали (разумно в то время), что качественные компиляторы для платформ, которые могли бы предложить более сильные гарантии практически без затрат, сделали бы это, и, таким образом, увидели необходимость явно предписывать действия, которые компиляторы собирались делать в любом случае.

Если одинПосмотрите на то, что на самом деле гарантия, предлагаемая стандартом, это абсурдно глупо.Он указывает, что преобразование void * в uintptr_t и затем обратно в void * даст указатель, который можно сравнить с оригиналом, и что сравнение сообщит, что два указателя равны.Он ничего не говорит о том, что произойдет, если код сделает что-то еще с указателем, преобразованным в оба конца.Соответствующая реализация может выполнять преобразования целочисленных указателей таким образом, что игнорируется целочисленное значение (если только это не нулевая указатель-константа) и получается некоторый произвольный битовый шаблон, который не соответствует ни одному действительному или нулевому указателю, а затем иметь указательоператоры неравенства сообщают «равно» всякий раз, когда любой операнд содержит этот специальный битовый шаблон.Конечно, ни одна качественная реализация не должна вести себя таким образом, но ничто в Стандарте не запретило бы это.

В отсутствие оптимизации было бы разумно предположить, что на любой платформе, которая использует "линейные" указатели, которыетакого же размера, как и uintptr_t, качественные компиляторы будут обрабатывать преобразование указателей в uintptr_t таким образом, что преобразование равных указателей приведет к тому же числовому значению, а значение uintptr_t u;, если u==(uintptr)&someObject, то *(typeOfObject*)u можетиспользоваться для доступа к someObject, по крайней мере, между моментом, когда адрес someObject был преобразован в uintptr_t, и в следующий раз, когда к someObject обращаются другими способами, независимо от того, как u стал удерживать егозначение.К сожалению, некоторые компиляторы слишком примитивны, чтобы признать, что преобразование адреса в uintptr_t предполагает, что указатель, сформированный из uintptr_t, может идентифицировать тот же объект.

0 голосов
/ 06 июня 2018

Это не гарантируется стандартом C.(Этот ответ не касается того, говорят ли POSIX или другие стандарты о intptr_t.) Что в стандарте C (2011, проект N1570) говорится о intptr_t:

7.20.1.4 1type обозначает целочисленный тип со знаком, обладающий свойством, что любой действительный указатель на void может быть преобразован в этот тип, затем преобразован обратно в указатель на void, и результат будет сравниваться равным исходному указателю: intptr_t

В качестве теоретического доказательства один контрпример представляет собой систему с 24-разрядными адресами, где старшие восемь битов не используются, но доступны целочисленные типы: 8-разрядные, 16-разрядные и 32-разрядные.,В этом случае реализация C могла бы сделать intptr_t 32-разрядным целым числом, и она могла бы преобразовать указатель в intptr_t, скопировав 24-разрядный адрес в 32-разрядное целое число и пренебрегая старшими восемью битами.Эти биты могут быть оставлены от того, что лежало раньше.Когда значение intptr_t преобразуется обратно в указатель, компилятор отбрасывает старшие восемь битов, что приводит к исходному адресу.В этой системе, когда a == b оценивается для указателей a и b, компилятор реализует это, сравнивая только 24 бита адреса.Таким образом, если a и b указывают на один и тот же объект, a == b будет иметь значение true, но (intptr_t) a == (intptr_t) b может принимать значение false из-за игнорируемых старших битов.(Обратите внимание, что строго a и b должны быть указателями на void или должны быть преобразованы в указатели на void перед преобразованием в intptr_t.)

Другим примером может бытьсистема, которая использует базовую и смещенную адресацию.В этой системе указатель может состоять из 16 битов, которые задают некоторый базовый адрес, и 16 битов, которые задают смещение.База может быть кратна 64 байтам, поэтому фактический адрес, представленный base и offset , равен base • 64 + offset .В этой системе, если указатель a имеет основание 2 и смещение 10, он представляет тот же адрес, что и указатель b с основанием 1 и смещением 74. При сравнении указателей компилятор будет оценивать base • 64+ смещение для каждого указателя и сравнение результатов, поэтому a == b оценивается как true.Однако при преобразовании в intptr_t компилятор может просто скопировать биты, в результате чего будет выдано 131 082 (2 • 65536 + 10) для (intptr_t) a и 65 610 (1 • 65536 + 74) для (intptr_t) b.Тогда (intptr_t) a == (intptr_t) b оценивается как ложное.Но правило, согласно которому преобразование intptr_t обратно в тип указателя дает исходный указатель, остается в силе, поскольку компилятор просто скопирует биты снова.

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