Сравнение указателей в C. Они подписаны или не подписаны? - PullRequest
15 голосов
/ 15 июля 2011

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

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

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

Тогда a больше. Это гарантировано стандартом?


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

Я ценю ответы до сих пор. Исходя из моих правок, вы сказали бы, что я делаю неопределенное поведение в 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);  


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

Ответы [ 5 ]

24 голосов
/ 15 июля 2011

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

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

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

8 голосов
/ 15 июля 2011

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

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

4 голосов
/ 15 июля 2011

Из черновика стандарта C ++ 5.9:

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

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

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

Обратите внимание, что в оченьВ старых стандартах C ++ поведение было неопределенным - как в черновиках 2005 WG14 / N1124 , на которые ответил Джеймс МакНеллис *1031* -

1 голос
/ 27 февраля 2019

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

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

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

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

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

0 голосов
/ 17 ноября 2015

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

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

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

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

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

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