Сравнение указателей в C. Являются ли они подписанными или неподписанными? - программирование
Подтвердить что ты не робот

Сравнение указателей в C. Являются ли они подписанными или неподписанными?

Привет. Я уверен, что это должен быть общий вопрос, но я не могу найти ответ, когда я его ищу. Мой вопрос в основном касается двух указателей. Я хочу сравнить их адреса и определить, больше ли один из них. Я бы ожидал, что во время сравнения все адреса будут неподписанными. Это правда, и отличается ли это от C89, C99 и С++? Когда я компилирую с gcc, сравнение не имеет знака.

Если у меня есть два указателя, которые я сравниваю следующим образом:

char *a = (char *) 0x80000000; //-2147483648 or 2147483648 ?  
char *b = (char *) 0x1; 

Тогда a больше. Гарантируется ли это стандартом?


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

Я ценю ответы до сих пор. Основываясь на моем редактировании, вы скажете, что то, что я делаю, - это поведение undefined в gcc и msvc? Это программа, которая будет работать только в Microsoft Windows.

Здесь приведен более упрощенный пример:

char letters[26];  
char *do_not_read = &letters[26];  
char *suspect = somefunction_i_dont_control(letters,26);  
if( (suspect >= letters) && (suspect < do_not_read) )  
    printf("%c", suspect);  



Другое редактирование, после прочтения ответа AndreyT, кажется правильным. Поэтому я сделаю что-то вроде этого:

char letters[26];  
uintptr_t begin = letters;  
uintptr_t toofar = begin + sizeof(letters);  
char *suspect = somefunction_i_dont_control(letters,26);  
if( ((uintptr_t)suspect >= begin) && ((uintptr_t)suspect < toofar ) )
    printf("%c", suspect);  


Спасибо всем!

4b9b3361

Ответ 1

Сравнение указателей не может быть подписано или не подписано. Указатели не являются целыми числами.

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

Вы не можете юридически сравнивать произвольные указатели в C/C++. Результат такого сравнения не определен. Если вы заинтересованы в сравнении числовых значений адресов, хранящихся в указателях, вы обязаны сначала вручную преобразовать указатели в целочисленные значения. В этом случае вам придется решить, использовать ли целочисленный тип со intptr_t или без знака (intptr_t или uintptr_t). В зависимости от того, какой тип вы выберете, сравнение будет "подписанным" или "без знака".

Ответ 2

Преобразование целых чисел в указатель целиком полностью определено, поэтому оно зависит от используемой реализации.

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

Ответ 3

Из проекта С++ Standard 5.9:

Если два указателя p и q того же типа указывают на разные объекты которые не являются членами одного и того же объекта или элементов одного и того же массива или к различным функциям, или если только один из них является нулевым, результаты из p<q, p>q, p<=q и p>=q не определены.

Итак, если вы набрасываете числа на указатели и сравниваете их, С++ дает неуказанные результаты. Если вы берете адрес элементов, которые вы можете сравнить, результаты операций сравнения указываются независимо от подписанности типов указателей.

Примечание unspecified is not undefined: вполне возможно сравнить указатели на разные объекты того же типа, которые не находятся в одной структуре или массиве, и вы можете ожидать некоторого самосогласованного результата (иначе это было бы невозможно использовать такие указатели, как ключи в деревьях, или сортировать vector таких указателей, двоичный поиск вектора и т.д., где требуется последовательный интуитивный общий порядок <).

Обратите внимание, что в очень старых С++-стандартах поведение было undefined - как 2005 проект WG14/N1124 и ссылки edrewdski под Джеймсом Макнеллисом answer -

Ответ 4

Я знаю, что несколько ответов здесь говорят, что вы не можете сравнивать указатели, если они не указывают на одну и ту же структуру, но на то, что красная селедка, и я попытаюсь объяснить, почему. Один из ваших указателей указывает на начало вашего массива, а другой - на конец, поэтому они указывают на одну и ту же структуру. Адвокат языка может сказать, что если ваш третий указатель указывает вне объекта, сравнение undefined, поэтому x >= array.start может быть true для всех x. Но это не проблема, так как в момент сравнения С++ не может знать, не массив ли встроен в еще большую структуру. Кроме того, если ваше адресное пространство является линейным, как и в наши дни, сравнение с указателем будет реализовано как (un) подписанное целочисленное сравнение, так как любая другая реализация будет медленнее. Даже в периоды сегментов и смещений сравнение (дальний) указатель было реализовано путем первой нормализации указателя, а затем сравнения их как целых чисел.

Что все это сводится к тому, что, если ваш компилятор в порядке, сравнение указателей, не беспокоясь о знаках, должно работать, если все, о чем вы заботитесь, это то, что указатель указывает внутри массива, поскольку компилятор должен сделать указатели, подписанные или неподписанные, в зависимости от того, какая из двух границ может иметь объект С++.

Различные платформы ведут себя по-другому в этом вопросе, поэтому С++ должен оставить его на платформе. Существуют даже платформы, в которых оба адреса около 0 и 80..00h не могут быть отображены или уже приняты при запуске процесса. В этом случае это не имеет значения, если вы согласны в этом.

Иногда это может вызвать проблемы совместимости. Например, в указателях Win32 нет знака. Теперь это было так, что для адресного пространства 4 ГБ для приложений было доступно только нижняя половина (точнее 10000h... 7FFFFFFFh из-за раздела назначения NULL-указателя); высокие адреса были доступны только ядру. Это заставило некоторых людей поместить адреса в подписанные переменные, и их программы продолжали работать, так как высокий бит всегда был 0. Но затем появился коммутатор /3GB, который сделал доступным для приложений почти 3 ГБ (точнее 10000h... BFFFFFFFh) и приложение будет рушиться или вести себя беспорядочно.

Вы явно заявляете, что ваша программа будет только для Windows, которая использует неподписанные указатели. Однако, возможно, вы передумаете в будущем, и использование intptr_t или uintptr_t плохо для переносимости. Я также задаюсь вопросом, нужно ли вообще делать это... если вы индексируете в массив, возможно, более безопасно сравнивать индексы. Предположим, например, что у вас есть массив 1 ГБ при 1500000h... 41500000h, состоящий из 16 384 элементов по 64 kB каждый. Предположим, вы случайно просмотрели индекс 80 000 – явно вне пределов досягаемости. Вычисление указателя даст 39D00000h, поэтому ваша проверка указателя позволит это, хотя это не должно быть.

Ответ 5

Чтобы дополнить другие ответы, сравнение между указателями, которые указывают на разные объекты, зависит от стандарта.

В C99 (ISO/IEC 9899: 1999 (E)), §6.5.8:

5 [...] Во всех остальных случаях поведение не определено.

В С++ 03 (ISO/IEC 14882: 2003 (E)), §5.9:

-Other сравнения указателей не определены.