C++: функция find() (почему 4294967295?)

Модератор: Модераторы разделов

Аватара пользователя
ArkanJR
Сообщения: 1170
Статус: Профан

C++: функция find()

Сообщение ArkanJR »

В "Самоучителе C++" Крупника сказано:
Если же фрагмент обнаружить не удастся, функция выдаст -1 -- отрицательное число, которое, естественно, не может быть позицией символа в строке.


В качестве примера работы данной функции приводится программа:

Код: Выделить всё

#include <iostream>
#include <string>
using namespace std;
int main ()
{
int p;
string a = "сеносеносеноиголкасеносеносеносено";
p = a.find("иголка");
cout << p << endl;
}

В результате на экран выводится число 12.

Ради интереса воткнул в программу строку: cout << a.find("иголка") << endl;

Код: Выделить всё

#include <iostream>
#include <string>
using namespace std;
int main ()
{
int p;
string a = "сеносеносеноиголкасеносеносеносено";
p = a.find("иголка");
cout << p << endl;
cout << a.find("иголка") << endl;
}

Результат работы:

12
12

Потом решил посмотреть на выдачу -1, если функции find() подсунуть символ, отсутствующий в строке:

Код: Выделить всё

#include <iostream>
#include <string>
using namespace std;
int main ()
{
int q;
string a = "сеносеносеноиголкасеносеносеносено";
q = a.find("Y");
cout << q << endl;
}

И действительно, был выдан результат: -1.

Аналогично воткнул в программу строку: cout << a.find("Y") << endl;

Код: Выделить всё

#include <iostream>
#include <string>
using namespace std;
int main ()
{
int q;
string a = "сеносеносеноиголкасеносеносеносено";
q = a.find("Y");
cout << q << endl;
cout << a.find("Y") << endl;
}

Результат работы:

-1
4294967295

Отсюда вопрос: почему cout << a.find("Y") << endl; выводит на экран не -1, а 4294967295?
10% — это 0,1.
© Bizdelnick
Спасибо сказали:
Аватара пользователя
Bizdelnick
Модератор
Сообщения: 20977
Статус: nulla salus bello
ОС: Debian GNU/Linux

Re: C++: функция find()

Сообщение Bizdelnick »

Код: Выделить всё

#include <iostream>

main() {
    int i = -1;
    std::cout << (unsigned int) i << std::endl;
}
Пишите правильно:
в консоли
вку́пе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик
Спасибо сказали:
yoshakar
Сообщения: 259
ОС: Debian Stretch

Re: C++: функция find()

Сообщение yoshakar »

Книжки Крупника - очень хорошие, и эта тоже. Но он немножко соврал: find выдаёт не -1 в случае неудачи. Она вообще не может вернуть -1, поскольку возвращает беззнаковое целое. В случае неудачи она возврашает string::npos - это именно то число, которое вы видите на экране. Но! Во всех существующих реализациях стандартной библиотеки, в том числе и вашей, string::npos - это самое большое возможное число соответствующего типа (size_t) и как правило вообще самое большое представимое число. Поэтому будучи приведено к знаковому типу (а C++ к сожалению делает это без вопросов, неявно), оно (по правилам двоичной арифметики) превращается в -1 (см. программу постом выше). Конечно, лучше так не делать, а использовать эту самую константу string::npos, но придумать реалистичный вариант, когда приведение к знаковому типу не сработает, я не могу. Но вот код типа if(find(....) < 0) не будет работать так, как ожидает тот, кто его написал - это точно. К счастью все нормальные компиляторы выдают на такой код предупреждение.
Спасибо сказали:
Аватара пользователя
ArkanJR
Сообщения: 1170
Статус: Профан

Re: C++: функция find()

Сообщение ArkanJR »

Всё равно мне неясно, почему результат:

-1
4294967295

а не

-1
-1

или

4294967295
4294967295
?
10% — это 0,1.
© Bizdelnick
Спасибо сказали:
yoshakar
Сообщения: 259
ОС: Debian Stretch

Re: C++: функция find()

Сообщение yoshakar »

Первая вариант выводит значение переменной p. Она типа int, то есть целое со знаком. Она не может быть равна 4294967295, так как максимальное значение, которое можно в неё запихнуть в два раза меньше. Поэтому такой код никак не может вывести 4294967295, он выводит -1.
Второй вариант выводит значение, которое вернула функция find. Оно типа unsigned int, то есть целое без знака. Оно не может быть равно -1, так оно без знака. Поэтому такой код никак не может вывести -1, он выводит 4294967295.
Спасибо сказали:
Аватара пользователя
ArkanJR
Сообщения: 1170
Статус: Профан

Re: C++: функция find()

Сообщение ArkanJR »

yoshakar писал(а):
21.10.2014 21:48
Первая вариант выводит значение переменной p. Она типа int, то есть целое со знаком. Она не может быть равна 4294967295, так как максимальное значение, которое можно в неё запихнуть в два раза меньше. Поэтому такой код никак не может вывести 4294967295, он выводит -1.
Второй вариант выводит значение, которое вернула функция find. Оно типа unsigned int, то есть целое без знака. Оно не может быть равно -1, так оно без знака. Поэтому такой код никак не может вывести -1, он выводит 4294967295.

Вот теперь понятно. :)
10% — это 0,1.
© Bizdelnick
Спасибо сказали:
Аватара пользователя
Bizdelnick
Модератор
Сообщения: 20977
Статус: nulla salus bello
ОС: Debian GNU/Linux

Re: C++: функция find()

Сообщение Bizdelnick »

Код: Выделить всё

#include <iostream>

int main() {
    int i = -1;
    unsigned int j = i;
    std::cout << i << std::endl;
    std::cout << j << std::endl;
}

Так понятнее?
Upd. Долго отвечал. (-:
Пишите правильно:
в консоли
вку́пе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: C++: функция find()

Сообщение drBatty »

yoshakar писал(а):
21.10.2014 21:24
это самое большое возможное число соответствующего типа (size_t) и как правило вообще самое большое представимое число.

увы. Сейчас нативное "самое большое" 2^64-1.
yoshakar писал(а):
21.10.2014 21:24
Конечно, лучше так не делать, а использовать эту самую константу string::npos, но придумать реалистичный вариант, когда приведение к знаковому типу не сработает, я не могу.

например если мы приводим к int64_t, и это реальный случай, т.к. именно этот тип имеет ptrdiff_t, например индексы C массивов.
т.е. если мы хотим перейти в массиве, и ожидаем, что a[-1] это прошлый эл-т, то он окажется ВНЕЗАПНО 4294967295м.

PS: я согласен с основной вашей мыслью, что надо сравнивать с константой string::npos
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:
yoshakar
Сообщения: 259
ОС: Debian Stretch

Re: C++: функция find()

Сообщение yoshakar »

drBatty писал(а):
25.10.2014 13:54
PS: я согласен с основной вашей мыслью, что надо сравнивать с константой string::npos
А я уже сомневаюсь. Похоже этот трюк стар как Си (точнее как ssize_t). И преобразование signed <-> unsigned поэтому имплиситное. Идея типа ssize_t как раз в том, что мы возвращаем либо size_t либо -1 (то есть SIZE_T_MAX), а вызывающий код после проверки на равенство минус единице дальше кастит (как правило имплиситно) эту величину обратно к size_t. И обсуждаемый метод find похоже сделан под такой же workflow, разница только в том, что возвращаемый тип оставили unsigned, чтобы неявный кастинг был при сравнении с -1, а не при дальнейшем использовании. Но ведь и в C в качестве возвращаемого типа указывают ssize_t только для того, чтобы по определению функции сразу было понятно, что она возвращает SIZE_T_MAX в качестве ошибки. Единственное чего я не могу понять - как это работает, если отрицательные числа представлены не в дополнительном коде.В случае экзотичских архитектур с обратным, а не дополнительным кодом всё почти так же, только в качестве ошибки используется (SIZE_T_MAX - 1). Для архитектур с прямым кодом компиляторов C/C++ похоже вообще не существовало. Так что сравнение с -1 вместо string:npos - это по сути освящённая временем традиция.
Спасибо сказали: